import type { AnyEntity , Cast , Constructor , Dictionary , EntityProperty , IsUnknown , Primary , PrimaryProperty } from '../typings' ; import type { EntityFactory } from './EntityFactory' ; import { wrap } from './wrap' ; export type IdentifiedReference < T extends AnyEntity < T > , PK extends keyof T | unknown = PrimaryProperty < T > > = true extends IsUnknown < PK > ? Reference < T > : ( { [ K in Cast < PK , keyof T > ] : T [ K ] ; } & Reference < T > ) ; export class Reference < T extends AnyEntity < T > > { constructor ( private entity ) { this . set ( entity ) ; const meta = this . entity . __meta ! ; meta . primaryKeys . forEach ( primaryKey => { Object . defineProperty ( this , primaryKey , { get ( ) { return this . entity [ primaryKey ] ; } , } ) ; } ) ; if ( meta . serializedPrimaryKey && meta . primaryKeys [ 0 ] !== meta . serializedPrimaryKey ) { Object . defineProperty ( this , meta . serializedPrimaryKey , { get ( ) { return this . entity . __helper ! . getSerializedPrimaryKey ( ) ; } , } ) ; } } static create < T extends AnyEntity < T > , PK extends keyof T | unknown = PrimaryProperty < T > > ( entity ) { if ( Reference . isReference ( entity ) ) { return entity as IdentifiedReference < T , PK > ; } return new Reference ( entity as T ) as IdentifiedReference < T , PK > ; } static createFromPK < T extends AnyEntity < T > , PK extends keyof T | unknown = PrimaryProperty < T > > ( entityType , pk ) { const ref = this . createNakedFromPK ( entityType , pk ) ; return new Reference ( ref ) as IdentifiedReference < T , PK > ; } static createNakedFromPK < T extends AnyEntity < T > , PK extends keyof T | unknown = PrimaryProperty < T > > ( entityType , pk ) { const factory = entityType . prototype . __factory as EntityFactory ; return factory . createReference ( entityType , pk , { merge : false } ) ; } /**
     * Checks whether the argument is instance or `Reference` wrapper.
     */ static isReference < T extends AnyEntity < T > > ( data ) { return data && ! ! data . __reference ; } /**
     * Wraps the entity in a `Reference` wrapper if the property is defined as `wrappedReference`.
     */ static wrapReference < T extends AnyEntity < T > > ( entity , prop ) { if ( entity && prop . wrappedReference && ! Reference . isReference ( entity ) ) { return Reference . create ( entity as T ) ; } return entity ; } /**
     * Returns wrapped entity.
     */ static unwrapReference < T extends AnyEntity < T > > ( ref ) { return Reference . isReference < T > ( ref ) ? ( ref as Reference < T > ) . unwrap ( ) : ref ; } /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
     * Returns the entity.
     */ async load ( ) ; /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
     * Returns the requested property instead of the whole entity.
     */ async load < K extends keyof T > ( prop ) ; /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
     * Returns either the whole entity, or the requested property.
     */ async load < K extends keyof T = never > ( prop ? ) { if ( ! this . isInitialized ( ) ) { await this . entity . __helper ! . init ( ) ; } if ( prop ) { return this . entity [ prop ] ; } return this . entity ; } set ( entity ) { if ( entity instanceof Reference ) { entity = entity . unwrap ( ) ; } this . entity = entity ; } unwrap ( ) { return this . entity ; } getEntity ( ) { if ( ! this . isInitialized ( ) ) { throw new Error ( `Reference<${ this . entity . __meta ! . name }> ${ this . entity . __helper ! . getPrimaryKey ( ) } not initialized` ) ; } return this . entity ; } getProperty < K extends keyof T > ( prop ) { return this . getEntity ( ) [ prop ] ; } isInitialized ( ) { return this . entity . __helper ! . __initialized ; } populated ( populated ? ) { this . entity . __helper ! . populated ! ( populated ) ; } toJSON ( ... args ) { return wrap ( this . entity ) . toJSON ! ( ... args ) ; } } Object . defineProperties ( Reference . prototype , { __reference : { value : true , enumerable : false } , __meta : { get ( ) { return this . entity . __meta ! ; } } , __platform : { get ( ) { return this . entity . __platform ! ; } } , __helper : { get ( ) { return this . entity . __helper ! ; } } , $ : { get ( ) { return this . entity ; } } , get : { get ( ) { return ( ) => this . entity ; } } , } ) ;