import { JSORMBase } from "./model" ; export type Attr < T > = ( ( ) => T ) | { new ( ... args ) : T & object ; } ; export type AttrType < T > = Attr < T > ; export interface AttrRecord < T > { name ? : string | symbol ; type ? : AttrType < T > ; persist ? : boolean ; } export const attr = < T = any > ( options ? ) : Attribute < T > => { if ( ! options ) { options = { } ; } return new Attribute < T > ( options ) ; } ; export type AttributeValue < Attributes > = { [ K in keyof Attributes ] : Attributes [ K ] ; } ; export type AttributeOptions = Partial < { name : string | symbol ; type : ( ) => any ; persist : boolean ; } > ; export class Attribute < T = any > { isRelationship = false ; name ! : string | symbol ; type ? : T = undefined ; persist : boolean = true ; owner ! : typeof JSORMBase ; constructor ( options ) { if ( ! options ) { return ; } if ( options . name ) { this . name = options . name ; } if ( options . type ) { this . type = ( options . type as any ) as T ; } if ( options . persist !== undefined ) { this . persist = ! ! options . persist ; } } apply ( ModelClass ) { Object . defineProperty ( ModelClass . prototype , this . name , this . descriptor ( ) ) ; } setter ( context , val ) { const privateContext = context ; privateContext . _attributes [ this . name ] = val ; } getter ( context ) { return context . attributes [ this . name ] ; } descriptor ( ) { const attrDef = this ; return { configurable : true , enumerable : true , get ( this ) { return attrDef . getter ( this ) ; } , set ( this , value ) { attrDef . setter ( this , value ) ; } } ; } } const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/ ; const assertType = < T > ( value , type ) : { valid : boolean ; expectedType : string ; } => { let valid ; const expectedType = getType ( type ) ; if ( simpleCheckRE . test ( expectedType ) ) { const t = typeof value ; valid = t === expectedType . toLowerCase ( ) ; if ( ! valid && t === "object" ) { valid = value instanceof type ; } } else if ( expectedType === "Object" ) { valid = isPlainObject ( value ) ; } else if ( expectedType === "Array" ) { valid = Array . isArray ( value ) ; } else { valid = value instanceof type ; } return { valid , expectedType } ; } ; /**
 * Use function string name to check built-in types,
 * because a simple equality check will fail when running
 * across different vms / iframes.
 */ const getType = ( fn ) => { const match = fn && fn . toString ( ) . match ( /^\s*function (\w+)/ ) ; return match ? match [ 1 ] : "" ; } ; const isType = < T > ( type , fn ) : fn is T => { if ( ! Array . isArray ( fn ) ) { return getType ( fn ) === getType ( type ) ; } for ( let i = 0 , len = fn . length ; i < len ; i ++ ) { if ( getType ( fn [ i ] ) === getType ( type ) ) { return true ; } } return false ; } ; /**
 * Get the raw type string of a value e.g. [object Object]
 */ const _toString = Object . prototype . toString ; /**
 * Strict object type check. Only returns true
 * for plain JavaScript objects.
 */ const isPlainObject = ( obj ) : obj is object => { return _toString . call ( obj ) === "[object Object]" ; } ;