import { ExecutorState , ExecutorContainer } from './container' ; import { createExecutorContainer } from './container' ; import { AnyExpressionFunctionDefinition , ExpressionFunction } from '../expression_functions' ; import { Execution , ExecutionParams } from '../execution/execution' ; import { IRegistry } from '../types' ; import { ExpressionType } from '../expression_types/expression_type' ; import { AnyExpressionTypeDefinition } from '../expression_types/types' ; import { ExpressionAstExpression } from '../ast' ; import { typeSpecs } from '../expression_types/specs' ; import { functionSpecs } from '../expression_functions/specs' ; export interface ExpressionExecOptions { /**
     * Whether to execute expression in *debug mode*. In *debug mode* inputs and
     * outputs as well as all resolved arguments and time it took to execute each
     * function are saved and are available for introspection.
     */ debug ? : boolean ; } export class TypesRegistry implements IRegistry < ExpressionType > { constructor ( private readonly executor ) { } public register ( typeDefinition ) { this . executor . registerType ( typeDefinition ) ; } public get ( id ) { return this . executor . state . selectors . getType ( id ) ; } public toJS ( ) { return this . executor . getTypes ( ) ; } public toArray ( ) { return Object . values ( this . toJS ( ) ) ; } } export class FunctionsRegistry implements IRegistry < ExpressionFunction > { constructor ( private readonly executor ) { } public register ( functionDefinition ) { this . executor . registerFunction ( functionDefinition ) ; } public get ( id ) { return this . executor . state . selectors . getFunction ( id ) ; } public toJS ( ) { return this . executor . getFunctions ( ) ; } public toArray ( ) { return Object . values ( this . toJS ( ) ) ; } } export class Executor < Context extends Record < string , unknown > = Record < string , unknown > > { static createWithDefaults < Ctx extends Record < string , unknown > = Record < string , unknown > > ( state ? ) { const executor = new Executor < Ctx > ( state ) ; for ( const type of typeSpecs ) executor . registerType ( type ) ; for ( const func of functionSpecs ) executor . registerFunction ( func ) ; return executor ; } public readonly state : ExecutorContainer < Context > ; deprecated public readonly functions : FunctionsRegistry ; deprecated public readonly types : TypesRegistry ; constructor ( state ? ) { this . state = createExecutorContainer < Context > ( state ) ; this . functions = new FunctionsRegistry ( this ) ; this . types = new TypesRegistry ( this ) ; } public registerFunction ( functionDefinition ) { const fn = new ExpressionFunction ( typeof functionDefinition === 'object' ? functionDefinition : functionDefinition ( ) ) ; this . state . transitions . addFunction ( fn ) ; } public getFunction ( name ) { return this . state . get ( ) . functions [ name ] ; } public getFunctions ( ) { return { ... this . state . get ( ) . functions } ; } public registerType ( typeDefinition ) { const type = new ExpressionType ( typeof typeDefinition === 'object' ? typeDefinition : typeDefinition ( ) ) ; this . state . transitions . addType ( type ) ; } public getType ( name ) { return this . state . get ( ) . types [ name ] ; } public getTypes ( ) { return { ... this . state . get ( ) . types } ; } public extendContext ( extraContext ) { this . state . transitions . extendContext ( extraContext ) ; } public get context ( ) : Record < string , unknown > { return this . state . selectors . getContext ( ) ; } param ast param input param context public async run < Input , Output , ExtraContext extends Record < string , unknown > = Record < string , unknown > > ( ast , input , context ? ) { const execution = this . createExecution ( ast , context ) ; execution . start ( input ) ; return ( await execution . result ) as Output ; } public createExecution < ExtraContext extends Record < string , unknown > = Record < string , unknown > , Input = unknown , Output = unknown > ( ast , context = { } as ExtraContext , { debug } = { } as ExpressionExecOptions ) { const params = { executor : this , context : { ... this . context , ... context , } as Context & ExtraContext , debug , } ; if ( typeof ast === 'string' ) params . expression = ast ; else params . ast = ast ; const execution = new Execution < Context & ExtraContext , Input , Output > ( params ) ; return execution ; } public fork ( ) { const initialState = this . state . get ( ) ; const fork = new Executor < Context > ( initialState ) ; /**
         * Synchronize registry state - make any new types, functions and context
         * also available in the forked instance of `Executor`.
         */ this . state . state$ . subscribe ( ( { types , functions , context } ) => { const state = fork . state . get ( ) ; fork . state . set ( { ... state , types : { ... types , ... state . types , } , functions : { ... functions , ... state . functions , } , context : { ... context , ... state . context , } , } ) ; } ) ; return fork ; } }