import { currentFlushSecret , Entity , EntityConstructor , EntityManager , EntityMetadata , EntityOrmField , Field , getMetadata , ManyToManyField , ManyToOneField , OneToManyField , OneToOneField , OptsOf , PolymorphicField , } from "./EntityManager" ; import { maybeResolveReferenceToId , tagFromId } from "./keys" ; import { Reference } from "./relations" ; import { AbstractRelationImpl } from "./relations/AbstractRelationImpl" ; import { reverseHint } from "./reverseHint" ; import { fail } from "./utils" ; export { newPgConnectionConfig } from "joist-utils" ; export { BaseEntity } from "./BaseEntity" ; export * from "./changes" ; export { ConfigApi , EntityHook } from "./config" ; export { DeepPartialOrNull } from "./createOrUpdatePartial" ; export * from "./drivers" ; export * from "./EntityManager" ; export * from "./getProperties" ; export * from "./keys" ; export { isNew , Loadable , Loaded , LoadHint , New , RelationsIn } from "./loaded" ; export * from "./loadLens" ; export * from "./newTestInstance" ; export * from "./QueryBuilder" ; export * from "./relations" ; export * from "./reverseHint" ; export { GenericError , newRequiredRule , ValidationError , ValidationErrors , ValidationRule , ValidationRuleResult , } from "./rules" ; export * from "./serde" ; export { fail } from "./utils" ; interface Flavoring < FlavorT > { _type ? : FlavorT ; } export type Flavor < T , FlavorT > = T & Flavoring < FlavorT > ; export function setField < T extends Entity > ( entity , fieldName , newValue ) { ensureNotDeleted ( entity , { ignore : "pending" } ) ; const em = getEm ( entity ) ; if ( em . isFlushing ) { const { flushSecret } = currentFlushSecret . getStore ( ) || { } ; if ( flushSecret === undefined ) { throw new Error ( `Cannot set '${ fieldName }' on ${ entity } during a flush outside of a entity hook` ) ; } if ( flushSecret !== em [ "flushSecret" ] ) { throw new Error ( `Attempting to reuse a hook context outside its flush loop` ) ; } } const { data , originalData } = entity . __orm ; if ( fieldName in originalData ) { if ( equal ( originalData [ fieldName ] , newValue ) ) { data [ fieldName ] = newValue ; delete originalData [ fieldName ] ; return true ; } } const currentValue = data [ fieldName ] ; if ( equal ( currentValue , newValue ) ) { return false ; } if ( ! ( fieldName in originalData ) ) { originalData [ fieldName ] = currentValue ; } data [ fieldName ] = newValue ; return true ; } /**
 * Sets each value in `values` on the current entity.
 *
 * The default behavior is that passing a value as either `null` or `undefined` will set
 * the field as `undefined`, i.e. automatic `null` to `undefined` conversion.
 *
 * However, if you pass `ignoreUndefined: true`, then any opt that is `undefined` will be treated
 * as "do not set", and `null` will still mean "set to `undefined`". This is useful for implementing
 * APIs were an input of `undefined` means "do not set / noop" and `null` means "unset".
 */ export function setOpts < T extends Entity > ( entity , values , opts ? ) { if ( values === undefined || typeof values === "string" ) { return ; } const { calledFromConstructor , partial } = opts || { } ; const meta = getMetadata ( entity ) ; Object . entries ( values as { } ) . forEach ( ( [ key , _value ] ) => { const field = meta . fields [ key ] ; if ( ! field ) { throw new Error ( `Unknown field ${ key }` ) ; } if ( partial && _value === undefined ) { return ; } const value = _value === null || ( typeof _value === "string" && _value . trim ( ) === "" ) ? undefined : _value ; const current = ( entity as any ) [ key ] ; if ( current instanceof AbstractRelationImpl ) { if ( calledFromConstructor ) { current . setFromOpts ( value ) ; } else if ( partial && ( field . kind === "o2m" || field . kind === "m2m" ) ) { const values = value as any [ ] ; const allowDelete = ! field . otherMetadata ( ) . fields [ "delete" ] ; const allowRemove = ! field . otherMetadata ( ) . fields [ "remove" ] ; const allowOp = ! field . otherMetadata ( ) . fields [ "op" ] ; const anyValueHasOp = allowOp && values . some ( ( v ) => ! ! v . op ) ; if ( anyValueHasOp ) { const anyValueMissingOp = values . some ( ( v ) => ! v . op ) ; if ( anyValueMissingOp ) { throw new Error ( "If any child sets the `op` key, then all children must have the `op` key." ) ; } values . forEach ( ( v ) => { if ( v . op === "delete" ) { getEm ( entity ) . delete ( v ) ; } else if ( v . op === "remove" ) { ( current as any ) . remove ( v ) ; } else if ( v . op === "include" ) { ( current as any ) . add ( v ) ; } else if ( v . op === "incremental" ) { } } ) ; return ; } const toSet = [ ] ; values . forEach ( ( e ) => { if ( allowDelete && e . delete === true ) { getEm ( entity ) . delete ( e ) ; } else if ( allowRemove && e . remove === true ) { } else { toSet . push ( e ) ; } } ) ; current . set ( toSet ) ; } else { current . set ( value ) ; } } else { ( entity as any ) [ key ] = value ; } } ) ; if ( calledFromConstructor ) { getRelations ( entity ) . forEach ( ( v ) => v . initializeForNewEntity ( ) ) ; } } export function ensureNotDeleted ( entity , opts = { } ) { if ( entity . isDeletedEntity && ( opts . ignore === undefined || entity . __orm . deleted !== opts . ignore ) ) { fail ( `${ entity } is marked as deleted` ) ; } } /** Adds `null` to every key in `T` to accept partial-update-style input. */ export type PartialOrNull < T > = { [ P in keyof T ] ? : T [ P ] | null ; } ; export function getRequiredKeys < T extends Entity > ( entity ) ; export function getRequiredKeys < T extends Entity > ( type ) ; export function getRequiredKeys < T extends Entity > ( entityOrType ) { return Object . values ( getMetadata ( entityOrType as any ) . fields ) . filter ( ( f ) => f . required ) . map ( ( f ) => f . fieldName ) ; } const tagToConstructorMap = new Map < string , EntityConstructor < any > > ( ) ; /** Processes the metas based on any custom calls to the `configApi` hooks. */ export function configureMetadata ( metas ) { metas . forEach ( ( meta ) => { tagToConstructorMap . set ( meta . tagName , meta . cstr ) ; meta . config . __data . rules . forEach ( ( rule ) => { if ( ( rule as any ) . hint ) { const reversals = reverseHint ( meta . cstr , ( rule as any ) . hint ) ; reversals . forEach ( ( [ otherEntity , reverseHint ] ) => { getMetadata ( otherEntity ) . config . __data . reactiveRules . push ( reverseHint ) ; } ) ; } } ) ; Object . entries ( meta . config . __data . asyncDerivedFields ) . forEach ( ( [ , entry ] ) => { const hint = entry ! [ 0 ] ; const reversals = reverseHint ( meta . cstr , hint ) ; reversals . forEach ( ( [ otherEntity , reverseHint ] ) => { getMetadata ( otherEntity ) . config . __data . reactiveDerivedValues . push ( reverseHint ) ; } ) ; } ) ; } ) ; } export function getEm ( entity ) { return entity . __orm . em ; } export function getRelations ( entity ) { return Object . values ( entity ) . filter ( ( v ) => v instanceof AbstractRelationImpl ) ; } export function getConstructorFromTaggedId ( id ) { const tag = tagFromId ( id ) ; return tagToConstructorMap . get ( tag ) ?? fail ( `Unknown tag: "${ tag }" ` ) ; } export function maybeGetConstructorFromReference ( value ) { const id = maybeResolveReferenceToId ( value ) ; return id ? getConstructorFromTaggedId ( id ) : undefined ; } function equal ( a , b ) { return a === b || ( a instanceof Date && b instanceof Date && a . getTime ( ) == b . getTime ( ) ) ; } export function isOneToManyField ( ormField ) { return ormField . kind === "o2m" ; } export function isManyToOneField ( ormField ) { return ormField . kind === "m2o" ; } export function isManyToManyField ( ormField ) { return ormField . kind === "m2m" ; } export function isOneToOneField ( ormField ) { return ormField . kind === "o2o" ; } export function isPolymorphicField ( ormField ) { return ormField . kind === "poly" ; } export function isReferenceField ( ormField ) { return ormField . kind === "m2o" || ormField . kind === "o2o" || ormField . kind === "poly" ; } export function isCollectionField ( ormField ) { return ormField . kind === "o2m" || ormField . kind === "m2m" ; }