@mikro-orm/core 7.1.0-dev.8 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +70 -5
- package/EntityManager.js +175 -23
- package/README.md +2 -1
- package/connections/Connection.d.ts +67 -1
- package/connections/Connection.js +64 -0
- package/drivers/IDatabaseDriver.d.ts +30 -6
- package/entity/BaseEntity.d.ts +68 -1
- package/entity/BaseEntity.js +18 -0
- package/entity/Collection.d.ts +4 -1
- package/entity/Collection.js +15 -4
- package/entity/EntityAssigner.js +8 -0
- package/entity/EntityFactory.js +5 -3
- package/entity/EntityLoader.d.ts +8 -1
- package/entity/EntityLoader.js +97 -29
- package/entity/EntityRepository.d.ts +12 -2
- package/entity/EntityRepository.js +12 -0
- package/entity/Reference.d.ts +42 -1
- package/entity/Reference.js +9 -0
- package/entity/defineEntity.d.ts +69 -16
- package/entity/defineEntity.js +17 -0
- package/entity/utils.js +4 -5
- package/enums.d.ts +8 -1
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.js +18 -0
- package/metadata/MetadataDiscovery.d.ts +1 -0
- package/metadata/MetadataDiscovery.js +79 -15
- package/metadata/MetadataValidator.d.ts +26 -0
- package/metadata/MetadataValidator.js +228 -1
- package/metadata/Routine.d.ts +74 -0
- package/metadata/Routine.js +140 -0
- package/metadata/index.d.ts +1 -0
- package/metadata/index.js +1 -0
- package/metadata/types.d.ts +75 -8
- package/package.json +1 -1
- package/platforms/Platform.d.ts +13 -3
- package/platforms/Platform.js +51 -6
- package/serialization/EntitySerializer.js +4 -3
- package/typings.d.ts +323 -21
- package/typings.js +32 -12
- package/unit-of-work/UnitOfWork.js +43 -5
- package/utils/AbstractMigrator.d.ts +9 -4
- package/utils/AbstractMigrator.js +60 -28
- package/utils/AbstractSchemaGenerator.d.ts +1 -1
- package/utils/AbstractSchemaGenerator.js +4 -1
- package/utils/Configuration.d.ts +30 -0
- package/utils/Configuration.js +45 -0
- package/utils/DataloaderUtils.d.ts +10 -1
- package/utils/DataloaderUtils.js +78 -0
- package/utils/EntityComparator.js +1 -1
- package/utils/TransactionManager.js +2 -0
- package/utils/Utils.js +1 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/partition-utils.d.ts +17 -0
- package/utils/partition-utils.js +79 -0
- package/utils/upsert-utils.d.ts +2 -0
- package/utils/upsert-utils.js +26 -1
package/entity/EntityLoader.js
CHANGED
|
@@ -177,8 +177,9 @@ export class EntityLoader {
|
|
|
177
177
|
Utils.isObject(orderBy[prop.name]))
|
|
178
178
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
179
179
|
const where = await this.extractChildCondition(options, prop);
|
|
180
|
+
const mergedOrderBy = populate.orderBy ? Utils.asArray(populate.orderBy) : innerOrderBy;
|
|
180
181
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.#driver.getPlatform().usesPivotTable()) {
|
|
181
|
-
const pivotOrderBy = QueryHelper.mergeOrderBy(
|
|
182
|
+
const pivotOrderBy = QueryHelper.mergeOrderBy(mergedOrderBy, prop.orderBy, prop.targetMeta?.orderBy);
|
|
182
183
|
const res = await this.findChildrenFromPivotTable(filtered, prop, options, pivotOrderBy, populate, !!ref);
|
|
183
184
|
return Utils.flatten(res);
|
|
184
185
|
}
|
|
@@ -188,10 +189,12 @@ export class EntityLoader {
|
|
|
188
189
|
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
189
190
|
...options,
|
|
190
191
|
where,
|
|
191
|
-
orderBy:
|
|
192
|
+
orderBy: mergedOrderBy,
|
|
192
193
|
}, !!(ref || prop.mapToPk));
|
|
193
|
-
const
|
|
194
|
-
|
|
194
|
+
const hasLimit = populate.limit != null;
|
|
195
|
+
const isPartial = partial || hasLimit;
|
|
196
|
+
const customOrder = mergedOrderBy.length > 0 || !!prop.orderBy || !!prop.targetMeta?.orderBy;
|
|
197
|
+
this.initializeCollections(filtered, prop, field, items, customOrder, isPartial, hasLimit);
|
|
195
198
|
return items;
|
|
196
199
|
}
|
|
197
200
|
async populateScalar(meta, filtered, options) {
|
|
@@ -269,15 +272,15 @@ export class EntityLoader {
|
|
|
269
272
|
}));
|
|
270
273
|
return allItems;
|
|
271
274
|
}
|
|
272
|
-
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
275
|
+
initializeCollections(filtered, prop, field, children, customOrder, partial, readonly) {
|
|
273
276
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
274
|
-
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
277
|
+
this.initializeOneToMany(filtered, children, prop, field, partial, readonly);
|
|
275
278
|
}
|
|
276
279
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this.#driver.getPlatform().usesPivotTable()) {
|
|
277
|
-
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
280
|
+
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial, readonly);
|
|
278
281
|
}
|
|
279
282
|
}
|
|
280
|
-
initializeOneToMany(filtered, children, prop, field, partial) {
|
|
283
|
+
initializeOneToMany(filtered, children, prop, field, partial, readonly) {
|
|
281
284
|
const mapToPk = prop.targetMeta.properties[prop.mappedBy].mapToPk;
|
|
282
285
|
const map = {};
|
|
283
286
|
for (const entity of filtered) {
|
|
@@ -293,14 +296,14 @@ export class EntityLoader {
|
|
|
293
296
|
}
|
|
294
297
|
for (const entity of filtered) {
|
|
295
298
|
const key = helper(entity).getSerializedPrimaryKey();
|
|
296
|
-
entity[field].hydrate(map[key], undefined, partial);
|
|
299
|
+
entity[field].hydrate(map[key], undefined, partial, readonly);
|
|
297
300
|
}
|
|
298
301
|
}
|
|
299
|
-
initializeManyToMany(filtered, children, prop, field, customOrder, partial) {
|
|
302
|
+
initializeManyToMany(filtered, children, prop, field, customOrder, partial, readonly) {
|
|
300
303
|
if (prop.mappedBy) {
|
|
301
304
|
for (const entity of filtered) {
|
|
302
305
|
const items = children.filter(child => child[prop.mappedBy].contains(entity, false));
|
|
303
|
-
entity[field].hydrate(items, true, partial);
|
|
306
|
+
entity[field].hydrate(items, true, partial, readonly);
|
|
304
307
|
}
|
|
305
308
|
}
|
|
306
309
|
else {
|
|
@@ -311,12 +314,12 @@ export class EntityLoader {
|
|
|
311
314
|
if (!customOrder) {
|
|
312
315
|
items.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
313
316
|
}
|
|
314
|
-
entity[field].hydrate(items, true, partial);
|
|
317
|
+
entity[field].hydrate(items, true, partial, readonly);
|
|
315
318
|
}
|
|
316
319
|
}
|
|
317
320
|
}
|
|
318
321
|
async findChildren(entities, prop, populate, options, ref) {
|
|
319
|
-
const children = Utils.unique(this.getChildReferences(entities, prop, options, ref));
|
|
322
|
+
const children = Utils.unique(this.getChildReferences(entities, prop, options, ref, populate));
|
|
320
323
|
const meta = prop.targetMeta;
|
|
321
324
|
// When targetKey is set, use it for FK lookup instead of the PK
|
|
322
325
|
let fk = prop.targetKey ?? Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
@@ -334,10 +337,10 @@ export class EntityLoader {
|
|
|
334
337
|
fk = ownerProp.name;
|
|
335
338
|
}
|
|
336
339
|
}
|
|
337
|
-
if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !ref) {
|
|
340
|
+
if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && (!ref || (!prop.mapToPk && !populate.filter))) {
|
|
338
341
|
children.length = 0;
|
|
339
342
|
fk = meta.properties[prop.mappedBy].name;
|
|
340
|
-
children.push(...this.filterByReferences(entities, prop
|
|
343
|
+
children.push(...this.filterByReferences(entities, prop, options, ref));
|
|
341
344
|
}
|
|
342
345
|
if (children.length === 0) {
|
|
343
346
|
return { items: [], partial };
|
|
@@ -366,8 +369,11 @@ export class EntityLoader {
|
|
|
366
369
|
}
|
|
367
370
|
const fields = this.buildFields(options.fields, prop, ref);
|
|
368
371
|
/* eslint-disable prefer-const */
|
|
369
|
-
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere
|
|
372
|
+
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging } = options;
|
|
370
373
|
/* eslint-enable prefer-const */
|
|
374
|
+
// `populateWhere` is typed as required via `Required<EntityLoaderOptions>`, but `em.populate(...)`
|
|
375
|
+
// forwards user-supplied options without filling it in — keep the runtime default.
|
|
376
|
+
populateWhere ??= 'infer';
|
|
371
377
|
if (typeof populateWhere === 'object') {
|
|
372
378
|
populateWhere = await this.extractChildCondition({ where: populateWhere }, prop);
|
|
373
379
|
}
|
|
@@ -375,7 +381,7 @@ export class EntityLoader {
|
|
|
375
381
|
where = { $and: [where, prop.where] };
|
|
376
382
|
}
|
|
377
383
|
const orderBy = QueryHelper.mergeOrderBy(options.orderBy, prop.orderBy);
|
|
378
|
-
const
|
|
384
|
+
const findOptions = {
|
|
379
385
|
filters,
|
|
380
386
|
convertCustomTypes,
|
|
381
387
|
lockMode,
|
|
@@ -390,11 +396,21 @@ export class EntityLoader {
|
|
|
390
396
|
fields,
|
|
391
397
|
schema,
|
|
392
398
|
connectionType,
|
|
399
|
+
signal: options.signal,
|
|
400
|
+
inflightQueryAbortStrategy: options.inflightQueryAbortStrategy,
|
|
393
401
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
394
402
|
refresh: refresh && !children.every(item => options.visited.has(item)),
|
|
395
403
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
396
404
|
visited: options.visited,
|
|
397
|
-
}
|
|
405
|
+
};
|
|
406
|
+
if (populate.limit != null) {
|
|
407
|
+
findOptions._partitionLimit = {
|
|
408
|
+
partitionBy: fk,
|
|
409
|
+
limit: populate.limit,
|
|
410
|
+
offset: populate.offset,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const items = await this.#em.find(meta.class, where, findOptions);
|
|
398
414
|
// For targetKey relations, wire up loaded entities to parent references
|
|
399
415
|
// This is needed because the references were created under alternate key,
|
|
400
416
|
// but loaded entities are stored under PK, so they don't automatically merge
|
|
@@ -582,6 +598,12 @@ export class EntityLoader {
|
|
|
582
598
|
const options2 = { ...options, fields, exclude, populateFilter };
|
|
583
599
|
['limit', 'offset', 'first', 'last', 'before', 'after', 'overfetch'].forEach(prop => delete options2[prop]);
|
|
584
600
|
options2.populate = populate?.children ?? [];
|
|
601
|
+
if (populate?.limit != null) {
|
|
602
|
+
options2._partitionLimit = {
|
|
603
|
+
limit: populate.limit,
|
|
604
|
+
offset: populate.offset,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
585
607
|
if (!Utils.isEmpty(prop.where)) {
|
|
586
608
|
where = { $and: [where, prop.where] };
|
|
587
609
|
}
|
|
@@ -609,7 +631,8 @@ export class EntityLoader {
|
|
|
609
631
|
});
|
|
610
632
|
return this.#em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
|
|
611
633
|
});
|
|
612
|
-
|
|
634
|
+
const hasLimit = populate?.limit != null;
|
|
635
|
+
entity[prop.name].hydrate(items, true, hasLimit, hasLimit);
|
|
613
636
|
children.push(items);
|
|
614
637
|
}
|
|
615
638
|
return children;
|
|
@@ -670,8 +693,11 @@ export class EntityLoader {
|
|
|
670
693
|
}
|
|
671
694
|
return ret;
|
|
672
695
|
}, []);
|
|
673
|
-
// we need to automatically select the FKs too, e.g. for 1:m
|
|
674
|
-
|
|
696
|
+
// we need to automatically select the FKs too, e.g. for 1:m and inverse 1:1 relations
|
|
697
|
+
// to be able to wire them with the items
|
|
698
|
+
if (prop.kind === ReferenceKind.ONE_TO_MANY ||
|
|
699
|
+
prop.kind === ReferenceKind.MANY_TO_MANY ||
|
|
700
|
+
(prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner)) {
|
|
675
701
|
const owner = prop.targetMeta.properties[prop.mappedBy];
|
|
676
702
|
// when the owning FK is lazy, we need to explicitly select it even without user-provided fields,
|
|
677
703
|
// otherwise the driver will exclude it and we won't be able to map children to their parent collections
|
|
@@ -684,7 +710,7 @@ export class EntityLoader {
|
|
|
684
710
|
}
|
|
685
711
|
return ret;
|
|
686
712
|
}
|
|
687
|
-
getChildReferences(entities, prop, options, ref) {
|
|
713
|
+
getChildReferences(entities, prop, options, ref, populate) {
|
|
688
714
|
const filtered = this.filterCollections(entities, prop.name, options, ref);
|
|
689
715
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
690
716
|
return filtered.map(e => e[prop.name].owner);
|
|
@@ -700,7 +726,7 @@ export class EntityLoader {
|
|
|
700
726
|
return filtered;
|
|
701
727
|
}
|
|
702
728
|
// MANY_TO_ONE or ONE_TO_ONE
|
|
703
|
-
return this.filterReferences(entities, prop.name, options, ref);
|
|
729
|
+
return this.filterReferences(entities, prop.name, options, ref, populate);
|
|
704
730
|
}
|
|
705
731
|
filterCollections(entities, field, options, ref) {
|
|
706
732
|
if (options.refresh) {
|
|
@@ -714,7 +740,16 @@ export class EntityLoader {
|
|
|
714
740
|
}
|
|
715
741
|
const wrapped = helper(entity);
|
|
716
742
|
if (!field.includes('.')) {
|
|
717
|
-
|
|
743
|
+
if (wrapped.__loadedProperties.has(field)) {
|
|
744
|
+
return true;
|
|
745
|
+
}
|
|
746
|
+
// Inlined embeddables track each leaf child separately, not the parent property,
|
|
747
|
+
// so we need to recurse for nested inline embeddables.
|
|
748
|
+
const prop = wrapped.__meta.properties[field];
|
|
749
|
+
if (prop?.kind === ReferenceKind.EMBEDDED && !prop.object) {
|
|
750
|
+
return Object.values(prop.embeddedProps).every(child => this.isPropertyLoaded(entity, child.name));
|
|
751
|
+
}
|
|
752
|
+
return false;
|
|
718
753
|
}
|
|
719
754
|
const [f, ...r] = field.split('.');
|
|
720
755
|
/* v8 ignore next */
|
|
@@ -726,7 +761,7 @@ export class EntityLoader {
|
|
|
726
761
|
}
|
|
727
762
|
return this.isPropertyLoaded(entity[f], r.join('.'));
|
|
728
763
|
}
|
|
729
|
-
filterReferences(entities, field, options, ref) {
|
|
764
|
+
filterReferences(entities, field, options, ref, populate) {
|
|
730
765
|
if (ref) {
|
|
731
766
|
return [];
|
|
732
767
|
}
|
|
@@ -735,13 +770,19 @@ export class EntityLoader {
|
|
|
735
770
|
return children.map(e => Reference.unwrapReference(e[field]));
|
|
736
771
|
}
|
|
737
772
|
if (options.fields) {
|
|
773
|
+
// `:ref` populate children are satisfied by FK/PK and will be loaded lazily — they don't force a parent reload
|
|
774
|
+
const refChildren = new Set((populate?.children ?? [])
|
|
775
|
+
.map(c => c.field.split(':'))
|
|
776
|
+
.filter(parts => parts[1] === 'ref')
|
|
777
|
+
.map(parts => parts[0]));
|
|
738
778
|
return children
|
|
739
779
|
.map(e => Reference.unwrapReference(e[field]))
|
|
740
780
|
.filter(target => {
|
|
741
781
|
const wrapped = helper(target);
|
|
742
782
|
const childFields = options.fields
|
|
743
783
|
.filter(f => f.startsWith(`${field}.`))
|
|
744
|
-
.map(f => f.substring(field.length + 1))
|
|
784
|
+
.map(f => f.substring(field.length + 1))
|
|
785
|
+
.filter(cf => !refChildren.has(cf.split('.')[0]));
|
|
745
786
|
return !wrapped.__initialized || !childFields.every(cf => this.isPropertyLoaded(target, cf));
|
|
746
787
|
});
|
|
747
788
|
}
|
|
@@ -749,12 +790,39 @@ export class EntityLoader {
|
|
|
749
790
|
.filter(e => !e[field].__helper.__initialized)
|
|
750
791
|
.map(e => Reference.unwrapReference(e[field]));
|
|
751
792
|
}
|
|
752
|
-
filterByReferences(entities,
|
|
793
|
+
filterByReferences(entities, prop, options, ref) {
|
|
753
794
|
/* v8 ignore next */
|
|
754
|
-
if (refresh) {
|
|
795
|
+
if (options.refresh) {
|
|
755
796
|
return entities;
|
|
756
797
|
}
|
|
757
|
-
|
|
798
|
+
const field = prop.name;
|
|
799
|
+
return entities.filter(e => {
|
|
800
|
+
const value = e[field];
|
|
801
|
+
if (value === null) {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
if (value === undefined || !Utils.isEntity(value, true)) {
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
const target = Reference.unwrapReference(value);
|
|
808
|
+
const wrapped = helper(target);
|
|
809
|
+
if (!wrapped.__initialized) {
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
if (ref) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
if (options.fields) {
|
|
816
|
+
const childFields = options.fields
|
|
817
|
+
.filter(f => f.startsWith(`${prop.name}.`))
|
|
818
|
+
.map(f => f.substring(prop.name.length + 1));
|
|
819
|
+
return childFields.length > 0 && !childFields.every(f => this.isPropertyLoaded(target, f));
|
|
820
|
+
}
|
|
821
|
+
return prop.targetMeta.comparableProps.some(targetProp => {
|
|
822
|
+
const inlineEmbedded = targetProp.kind === ReferenceKind.EMBEDDED && !targetProp.object;
|
|
823
|
+
return !inlineEmbedded && !targetProp.lazy && !wrapped.__loadedProperties.has(targetProp.name);
|
|
824
|
+
});
|
|
825
|
+
});
|
|
758
826
|
}
|
|
759
827
|
lookupAllRelationships(entityName) {
|
|
760
828
|
const ret = [];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PopulatePath } from '../enums.js';
|
|
2
2
|
import type { CreateOptions, EntityManager, MergeOptions } from '../EntityManager.js';
|
|
3
3
|
import type { AssignOptions } from './EntityAssigner.js';
|
|
4
|
-
import type { EntityData,
|
|
5
|
-
import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
|
+
import type { Dictionary, EntityData, EntityDictionary, EntityKey, EntityName, FilterQuery, Loaded, Primary, AutoPath, RequiredEntityData, Ref, EntityType, EntityDTO, MergeSelected, FromEntityType, IsSubset, MergeLoaded, ArrayElement, IndexFilterQuery, WithUsingOptions } from '../typings.js';
|
|
5
|
+
import type { CountByOptions, CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
6
6
|
import type { EntityLoaderOptions } from './EntityLoader.js';
|
|
7
7
|
import type { Cursor } from '../utils/Cursor.js';
|
|
8
8
|
/** Repository class providing a type-safe API for querying and persisting a specific entity type. */
|
|
@@ -207,6 +207,16 @@ export declare class EntityRepository<Entity extends object> {
|
|
|
207
207
|
* Returns total number of entities matching your `where` query.
|
|
208
208
|
*/
|
|
209
209
|
count<Hint extends string = never>(where?: FilterQuery<Entity>, options?: CountOptions<Entity, Hint>): Promise<number>;
|
|
210
|
+
/**
|
|
211
|
+
* Counts entities grouped by one or more properties.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* const counts = await repo.countBy('status');
|
|
216
|
+
* // { 'active': 5, 'inactive': 2 }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
countBy(groupBy: EntityKey<Entity> | readonly EntityKey<Entity>[], options?: CountByOptions<Entity>): Promise<Dictionary<number>>;
|
|
210
220
|
/** Returns the entity class name associated with this repository. */
|
|
211
221
|
getEntityName(): string;
|
|
212
222
|
/**
|
|
@@ -194,6 +194,18 @@ export class EntityRepository {
|
|
|
194
194
|
async count(where = {}, options = {}) {
|
|
195
195
|
return this.getEntityManager().count(this.entityName, where, options);
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Counts entities grouped by one or more properties.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* const counts = await repo.countBy('status');
|
|
203
|
+
* // { 'active': 5, 'inactive': 2 }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
async countBy(groupBy, options) {
|
|
207
|
+
return this.getEntityManager().countBy(this.entityName, groupBy, options);
|
|
208
|
+
}
|
|
197
209
|
/** Returns the entity class name associated with this repository. */
|
|
198
210
|
getEntityName() {
|
|
199
211
|
return Utils.className(this.entityName);
|
package/entity/Reference.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
1
|
+
import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, LazyRef, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
2
2
|
import type { FindOneOptions, FindOneOrFailOptions } from '../drivers/IDatabaseDriver.js';
|
|
3
3
|
/** Wrapper around an entity that provides lazy loading capabilities and identity-preserving reference semantics. */
|
|
4
4
|
export declare class Reference<T extends object> {
|
|
@@ -98,6 +98,47 @@ export declare function ref<I extends unknown | Ref<unknown> | undefined | null,
|
|
|
98
98
|
* shortcut for `Reference.createFromPK(entityType, pk)`
|
|
99
99
|
*/
|
|
100
100
|
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>;
|
|
101
|
+
/**
|
|
102
|
+
* Unwraps a reference to its underlying value, returning the entity or scalar. Works on any of:
|
|
103
|
+
*
|
|
104
|
+
* - `Ref<T>` / `Reference<T>` (entity) — calls `.unwrap()` to extract the wrapped entity, returns `T`.
|
|
105
|
+
* - `LazyRef<T>` — type-only marker; runtime value is already `T`, so returned as-is.
|
|
106
|
+
* - plain entity `T` — returned as-is.
|
|
107
|
+
* - `ScalarReference<V>` / `ScalarRef<V>` — calls `.unwrap()` to extract the scalar value, returns `V | undefined`
|
|
108
|
+
* (a scalar reference may be bound without an initial value).
|
|
109
|
+
*
|
|
110
|
+
* Inverse of the {@link ref} helper. Use as a typed escape hatch when you know a relation is populated
|
|
111
|
+
* but don't want to (or can't) thread `Loaded<T, 'relation'>` through a function signature.
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* function logAuthor(book: Book) {
|
|
115
|
+
* // `book.author` is typed as `LazyRef<Author>`, so `.name` is not directly accessible
|
|
116
|
+
* console.log(unref(book.author).name);
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* // Scalar reference:
|
|
120
|
+
* const scalar = ref('hello');
|
|
121
|
+
* unref(scalar); // 'hello' (type: string | undefined)
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* Note: `unref` is a compile-time narrowing only for entity refs. On a `LazyRef<T>` or plain entity
|
|
125
|
+
* relation that is a stub (PK only, never populated), reads of non-PK properties will still return
|
|
126
|
+
* `undefined` at runtime — same footgun as a plain non-`Ref` relation.
|
|
127
|
+
*/
|
|
128
|
+
export declare function unref<V>(value: ScalarReference<V>): V | undefined;
|
|
129
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T): T;
|
|
130
|
+
/**
|
|
131
|
+
* Unwraps a reference to its underlying value. Nullable variant — returns `null` unchanged.
|
|
132
|
+
*/
|
|
133
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | null): T | null;
|
|
134
|
+
/**
|
|
135
|
+
* Unwraps a reference to its underlying value. Optional variant — returns `undefined` unchanged.
|
|
136
|
+
*/
|
|
137
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | undefined): T | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Unwraps a reference to its underlying value. Nullable variant — returns `null`/`undefined` unchanged.
|
|
140
|
+
*/
|
|
141
|
+
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | null | undefined): T | null | undefined;
|
|
101
142
|
/**
|
|
102
143
|
* shortcut for `Reference.createNakedFromPK(entityType, pk)`
|
|
103
144
|
*/
|
package/entity/Reference.js
CHANGED
|
@@ -303,6 +303,15 @@ export function ref(entityOrType, pk) {
|
|
|
303
303
|
}
|
|
304
304
|
return Reference.createFromPK(entityOrType, pk);
|
|
305
305
|
}
|
|
306
|
+
export function unref(value) {
|
|
307
|
+
if (value == null) {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
if (ScalarReference.isScalarReference(value)) {
|
|
311
|
+
return value.unwrap();
|
|
312
|
+
}
|
|
313
|
+
return Reference.unwrapReference(value);
|
|
314
|
+
}
|
|
306
315
|
/**
|
|
307
316
|
* shortcut for `Reference.createNakedFromPK(entityType, pk)`
|
|
308
317
|
*/
|
package/entity/defineEntity.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EntityManager } from '../EntityManager.js';
|
|
2
2
|
import type { ColumnType, PropertyOptions, ReferenceOptions, EnumOptions, EmbeddedOptions, ManyToOneOptions, OneToManyOptions, OneToOneOptions, ManyToManyOptions, IndexColumnOptions } from '../metadata/types.js';
|
|
3
|
-
import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, EntityRepositoryType, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType, Ref, IndexCallback, TriggerCallback, FormulaCallback, EntityCtor, IsNever, IWrappedEntity, DefineConfig, Config, MaybePromise, IndexHints } from '../typings.js';
|
|
3
|
+
import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, EntityRepositoryType, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType, Ref, LazyRef, IndexCallback, TriggerCallback, FormulaCallback, EntityCtor, IsNever, IWrappedEntity, DefineConfig, Config, MaybePromise, IndexHints } from '../typings.js';
|
|
4
4
|
import type { Raw } from '../utils/RawQueryFragment.js';
|
|
5
5
|
import type { ScalarReference } from './Reference.js';
|
|
6
6
|
import type { SerializeOptions } from '../serialization/EntitySerializer.js';
|
|
@@ -12,7 +12,7 @@ import type { Collection } from './Collection.js';
|
|
|
12
12
|
import type { FilterOptions, FindOptions, FindOneOptions } from '../drivers/IDatabaseDriver.js';
|
|
13
13
|
/** Union of all option keys supported across all property definition types (scalar, enum, embedded, relations). */
|
|
14
14
|
export type UniversalPropertyKeys = keyof PropertyOptions<any> | keyof EnumOptions<any> | keyof EmbeddedOptions<any, any> | keyof ReferenceOptions<any, any> | keyof ManyToOneOptions<any, any> | keyof OneToManyOptions<any, any> | keyof OneToOneOptions<any, any> | keyof ManyToManyOptions<any, any>;
|
|
15
|
-
type BuilderExtraKeys = '~options' | '~type' | '$type' | 'strictNullable';
|
|
15
|
+
type BuilderExtraKeys = '~options' | '~type' | '$type' | 'strictNullable' | 'lazyRef';
|
|
16
16
|
type ExcludeKeys = 'entity' | 'items';
|
|
17
17
|
type BuilderKeys = Exclude<UniversalPropertyKeys, ExcludeKeys> | BuilderExtraKeys;
|
|
18
18
|
type IncludeKeysForProperty = Exclude<keyof PropertyOptions<any>, ExcludeKeys> | BuilderExtraKeys;
|
|
@@ -36,9 +36,23 @@ export interface PropertyChain<Value, Options> {
|
|
|
36
36
|
nullable: true;
|
|
37
37
|
strictNullable: true;
|
|
38
38
|
}>;
|
|
39
|
-
ref():
|
|
39
|
+
ref(): Options extends {
|
|
40
|
+
lazyRef: true;
|
|
41
|
+
} ? never : PropertyChain<Value, Omit<Options, 'ref'> & {
|
|
40
42
|
ref: true;
|
|
41
43
|
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Mark a to-one relation (`m:1`/`1:1`) as a {@apilink LazyRef} — direct entity at runtime, type-restricted
|
|
46
|
+
* until `Loaded<>` narrows it. Type-level only, no `ref: true` wrapping. Not compatible with `.ref()` or
|
|
47
|
+
* `.mapToPk()` — those chains are rejected in both directions at compile time.
|
|
48
|
+
*/
|
|
49
|
+
lazyRef(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
50
|
+
ref: true;
|
|
51
|
+
} | {
|
|
52
|
+
mapToPk: true;
|
|
53
|
+
} ? never : PropertyChain<Value, Omit<Options, 'lazyRef'> & {
|
|
54
|
+
lazyRef: true;
|
|
55
|
+
}> : never;
|
|
42
56
|
primary(): PropertyChain<Value, Omit<Options, 'primary'> & {
|
|
43
57
|
primary: true;
|
|
44
58
|
}>;
|
|
@@ -103,7 +117,7 @@ export interface PropertyChain<Value, Options> {
|
|
|
103
117
|
groups(...groups: string[]): PropertyChain<Value, Options>;
|
|
104
118
|
customOrder(...customOrder: string[] | number[] | boolean[]): PropertyChain<Value, Options>;
|
|
105
119
|
extra(extra: string): PropertyChain<Value, Options>;
|
|
106
|
-
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default')[]): PropertyChain<Value, Options>;
|
|
120
|
+
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default' | 'collation')[]): PropertyChain<Value, Options>;
|
|
107
121
|
/** Explicitly specify index on a property. When a string name is passed, it enables type-safe `using` in `FindOptions`. */
|
|
108
122
|
index<N extends string>(name: N): PropertyChain<Value, Omit<Options, 'index'> & {
|
|
109
123
|
index: N;
|
|
@@ -115,6 +129,7 @@ export interface PropertyChain<Value, Options> {
|
|
|
115
129
|
}>;
|
|
116
130
|
unique(unique?: boolean): PropertyChain<Value, Options>;
|
|
117
131
|
comment(comment: string): PropertyChain<Value, Options>;
|
|
132
|
+
collation(collation: string): PropertyChain<Value, Options>;
|
|
118
133
|
accessor(accessor?: string | boolean): PropertyChain<Value, Options>;
|
|
119
134
|
eager(eager?: boolean): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
120
135
|
cascade(...cascade: Cascade[]): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -125,7 +140,9 @@ export interface PropertyChain<Value, Options> {
|
|
|
125
140
|
owner(): HasKind<Options, '1:1' | 'm:n'> extends true ? PropertyChain<Value, Omit<Options, 'owner'> & {
|
|
126
141
|
owner: true;
|
|
127
142
|
}> : never;
|
|
128
|
-
mapToPk(): HasKind<Options, 'm:1' | '1:1'> extends true ?
|
|
143
|
+
mapToPk(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
144
|
+
lazyRef: true;
|
|
145
|
+
} ? never : PropertyChain<Value, Omit<Options, 'mapToPk'> & {
|
|
129
146
|
mapToPk: true;
|
|
130
147
|
}> : never;
|
|
131
148
|
orphanRemoval(orphanRemoval?: boolean): HasKind<Options, '1:m' | '1:1'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -325,9 +342,24 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
325
342
|
/**
|
|
326
343
|
* Enable `ScalarReference` wrapper for lazy values. Use this in combination with `lazy: true` to have a type-safe accessor object in place of the value.
|
|
327
344
|
*/
|
|
328
|
-
ref():
|
|
345
|
+
ref(): Options extends {
|
|
346
|
+
lazyRef: true;
|
|
347
|
+
} ? never : UniversalPropertyOptionsBuilder<Value, Omit<Options, 'ref'> & {
|
|
329
348
|
ref: true;
|
|
330
349
|
}, IncludeKeys>;
|
|
350
|
+
/**
|
|
351
|
+
* Mark a to-one relation (`m:1`/`1:1`) as a {@apilink LazyRef} — the runtime value stays a plain entity
|
|
352
|
+
* (no `Reference` wrapper) but the TypeScript type restricts access to the primary key until `Loaded<>`
|
|
353
|
+
* narrows it. Type-level only; does not set `ref: true`. Not compatible with `.ref()` or `.mapToPk()` —
|
|
354
|
+
* those chains are rejected in both directions at compile time.
|
|
355
|
+
*/
|
|
356
|
+
lazyRef(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
357
|
+
ref: true;
|
|
358
|
+
} | {
|
|
359
|
+
mapToPk: true;
|
|
360
|
+
} ? never : UniversalPropertyOptionsBuilder<Value, Omit<Options, 'lazyRef'> & {
|
|
361
|
+
lazyRef: true;
|
|
362
|
+
}, IncludeKeys> : never;
|
|
331
363
|
/**
|
|
332
364
|
* Set to true to omit the property when {@link https://mikro-orm.io/docs/serializing Serializing}.
|
|
333
365
|
*/
|
|
@@ -447,6 +479,11 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
447
479
|
* Specify comment of column for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
448
480
|
*/
|
|
449
481
|
comment(comment: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
482
|
+
/**
|
|
483
|
+
* Specify column-level collation for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
484
|
+
* Emits a `COLLATE` clause on the column definition; the accepted collation names are dialect-specific.
|
|
485
|
+
*/
|
|
486
|
+
collation(collation: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
450
487
|
/** mysql only */
|
|
451
488
|
extra(extra: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
452
489
|
/**
|
|
@@ -454,7 +491,7 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
454
491
|
*
|
|
455
492
|
* @see https://mikro-orm.io/docs/defining-entities#sql-generated-columns
|
|
456
493
|
*/
|
|
457
|
-
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default')[]): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
494
|
+
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default' | 'collation')[]): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
458
495
|
array(): Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'array'> & {
|
|
459
496
|
array: true;
|
|
460
497
|
}, IncludeKeys>, IncludeKeys>;
|
|
@@ -512,7 +549,9 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
512
549
|
/** What to do when the reference to the target entity gets updated. */
|
|
513
550
|
updateRule(updateRule: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
514
551
|
/** Map this relation to the primary key value instead of an entity. */
|
|
515
|
-
mapToPk():
|
|
552
|
+
mapToPk(): Options extends {
|
|
553
|
+
lazyRef: true;
|
|
554
|
+
} ? never : Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'mapToPk'> & {
|
|
516
555
|
mapToPk: true;
|
|
517
556
|
}, IncludeKeys>, IncludeKeys>;
|
|
518
557
|
/** Set the constraint type. Immediate constraints are checked for each statement, while deferred ones are only checked at the end of the transaction. Only for postgres unique constraints. */
|
|
@@ -556,7 +595,7 @@ export type PropertyBuilders = {
|
|
|
556
595
|
datetime: (length?: number) => UniversalPropertyOptionsBuilder<InferPropertyValueType<typeof types.datetime>, EmptyOptions, IncludeKeysForProperty>;
|
|
557
596
|
time: (length?: number) => UniversalPropertyOptionsBuilder<InferPropertyValueType<typeof types.time>, EmptyOptions, IncludeKeysForProperty>;
|
|
558
597
|
type: <T extends PropertyValueType>(type: T) => UniversalPropertyOptionsBuilder<InferPropertyValueType<T>, EmptyOptions, IncludeKeysForProperty>;
|
|
559
|
-
enum: <const T extends readonly (number | string)[] | (() => Dictionary)>(items?: T) => UniversalPropertyOptionsBuilder<T extends () => Dictionary ? ValueOf<ReturnType<T>> : T extends readonly (infer Value)[] ? Value : T, EmptyOptions, IncludeKeysForEnumOptions>;
|
|
598
|
+
enum: <const T extends readonly (number | string)[] | Dictionary | (() => Dictionary)>(items?: T) => UniversalPropertyOptionsBuilder<T extends () => Dictionary ? ValueOf<ReturnType<T>> : T extends readonly (infer Value)[] ? Value : T extends Dictionary ? ValueOf<T> : T, EmptyOptions, IncludeKeysForEnumOptions>;
|
|
560
599
|
embedded: <Target extends EntityTarget | EntityTarget[]>(target: Target) => PropertyChain<InferEntity<Target extends (infer T)[] ? T : Target>, EmptyOptions & {
|
|
561
600
|
kind: 'embedded';
|
|
562
601
|
}>;
|
|
@@ -576,12 +615,15 @@ export type PropertyBuilders = {
|
|
|
576
615
|
/** Own keys + base entity keys (when TBase is not `never`). Guards against `keyof never = string | number | symbol`. */
|
|
577
616
|
type AllKeys<TProperties, TBase> = keyof TProperties | (IsNever<TBase> extends true ? never : keyof TBase);
|
|
578
617
|
/** Metadata descriptor for `defineEntity()`, combining entity options with property definitions. */
|
|
579
|
-
export interface EntityMetadataWithProperties<TName extends string, TTableName extends string, TProperties extends Record<string, any>, TPK extends (keyof TProperties)[] | undefined = undefined, TBase = never, TRepository = never, TForceObject extends boolean = false> extends Omit<Partial<EntityMetadata<InferEntityFromProperties<TProperties, TPK, TBase, TRepository>>>, 'properties' | 'extends' | 'primaryKeys' | 'hooks' | 'discriminatorColumn' | 'versionProperty' | 'concurrencyCheckKeys' | 'serializedPrimaryKey' | 'indexes' | 'uniques' | 'triggers' | 'repository' | 'filters' | 'orderBy'> {
|
|
618
|
+
export interface EntityMetadataWithProperties<TName extends string, TTableName extends string, TProperties extends Record<string, any>, TPK extends (keyof TProperties)[] | undefined = undefined, TBase = never, TRepository = never, TForceObject extends boolean = false, TDiscriminatorColumn extends string | undefined = undefined, TDiscriminatorValue extends string | number | undefined = undefined, TBaseDiscriminatorColumn extends string | undefined = undefined> extends Omit<Partial<EntityMetadata<InferEntityFromProperties<TProperties, TPK, TBase, TRepository>>>, 'properties' | 'extends' | 'primaryKeys' | 'hooks' | 'discriminator' | 'discriminatorColumn' | 'discriminatorValue' | 'versionProperty' | 'concurrencyCheckKeys' | 'serializedPrimaryKey' | 'indexes' | 'uniques' | 'triggers' | 'repository' | 'filters' | 'orderBy'> {
|
|
580
619
|
name: TName;
|
|
581
620
|
tableName?: TTableName;
|
|
582
621
|
extends?: {
|
|
583
622
|
'~entity': TBase;
|
|
584
|
-
|
|
623
|
+
'~discriminatorColumn'?: TBaseDiscriminatorColumn;
|
|
624
|
+
} | (EntityCtor<TBase> & {
|
|
625
|
+
'~discriminatorColumn'?: TBaseDiscriminatorColumn;
|
|
626
|
+
});
|
|
585
627
|
properties: TProperties | ((properties: PropertyBuilders) => TProperties);
|
|
586
628
|
primaryKeys?: TPK & InferPrimaryKeyConstraint<TProperties>[];
|
|
587
629
|
hooks?: DefineEntityHooks;
|
|
@@ -601,7 +643,10 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
601
643
|
} | {
|
|
602
644
|
[K in Extract<AllKeys<TProperties, TBase>, string>]?: QueryOrderKeysFlat;
|
|
603
645
|
}[];
|
|
604
|
-
|
|
646
|
+
discriminator?: TDiscriminatorColumn;
|
|
647
|
+
/** Alias for `discriminator`. */
|
|
648
|
+
discriminatorColumn?: TDiscriminatorColumn;
|
|
649
|
+
discriminatorValue?: TDiscriminatorValue;
|
|
605
650
|
versionProperty?: AllKeys<TProperties, TBase>;
|
|
606
651
|
concurrencyCheckKeys?: Set<AllKeys<TProperties, TBase>>;
|
|
607
652
|
serializedPrimaryKey?: AllKeys<TProperties, TBase>;
|
|
@@ -611,6 +656,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
611
656
|
type?: string;
|
|
612
657
|
options?: Dictionary;
|
|
613
658
|
expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
659
|
+
where?: string | FilterQuery<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
614
660
|
columns?: IndexColumnOptions[];
|
|
615
661
|
include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
|
|
616
662
|
fillFactor?: number;
|
|
@@ -623,6 +669,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
623
669
|
name?: string;
|
|
624
670
|
options?: Dictionary;
|
|
625
671
|
expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
672
|
+
where?: string | FilterQuery<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
626
673
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
627
674
|
columns?: IndexColumnOptions[];
|
|
628
675
|
include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
|
|
@@ -640,7 +687,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
640
687
|
}[];
|
|
641
688
|
}
|
|
642
689
|
/** Defines an entity schema using property builders, with full type inference from the property definitions. */
|
|
643
|
-
export declare function defineEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, any>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never, const TRepository = never, const TForceObject extends boolean = false>(meta: EntityMetadataWithProperties<TName, TTableName, TProperties, TPK, TBase, TRepository, TForceObject>): EntitySchemaWithMeta<TName, TTableName, InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject>, TBase, TProperties>;
|
|
690
|
+
export declare function defineEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, any>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never, const TRepository = never, const TForceObject extends boolean = false, const TDiscriminatorColumn extends string | undefined = undefined, const TDiscriminatorValue extends string | number | undefined = undefined, const TBaseDiscriminatorColumn extends string | undefined = undefined>(meta: EntityMetadataWithProperties<TName, TTableName, TProperties, TPK, TBase, TRepository, TForceObject, TDiscriminatorColumn, TDiscriminatorValue, TBaseDiscriminatorColumn>): EntitySchemaWithMeta<TName, TTableName, InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject, TBaseDiscriminatorColumn, TDiscriminatorValue>, TBase, TProperties, EntityCtor<InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject, TBaseDiscriminatorColumn, TDiscriminatorValue>>, TDiscriminatorColumn>;
|
|
644
691
|
/** Defines an entity schema for an existing class, combining the class with property builders. */
|
|
645
692
|
export declare function defineEntity<const TEntity = any, const TProperties extends Record<string, any> = Record<string, any>, const TClassName extends string = string, const TTableName extends string = string, const TBase = never, const TClass extends EntityCtor = EntityCtor<TEntity>>(meta: Omit<Partial<EntityMetadata<TEntity>>, 'properties' | 'extends' | 'className' | 'tableName' | 'hooks'> & {
|
|
646
693
|
class: TClass;
|
|
@@ -676,7 +723,7 @@ type InferJSType<T> = T extends typeof Type<infer TValue, any> ? NonNullable<TVa
|
|
|
676
723
|
type InferColumnType<T extends string> = T extends 'int' | 'int4' | 'integer' | 'bigint' | 'int8' | 'int2' | 'tinyint' | 'smallint' | 'mediumint' ? number : T extends 'double' | 'double precision' | 'real' | 'float8' | 'decimal' | 'numeric' | 'float' | 'float4' ? number : T extends 'datetime' | 'time' | 'time with time zone' | 'timestamp' | 'timestamp with time zone' | 'timetz' | 'timestamptz' | 'date' | 'interval' ? Date : T extends 'ObjectId' | 'objectId' | 'character varying' | 'varchar' | 'char' | 'character' | 'uuid' | 'text' | 'tinytext' | 'mediumtext' | 'longtext' | 'enum' ? string : T extends 'boolean' | 'bool' | 'bit' ? boolean : T extends 'blob' | 'tinyblob' | 'mediumblob' | 'longblob' | 'bytea' ? Buffer : T extends 'point' | 'line' | 'lseg' | 'box' | 'circle' | 'path' | 'polygon' | 'geometry' ? number[] : T extends 'tsvector' | 'tsquery' ? string[] : T extends 'json' | 'jsonb' ? any : any;
|
|
677
724
|
type BaseEntityMethodKeys = 'toObject' | 'toPOJO' | 'serialize' | 'assign' | 'populate' | 'init' | 'toReference';
|
|
678
725
|
/** Infers the entity type from a `defineEntity()` properties map, resolving builders, base classes, and primary keys. */
|
|
679
|
-
export type InferEntityFromProperties<Properties extends Record<string, any>, PK extends (keyof Properties)[] | undefined = undefined, Base = never, Repository = never, ForceObject extends boolean = false> = (IsNever<Base> extends true ? {} : Base extends {
|
|
726
|
+
export type InferEntityFromProperties<Properties extends Record<string, any>, PK extends (keyof Properties)[] | undefined = undefined, Base = never, Repository = never, ForceObject extends boolean = false, BaseDiscriminatorColumn extends string | undefined = undefined, DiscriminatorValue extends string | number | undefined = undefined> = (IsNever<Base> extends true ? {} : Base extends {
|
|
680
727
|
toObject(...args: any[]): any;
|
|
681
728
|
} ? Pick<IWrappedEntity<{
|
|
682
729
|
-readonly [K in keyof Properties]: InferBuilderValue<MaybeReturnType<Properties[K]>>;
|
|
@@ -684,19 +731,22 @@ export type InferEntityFromProperties<Properties extends Record<string, any>, PK
|
|
|
684
731
|
[PrimaryKeyProp]?: InferCombinedPrimaryKey<Properties, PK, Base>;
|
|
685
732
|
} & (IsNever<Repository> extends true ? {} : {
|
|
686
733
|
[EntityRepositoryType]?: Repository extends Constructor<infer R> ? R : Repository;
|
|
687
|
-
}) & Omit<Base, typeof PrimaryKeyProp>>, BaseEntityMethodKeys> : {}) & {
|
|
734
|
+
}) & NarrowDiscriminator<Omit<Base, typeof PrimaryKeyProp>, BaseDiscriminatorColumn, DiscriminatorValue>>, BaseEntityMethodKeys> : {}) & {
|
|
688
735
|
-readonly [K in keyof Properties]: InferBuilderValue<MaybeReturnType<Properties[K]>>;
|
|
689
736
|
} & {
|
|
690
737
|
[PrimaryKeyProp]?: InferCombinedPrimaryKey<Properties, PK, Base>;
|
|
691
738
|
} & (IsNever<Repository> extends true ? {} : {
|
|
692
739
|
[EntityRepositoryType]?: Repository extends Constructor<infer R> ? R : Repository;
|
|
693
|
-
}) & (IsNever<Base> extends true ? {} : Omit<Base, typeof PrimaryKeyProp>) & (ForceObject extends true ? {
|
|
740
|
+
}) & (IsNever<Base> extends true ? {} : NarrowDiscriminator<Omit<Base, typeof PrimaryKeyProp>, BaseDiscriminatorColumn, DiscriminatorValue>) & (ForceObject extends true ? {
|
|
694
741
|
[Config]?: DefineConfig<{
|
|
695
742
|
forceObject: true;
|
|
696
743
|
}>;
|
|
697
744
|
} : {}) & {
|
|
698
745
|
[IndexHints]?: [Properties];
|
|
699
746
|
};
|
|
747
|
+
type NarrowDiscriminator<Base, DiscColumn extends string | undefined, DiscValue> = DiscColumn extends string ? DiscColumn extends keyof Base ? DiscValue extends string | number ? Omit<Base, DiscColumn> & {
|
|
748
|
+
[K in DiscColumn]: DiscValue;
|
|
749
|
+
} : Base : Base : Base;
|
|
700
750
|
type InferCombinedPrimaryKey<Properties extends Record<string, any>, PK, Base> = PK extends undefined ? CombinePrimaryKeys<InferPrimaryKey<Properties>, ExtractBasePrimaryKey<Base>> : PK;
|
|
701
751
|
type ExtractBasePrimaryKey<Base> = Base extends {
|
|
702
752
|
[PrimaryKeyProp]?: infer BasePK;
|
|
@@ -738,6 +788,9 @@ type MaybeNullable<Value, Options> = Options extends {
|
|
|
738
788
|
type MaybeRelationRef<Value, Options> = Options extends {
|
|
739
789
|
mapToPk: true;
|
|
740
790
|
} ? Value : Options extends {
|
|
791
|
+
lazyRef: true;
|
|
792
|
+
kind: '1:1' | 'm:1';
|
|
793
|
+
} ? Value extends object ? LazyRef<Value> : never : Options extends {
|
|
741
794
|
ref: true;
|
|
742
795
|
kind: '1:1';
|
|
743
796
|
} ? Value extends object ? Ref<Value> : never : Options extends {
|