import { ObservableObjectAdministration, Annotation, defineProperty, die, flow, isFlow, isFunction, globalState, MakeResult } from "../internal" export function createFlowAnnotation(name: string, options?: object): Annotation { return { annotationType_: name, options_: options, make_, extend_ } } function make_( adm: ObservableObjectAdministration, key: PropertyKey, descriptor: PropertyDescriptor, source: object ): MakeResult { // own if (source === adm.target_) { return this.extend_(adm, key, descriptor, false) === null ? MakeResult.Cancel : MakeResult.Continue } // prototype // bound - must annotate protos to support super.flow() if (this.options_?.bound && !isFlow(adm.target_[key])) { if (this.extend_(adm, key, descriptor, false) === null) return MakeResult.Cancel } if (isFlow(descriptor.value)) { // A prototype could have been annotated already by other constructor, // rest of the proto chain must be annotated already return MakeResult.Break } const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, false, false) defineProperty(source, key, flowDescriptor) return MakeResult.Continue } function extend_( adm: ObservableObjectAdministration, key: PropertyKey, descriptor: PropertyDescriptor, proxyTrap: boolean ): boolean | null { const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, this.options_?.bound) return adm.defineProperty_(key, flowDescriptor, proxyTrap) } function assertFlowDescriptor( adm: ObservableObjectAdministration, { annotationType_ }: Annotation, key: PropertyKey, { value }: PropertyDescriptor ) { if (__DEV__ && !isFunction(value)) { die( `Cannot apply '${annotationType_}' to '${adm.name_}.${key.toString()}':` + `\n'${annotationType_}' can only be used on properties with a generator function value.` ) } } function createFlowDescriptor( adm: ObservableObjectAdministration, annotation: Annotation, key: PropertyKey, descriptor: PropertyDescriptor, bound: boolean, // provides ability to disable safeDescriptors for prototypes safeDescriptors: boolean = globalState.safeDescriptors ): PropertyDescriptor { assertFlowDescriptor(adm, annotation, key, descriptor) let { value } = descriptor if (bound) { value = value.bind(adm.proxy_ ?? adm.target_) } return { value: flow(value), // Non-configurable for classes // prevents accidental field redefinition in subclass configurable: safeDescriptors ? adm.isPlainObject_ : true, // https://github.com/mobxjs/mobx/pull/2641#issuecomment-737292058 enumerable: false, // Non-obsevable, therefore non-writable // Also prevents rewriting in subclass constructor writable: safeDescriptors ? false : true } }