UNPKG

@yantrix/codegen

Version:

Yantrix framework code generator API

1,411 lines (1,337 loc) 97.4 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/index.ts import { YantrixParser as YantrixParser2 } from "@yantrix/yantrix-parser"; // src/core/InjectFunctionsProcess.ts import path from "node:path"; // src/core/modules/Java.ts import { BasicActionDictionary, BasicStateDictionary } from "@yantrix/automata"; import { StartState } from "@yantrix/mermaid-parser"; // src/core/shared.ts import { ExpressionTypes } from "@yantrix/yantrix-parser"; // src/constants.ts var ByPassAction = "[-]"; var DEFAULT_USER_FUNCTIONS_NAMESPACE = "userFunctions"; // src/core/shared.ts function fillDictionaries(diagram, stateDictionary, actionDictionary, eventDictionary) { if (stateDictionary) { const stateKeys = diagram.states.map((s) => s.id); stateDictionary.addStates({ keys: stateKeys }); } if (actionDictionary) { for (const state2 of diagram.states) { for (const path2 of state2.actionsPath.map((p) => p.action)) { const firstAction = path2[0]; const isUniqueAction = actionDictionary.getActionValues({ keys: [firstAction] })[0] === null; if (!isUniqueAction) { continue; } actionDictionary.addActions({ keys: [firstAction] }); } } } if (eventDictionary) { for (const state2 of diagram.states) { const emittedEventsKeys = state2.notes?.emit.map((event) => event.identifier); if (emittedEventsKeys && emittedEventsKeys.length > 0) { const uniqueKeys = emittedEventsKeys.filter((e) => eventDictionary.getEventValues({ keys: [e] })[0] == null); eventDictionary.addEvents({ keys: uniqueKeys }); } const subscribedEventsKeys = state2.notes?.subscribe.map((event) => event.identifier); if (subscribedEventsKeys && subscribedEventsKeys.length > 0) { const uniqueKeys = subscribedEventsKeys.filter((e) => eventDictionary.getEventValues({ keys: [e] })[0] == null); eventDictionary.addEvents({ keys: uniqueKeys }); } } } } __name(fillDictionaries, "fillDictionaries"); function getStatesByPass(diagram, stateDictionary) { const byPassed = []; diagram.states.forEach((state2) => { if (state2.notes?.byPass) { const actionChains = diagram.actionChains[state2.id]; if (!actionChains) throw new Error(`ByPassed state ${state2.id} doesn't have transition`); const byPassAction = actionChains[ByPassAction]; if (!byPassAction) { throw new Error(`ByPass action ${ByPassAction} not found for state ${state2.id}`); } byPassAction.chains.forEach(({ chain }) => { if (byPassAction.chains.length > 1 && chain.length === 0) { throw new Error(`ByPass action ${ByPassAction} should have more than one transition`); } }); const value = stateDictionary.getStateValues({ keys: [state2.id] })[0]; if (!value) throw new Error(`State ${state2.id} not found`); byPassed.push(value); } }); return byPassed; } __name(getStatesByPass, "getStatesByPass"); var pathRecord = { [ExpressionTypes.Constant]: "constant", [ExpressionTypes.Context]: "prevContext", [ExpressionTypes.Payload]: "payload" }; // src/core/modules/Java.ts var JavaCodegen = class { static { __name(this, "JavaCodegen"); } stateDictionary; actionDictionary; diagram; handlersDict; initialContext; initialContextKeys; changeStateHandlers; dictionaries; imports = { "@yantrix/automata": ["GenericAutomata"] }; package = "org.example"; // base package name for all automata files constructor({ diagram }) { this.actionDictionary = new BasicActionDictionary(); this.stateDictionary = new BasicStateDictionary(); this.diagram = diagram; this.handlersDict = []; this.changeStateHandlers = []; this.dictionaries = []; this.initialContextKeys = []; this.initialContext = this.getInitialContext(); fillDictionaries(diagram, this.stateDictionary, this.actionDictionary); this.setupDictionaries(); } getCode(options) { return ` ${this.getImports()} ${this.getClassTemplate(options.className)} `; } // Package declaration and imports necessary for the automata to function getImports() { const lines = [ `package ${this.package};`, `import java.util.Map;`, `import java.util.Objects;`, `import java.util.HashMap;`, `import java.util.function.Function;` ]; return lines.join("\n"); } getDictionaries() { return this.dictionaries.join("\n"); } getActionToStateFromState() { return `public final Map<TAutomataBaseState, Map<TAutomataBaseAction, AutomataStateTransitionResult>> stateTransitionMatrix = Map.ofEntries( ${this.getStateTransitionMatrix()} ); `; } // State transition matrix getStateTransitionMatrix() { return Object.entries(this.diagram.transitions).map(([state2, transitions]) => { const value = this.stateDictionary.getStateValues({ keys: [state2] })[0]; if (!value) throw new Error(`State ${state2} not found`); return ` Map.entry( TAutomataBaseState.of(${value}L), Map.of( ${this.getTransitions(transitions).join(",\n")} ) ) `; }).join(",\n"); } getTransitions(transitions) { return Object.entries(transitions).map(([state2, transition]) => { const newState = this.stateDictionary.getStateValues({ keys: [state2] })[0]; return transition.actionsPath.map(({ action }) => { const actionValue = this.actionDictionary.getActionValues({ keys: action })[0]; if (!actionValue) throw new Error(`Action ${action} not found`); if (!newState) throw new Error(`State ${state2} not found`); return ` TAutomataBaseAction.of(${actionValue}L), new AutomataStateTransitionResult( TAutomataBaseState.of(${newState}L), (payloadContext) -> { var prevContext = getDefaultContext(payloadContext); return prevContext; } ) `; }); }).flatMap((el) => `${el.join(",\n ")}`); } getDefaultContext() { return ` private TAutomataBaseContext getDefaultContext(AutomataPayloadContext arg) { TAutomataBaseContext prevContext = arg.context(); return prevContext; } `; } // Full class declaration with all dictionaries and handlers inside getClassTemplate(className) { return ` public final class ${className} { ${this.getDictionaries()} ${this.getActionToStateFromState()} ${this.setupClassMembers()} ${this.getDefaultConstructor(className)} ${this.setupClassMembersAccessors()} ${this.getDefaultContext()} ${this.dispatchMethod()} ${this.toStringMethod()} ${this.getTypes()} } `; } // Default constructor for the class getDefaultConstructor(className) { return ` public ${className}() { this.state = TAutomataBaseState.of(${this.getInitialState()}L); this.context = ${this.getInitialContext()}; this.rootReducer = ${this.getRootReducer()}; } `; } // Transforms codegen dictionaries into language-specific text representations setupDictionaries() { this.dictionaries.push(` public static final Map<String, TAutomataBaseState> statesDictionary = Map.of( ${Object.entries(this.stateDictionary.getDictionary()).map(([key, value]) => `"${key}", TAutomataBaseState.of(${value}L)`).join(",\n")} ); `); this.dictionaries.push(` public static final Map<String, TAutomataBaseAction> actionsDictionary = Map.of( ${Object.entries(this.actionDictionary.getDictionary()).map(([key, value]) => `"${key}", TAutomataBaseAction.of(${value}L)`).join(",\n")} ); `); } // Initial context is empty getInitialContext() { return "new TAutomataBaseContext()"; } // Fetches first state from list getInitialState() { return this.stateDictionary.getStateValues({ keys: [StartState] })[0]; } // Root reducer function getRootReducer() { return ` (obj) -> { if(obj.action == null || obj.payload == null) { return TAutomataStateContext.of(obj.state, obj.context); } ${this.getRootReducerStateValidation()} ${this.getRootReducerActionValidation()} AutomataStateTransitionResult res = stateTransitionMatrix.get(obj.state).get(obj.action); return TAutomataStateContext.of( res.newState(), res.getNewContext().apply(new AutomataPayloadContext(obj.payload, obj.context)) ); } `; } // Checks if state can be found in dictionary for the automata getRootReducerStateValidation() { return ` // state validation if(!stateTransitionMatrix.containsKey(obj.state)) { throw new RuntimeException("Invalid state, maybe machine isn't running"); } `; } // Checks if action can be found in dictionary for the automata & if the action is appropriate for the automata state getRootReducerActionValidation() { return ` // action validation if(!stateTransitionMatrix.get(obj.state).containsKey(obj.action)) { return TAutomataStateContext.of( obj.state, obj.context ); } `; } setupClassMembers() { return ` private TAutomataBaseState state; private TAutomataBaseContext context; private IAutomataReducer rootReducer; `; } setupClassMembersAccessors() { return ` public Map getStateTransitionMatrix() { return this.stateTransitionMatrix; } public TAutomataStateContext getContext() { return new TAutomataStateContext(this.state, this.context); } public IAutomataReducer getReducer() { return this.rootReducer; } public void setContext(TAutomataStateContext context) { this.state = context.state; this.context = context.context; } `; } dispatchMethod() { return ` public TAutomataStateContext dispatch(TAutomataActionPayload action) { TAutomataStateContext reducedValue = this.rootReducer.apply(new TAutomataStateContextActionPayload(this.state, this.context, action.action, action.payload)); this.setContext(reducedValue); return reducedValue; } `; } toStringMethod() { return ` @Override public String toString() { return "GeneratedAutomata{" + "statesDictionary=" + statesDictionary + ", actionsDictionary=" + actionsDictionary + ", stateTransitionMatrix=" + stateTransitionMatrix + '}'; } `; } // Types necessary for the automata getTypes() { return ` public abstract static class TAutomataBaseType { protected Long value; public Long getValue() { return value; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TAutomataBaseType that = (TAutomataBaseType) o; return Objects.equals(value, that.value); } @Override public int hashCode() { return Objects.hash(value); } } public static class TAutomataBaseState extends TAutomataBaseType { private TAutomataBaseState() {} private TAutomataBaseState(Long value) { this.value = value; } public static TAutomataBaseState of(Long value) { return new TAutomataBaseState(value); } } public static class TAutomataBaseAction extends TAutomataBaseType { private TAutomataBaseAction() {} private TAutomataBaseAction(Long value) { this.value = value; } public static TAutomataBaseAction of(Long value) { return new TAutomataBaseAction(value); } } public static class TAutomataBaseContext extends HashMap<String, Object> { public TAutomataBaseContext(Map<String, Object> map) { super(map); } private TAutomataBaseContext(TAutomataBasePayload payload) { super(); this.putAll(payload); } public static TAutomataBaseContext fromPayload(TAutomataBasePayload payload) { return new TAutomataBaseContext(payload); } } public static class TAutomataBasePayload extends HashMap<String, Object> { } public record AutomataStateTransitionResult ( TAutomataBaseState newState, Function<AutomataPayloadContext, TAutomataBaseContext> getNewContext ) { } public interface IAutomataReducer extends Function<TAutomataStateContextActionPayload, TAutomataStateContext> {} public record TAutomataStateContext(TAutomataBaseState state, TAutomataBaseContext context) {} public record TAutomataStateContextActionPayload(TAutomataBaseState state, TAutomataBaseContext context, TAutomataBaseAction action, TAutomataBasePayload payload) {} public record AutomataPayloadContext(TAutomataBasePayload payload,TAutomataBaseContext context) {} public record TAutomataActionPayload(TAutomataBaseAction action, TAutomataBasePayload payload) {} `; } }; // src/core/modules/JavaScript/codegen.ts import { BasicActionDictionary as BasicActionDictionary2, BasicEventDictionary, BasicStateDictionary as BasicStateDictionary2 } from "@yantrix/automata"; // src/core/modules/JavaScript/JavaScriptCompiler/class/serializer.ts import { StartState as StartState3 } from "@yantrix/mermaid-parser"; // src/core/templates/JavaScript.ts var JavaScriptTemplate = ` export class %CLASSNAME% extends GenericAutomata { static id = %ID%; static actions = %ACTIONS_MAP%; static states = %STATES_MAP%; static getState = %GET_STATE%; static hasState = %HAS_STATE%; static getAction = %GET_ACTION%; static createAction = %CREATE_ACTION%; constructor() { super(eventAdapter); this.init({ state: %STATE%, context:%CONTEXT%, rootReducer: %REDUCER%, stateValidator: %STATE_VALIDATOR%, actionValidator: %ACTION_VALIDATOR%, functionRegistry: %FUNCTION_REGISTRY% }); } isKeyOf = %IS_KEY_OF%; } export default %CLASSNAME%;`; // src/utils/utils.ts function replaceFileContents(replacementMap) { let res = JavaScriptTemplate; Object.entries(replacementMap).forEach(([template, str]) => { res = res.replaceAll(template, str); }); return res; } __name(replaceFileContents, "replaceFileContents"); // src/core/modules/JavaScript/JavaScriptCompiler/context/core.ts function getInitialContextShape(props) { const states = props.diagram.states.filter((state2) => state2.id === props.stateName); if (states.length) { return states.reduce( (acc, curr) => { curr.notes?.contextDescription.forEach((el) => { el.context.forEach((el2) => { acc[el2.keyItem.identifier] = null; }); }); return acc; }, {} ); } return null; } __name(getInitialContextShape, "getInitialContextShape"); // src/core/modules/JavaScript/JavaScriptCompiler/context/serializer.ts import { StartState as StartState2 } from "@yantrix/mermaid-parser"; import { ExpressionTypes as ExpressionTypes4, isContextWithReducer, isKeyItemReference as isKeyItemReference2, isKeyItemWithExpression as isKeyItemWithExpression2 } from "@yantrix/yantrix-parser"; // src/core/modules/JavaScript/JavaScriptCompiler/expressions/core.ts import { ExpressionTypes as ExpressionTypes3, isKeyItemReference, isKeyItemWithExpression, maxNestedFuncLevel } from "@yantrix/yantrix-parser"; // src/core/modules/JavaScript/JavaScriptCompiler/expressions/functions.ts import { builtInFunctions, ReservedInternalFunctionNames } from "@yantrix/functions"; import { ExpressionTypes as ExpressionTypes2 } from "@yantrix/yantrix-parser"; function getFunctionBody(props) { if (props.expression.expressionType === ExpressionTypes2.Function) { const { FunctionName, Arguments } = props.expression.FunctionDeclaration; if (ReservedInternalFunctionNames.includes(FunctionName)) { return `(function() { const func = functionDictionary.get('${FunctionName}'); return func(automata); })()`; } const argsList = Arguments.map((arg) => { if (arg.expressionType === ExpressionTypes2.Function) { return getFunctionBody({ expression: arg, expressions: props.expressions }); } else { return getExpressionValueDefine({ expression: arg, expressions: props.expressions }); } }).join(", "); return `(function() { const func = functionDictionary.get('${FunctionName}'); return func(${argsList}); })()`; } else { return getExpressionValueDefine({ expression: props.expression, expressions: props.expressions }); } } __name(getFunctionBody, "getFunctionBody"); // src/core/modules/JavaScript/JavaScriptCompiler/expressions/serializer.ts function getDefaultPropertyContext(path2, indetifier, expression) { const fullPath = getReferenceString(path2, indetifier); return `(function(){ if(${path2} !== null && ${fullPath} !== undefined && ${fullPath} !== null) { return ${path2}['${indetifier}'] } else { return ${expression ?? "null"} } }())`; } __name(getDefaultPropertyContext, "getDefaultPropertyContext"); function getReferenceString(path2, identifier) { return `${path2}['${identifier}']`; } __name(getReferenceString, "getReferenceString"); function getFunctionFromDictionary(name) { return `functionDictionary.get('${name}')`; } __name(getFunctionFromDictionary, "getFunctionFromDictionary"); var expressionsSerializer = { getDefaultPropertyContext, getFunctionFromDictionary }; // src/core/modules/JavaScript/JavaScriptCompiler/expressions/core.ts function setupExpressions(props) { const expressionRecord = { [ExpressionTypes3.ArrayDeclaration]: () => "[]", [ExpressionTypes3.Constant]: ({ identifier }) => { if (props.constants === null) { throw new Error("Missing dictionary with constants"); } if (props.constants[identifier] === void 0) { throw new Error( `The identifier is missing in the const dictionary: ${identifier}` ); } if (typeof props.constants[identifier] === "string") return `"${props.constants[identifier]}"`; return `${props.constants[identifier]}`; }, [ExpressionTypes3.Function]: () => { return "null"; }, [ExpressionTypes3.DecimalDeclaration]: ({ NumberDeclaration }) => { return `${NumberDeclaration}`; }, [ExpressionTypes3.IntegerDeclaration]: ({ NumberDeclaration }) => { return `${NumberDeclaration}`; }, [ExpressionTypes3.StringDeclaration]: ({ StringDeclaration }) => { return `'${StringDeclaration}'`; }, [ExpressionTypes3.Context]: ({ identifier }) => { return `prevContext === null || (prevContext === undefined || prevContext['${identifier}'] === undefined) ? null : prevContext['${identifier}']`; }, [ExpressionTypes3.Payload]: ({ identifier }) => { return `payload === null || (payload === undefined || payload['${identifier}'] === undefined) ? null : payload['${identifier}']`; } }; expressionRecord[ExpressionTypes3.Function] = (func) => { let currentRecLevel = 0; const recursive = /* @__PURE__ */ __name((func2) => { const { FunctionDeclaration } = func2; const { FunctionName, Arguments } = FunctionDeclaration; const res2 = []; if (currentRecLevel < maxNestedFuncLevel) { Arguments.forEach((item) => { if (isKeyItemReference(item)) { const { expressionType, identifier } = item; const path2 = pathRecord[expressionType]; if (isKeyItemWithExpression(item)) { const { expression } = item; if (expression.expressionType === ExpressionTypes3.Function) { currentRecLevel++; res2.push(recursive(expression)); } const valueExpression = getExpressionValue({ expressionRecord, expression }); res2.push( `${expressionsSerializer.getDefaultPropertyContext(path2, identifier, valueExpression)}` ); } else { if (item.expressionType === ExpressionTypes3.Constant) { const expressionValueRight = getExpressionValue({ expressionRecord, expression: item }); res2.push(expressionValueRight); } else { res2.push( `${expressionsSerializer.getDefaultPropertyContext(path2, identifier)}` ); } } } else { if (item.expressionType === ExpressionTypes3.Function) { res2.push(recursive(item)); } else { const valueExpression = getExpressionValue({ expressionRecord, expression: item }); res2.push(valueExpression); } } }); } else { throw new Error( `Max level of nested functions reached ${maxNestedFuncLevel}` ); } return expressionsSerializer.getFunctionFromDictionary(FunctionName).concat( `(${res2.join(",")})` ); }, "recursive"); const res = recursive(func); return res; }; return expressionRecord; } __name(setupExpressions, "setupExpressions"); function getExpressionValue(props) { return props.expressionRecord[props.expression.expressionType](props.expression); } __name(getExpressionValue, "getExpressionValue"); function getExpressionValueDefine(props) { switch (props.expression.expressionType) { case ExpressionTypes3.Identifier: return props.expression.identifier; case ExpressionTypes3.Function: return getFunctionBody({ expression: props.expression, expressions: props.expressions }); default: return getExpressionValue({ expressionRecord: props.expressions, expression: props.expression }); } } __name(getExpressionValueDefine, "getExpressionValueDefine"); // src/core/modules/JavaScript/JavaScriptCompiler/expressions/index.ts var expressions = { serializer: expressionsSerializer, functions: { getExpressionValue, setupExpressions } }; // src/core/modules/JavaScript/JavaScriptCompiler/context/serializer.ts function getStateReducerCode(props) { return `const reducer = { ${getStateToContext(props).join(",\n ")} }`; } __name(getStateReducerCode, "getStateReducerCode"); function getContextItem(props) { if (isContextWithReducer(props.ctx)) { const { context: context2, reducer } = props.ctx; return getBoundValues({ expressions: props.expressions, arr: mapReducerItems({ reducer, expressions: props.expressions }), context: context2 }); } else { const { context: context2 } = props.ctx; return context2.map(({ keyItem }) => { const { identifier } = keyItem; if (isKeyItemWithExpression2(keyItem)) { const expressionValue = expressions.functions.getExpressionValue({ expression: keyItem.expression, expressionRecord: props.expressions }); return `${identifier}: ${expressions.serializer.getDefaultPropertyContext("prevContext", identifier, expressionValue)}`; } else { return `${identifier}: ${expressions.serializer.getDefaultPropertyContext("prevContext", identifier)}`; } }); } } __name(getContextItem, "getContextItem"); function mapReducerItems(props) { return props.reducer.map(({ keyItem }) => { if (isKeyItemReference2(keyItem)) { const { expressionType, identifier: boundIdentifier } = keyItem; const path2 = props.sourcePath ?? pathRecord[expressionType]; if (keyItem.expressionType === ExpressionTypes4.Constant) { const expressionValueRight = expressions.functions.getExpressionValue({ expression: keyItem, expressionRecord: props.expressions }); return `(function(){ return ${expressionValueRight} }())`; } if (isKeyItemWithExpression2(keyItem)) { const { expression } = keyItem; const expressionValueRight = expressions.functions.getExpressionValue({ expression, expressionRecord: props.expressions }); return expressions.serializer.getDefaultPropertyContext(path2, boundIdentifier, expressionValueRight); } return expressions.serializer.getDefaultPropertyContext(path2, boundIdentifier); } else { const { expression } = keyItem; const expressionValueRight = expressions.functions.getExpressionValue({ expression, expressionRecord: props.expressions }); return `(function(){ return ${expressionValueRight} }())`; } }); } __name(mapReducerItems, "mapReducerItems"); function getBoundValues(props) { return props.arr.map((el, index) => { const item = props.context[index]; if (!item) { throw new Error("Unexpected index bound property"); } const { keyItem } = item; const { identifier: targetProperty } = keyItem; if (isKeyItemWithExpression2(keyItem)) { const { expression } = keyItem; const expressionValueRight = expressions.functions.getExpressionValue({ expression, expressionRecord: props.expressions }); return `${targetProperty}: (function(){ const boundValue = ${el} if(boundValue !== null){ return boundValue } else { return ${expressionValueRight} } }())`; } else { return `${targetProperty}: (function(){ const boundValue = ${el} return boundValue }())`; } }); } __name(getBoundValues, "getBoundValues"); function getContextTransition(props) { const stateFromDict = props.stateDictionary.getStateKeys({ states: [props.value] })[0]; if (stateFromDict === null) { throw new Error(`Invalid state - ${props.value}`); } const diagramState = props.diagram.states.find((diagramState2) => { return diagramState2.id === stateFromDict; }); if (!diagramState) { throw new Error(`Invalid state - ${props.value}`); } const ctxRes = []; diagramState.notes?.contextDescription.forEach((ctx) => { const newContext = getContextItem({ ctx, expressions: props.expressions }); ctxRes.push(...newContext); }); if (ctxRes.length === 0) return "prevContext"; return `{${ctxRes.join(",\n ")}}`; } __name(getContextTransition, "getContextTransition"); function getStateToContext(props) { return props.diagram.states.map((state2) => { const stateValue = props.stateDictionary.getStateValues({ keys: [state2.id] })[0]; if (!stateValue) { throw new Error("Invalid state"); } return `${stateValue}: (prevContext, payload, functionDictionary, automata) => { return ${getContextTransition({ value: stateValue, stateDictionary: props.stateDictionary, diagram: props.diagram, expressions: props.expressions })} }`; }); } __name(getStateToContext, "getStateToContext"); function getDefaultContext(props) { const state2 = props.stateDictionary.getStateValues({ keys: [StartState2] })[0]; if (state2) { const ctx = getContextTransition({ diagram: props.diagram, expressions: props.expressions, stateDictionary: props.stateDictionary, value: state2 }); return `const getDefaultContext = (prevContext, payload) => { const ctx = ${ctx} return Object.assign({}, prevContext, ctx); } `; } return `const getDefaultContext = (prevContext, payload) => { return prevContext }`; } __name(getDefaultContext, "getDefaultContext"); var contextSerializer = { getStateReducerCode, getContextItem, mapReducerItems, getBoundValues, getStateToContext, getDefaultContext }; // src/core/modules/JavaScript/JavaScriptCompiler/context/index.ts var context = { serializer: contextSerializer, functions: { getInitialContextShape } }; // src/core/modules/JavaScript/JavaScriptCompiler/state/core.ts function getInitialState(props) { const hasInitial = props.diagram.states.find((state2) => { return Boolean(state2.notes?.initialState); }); if (hasInitial) { return hasInitial.id; } const firstState = props.diagram.states[0]?.id; if (!firstState) { throw new Error("Invalid state"); } return firstState; } __name(getInitialState, "getInitialState"); // src/core/modules/JavaScript/JavaScriptCompiler/state/index.ts var state = { functions: { getInitialState } }; // src/core/modules/JavaScript/JavaScriptCompiler/class/serializer.ts function getClassTemplate(props) { const { diagram } = props; const initialState = state.functions.getInitialState({ diagram }); const stateValue = props.stateDictionary.getStateValues({ keys: [initialState] })[0]; if (stateValue === null) { throw new Error("GetClassTemplate: Invalid state"); } const a = context.functions.getInitialContextShape({ diagram, stateName: StartState3 }); const b = context.functions.getInitialContextShape({ diagram, stateName: initialState }); const initialContext = Object.assign({}, a, b); return replaceFileContents( { "%CLASSNAME%": props.className, "%ID%": `'${props.className}_${Date.now()}'`, "%ACTIONS_MAP%": "actionsMap", "%STATES_MAP%": "statesMap", "%GET_STATE%": props.classSerializer.getGetStateFunc().toString(), "%HAS_STATE%": props.classSerializer.getHasStateFunc({ className: props.className }).toString(), "%GET_ACTION%": props.classSerializer.getGetActionFunc().toString(), "%CREATE_ACTION%": props.classSerializer.getCreateActionFunc({ className: props.className }).toString(), "%STATE%": (stateValue ?? -1).toString(), "%CONTEXT%": JSON.stringify(initialContext), "%REDUCER%": props.classSerializer.getRootReducer().toString(), "%STATE_VALIDATOR%": props.classSerializer.getStateValidator().toString(), "%ACTION_VALIDATOR%": props.classSerializer.getActionValidator().toString(), "%FUNCTION_REGISTRY%": "functionDictionary", "%EVENT_DICTIONARY%": "GlobalEventDictionary", "%IS_KEY_OF%": props.classSerializer.getIsKeyOf().toString() } ); } __name(getClassTemplate, "getClassTemplate"); function getHasStateFunc(props) { return `(instance, state) => instance.state === ${props.className}.getState(state)`; } __name(getHasStateFunc, "getHasStateFunc"); function getGetStateFunc() { return `(state) => statesDictionary[state]`; } __name(getGetStateFunc, "getGetStateFunc"); function getGetActionFunc() { return `(action) => actionsDictionary[action];`; } __name(getGetActionFunc, "getGetActionFunc"); function getCreateActionFunc(props) { return ` (action, payload) => { const actionId = ${props.className}.getAction(action); return { action: actionId, payload, } }`; } __name(getCreateActionFunc, "getCreateActionFunc"); function getActionValidator() { return `(a) => Object.values(actionsDictionary).includes(a)`; } __name(getActionValidator, "getActionValidator"); function getRootReducer() { return `({ action, context, payload, state }) => { if (!action || payload === null) return { state, context }; ${getRootReducerStateValidation()} ${getRootReducerActionValidation()} const getNew = (action,state,context,payload) => { this.lastAction = action; const actionMove = actionToStateFromStateDict[state][action]; const newStateObject = { state: actionMove.state[0] } const contextWithInitial = getDefaultContext(context,payload) ${getRootReducerNewStatePredicateResolution()} const newState = newStateObject.state; const newContextFunc = reducer[newState] if(typeof newContextFunc !== 'function') { throw new Error('Invalid newContextFunc') } return {state:newState, context: newContextFunc(contextWithInitial, payload, this.getFunctionRegistry(), this)}; } let localCtx = getNew(action,state,context,payload) while(byPassedStates.has(localCtx.state)) { localCtx = getNew(actionsDictionary['${ByPassAction}'], localCtx.state, localCtx.context, {}) } this.incrementCycle(); // increment automata local cycle counter incrementEpoch(); // increment global epoch counter return localCtx }`; } __name(getRootReducer, "getRootReducer"); function getRootReducerNewStatePredicateResolution() { return ` if(actionMove.state.length > 1 && actionMove.predicate != null) { // determine new state from predicate const resolvedPredicateValue = actionMove.predicate(contextWithInitial, payload, functionDictionary); if(resolvedPredicateValue == null) return { state, context }; newStateObject.state = resolvedPredicateValue; } `; } __name(getRootReducerNewStatePredicateResolution, "getRootReducerNewStatePredicateResolution"); function getRootReducerStateValidation() { return `${getRootReducerStateValidationHead()} ${getRootReducerStateValidationError()}`; } __name(getRootReducerStateValidation, "getRootReducerStateValidation"); function getRootReducerStateValidationHead() { return `if (!this.isKeyOf(state, actionToStateFromStateDict))`; } __name(getRootReducerStateValidationHead, "getRootReducerStateValidationHead"); function getRootReducerStateValidationError() { return `throw new Error("Invalid state, maybe machine isn't running.")`; } __name(getRootReducerStateValidationError, "getRootReducerStateValidationError"); function getRootReducerActionValidation() { return `if (!this.isKeyOf(action, actionToStateFromStateDict[state])) return { state, context };`; } __name(getRootReducerActionValidation, "getRootReducerActionValidation"); function getStateValidator() { return `(s) => Object.values(statesDictionary).includes(s)`; } __name(getStateValidator, "getStateValidator"); function getIsKeyOf() { return `(key, obj) => key in obj`; } __name(getIsKeyOf, "getIsKeyOf"); var classSerializer = { getClassTemplate, getStateValidator, getHasStateFunc, getGetStateFunc, getGetActionFunc, getCreateActionFunc, getActionValidator, getRootReducer, getRootReducerStateValidation, getRootReducerStateValidationHead, getRootReducerStateValidationError, getRootReducerActionValidation, getIsKeyOf }; // src/core/modules/JavaScript/JavaScriptCompiler/class/index.ts var classModule = { serializer: classSerializer }; // src/core/modules/JavaScript/JavaScriptCompiler/functions/core.ts import { builtInFunctions as builtInFunctions2 } from "@yantrix/functions"; import { ExpressionTypes as ExpressionTypes5 } from "@yantrix/yantrix-parser"; function getFunctionBody2(props) { if (props.expression.expressionType === ExpressionTypes5.Function) { const { FunctionName, Arguments } = props.expression.FunctionDeclaration; const argsList = Arguments.map((arg) => { if (arg.expressionType === ExpressionTypes5.Function) { return getFunctionBody2({ expression: arg, expressions: props.expressions }); } else { return getExpressionValueDefine({ expression: arg, expressions: props.expressions }); } }).join(", "); return `(function() { const func = functionDictionary.get('${FunctionName}'); return func(${argsList}); })()`; } else { return getExpressionValueDefine({ expression: props.expression, expressions: props.expressions }); } } __name(getFunctionBody2, "getFunctionBody"); function checkUserFunctionsDefined(props) { const { injectedPath, injects } = props; const identifiers = injects.map((inject) => `'${inject.identifier}'`); if (!injectedPath) return ``; return ` (function (){ const injects = [${identifiers.join(",")}] const defaults = ${DEFAULT_USER_FUNCTIONS_NAMESPACE}?.default ?? null if(defaults) { switch(typeof defaults) { case 'function': if(!defaults?.name) throw new Error('Default exported user functions must have a name'); Object.assign(${DEFAULT_USER_FUNCTIONS_NAMESPACE}, {[defaults.name]: defaults}); break; case 'object': if(Array.isArray(defaults)) { throw new Error('Default exported user functions must be an object or a function'); } Object.assign(${DEFAULT_USER_FUNCTIONS_NAMESPACE}, {...defaults}); break; default: throw new Error('Default exported user functions must be a function or an object from ${injectedPath}'); } } injects.forEach((identifier) => { if (!${DEFAULT_USER_FUNCTIONS_NAMESPACE}[identifier]) { throw new Error(\`Function \${identifier} is not defined in ${injectedPath}\`); } if(typeof ${DEFAULT_USER_FUNCTIONS_NAMESPACE}[identifier] !== 'function') { throw new Error(\`Function \${identifier} is not a function in ${injectedPath}\`); } }); })()`; } __name(checkUserFunctionsDefined, "checkUserFunctionsDefined"); function registerCustomFunctions(props) { const { dictionaries: dictionaries2, diagram, injectFunctions, dependencyGraph, expressions: expressions2 } = props; const newDictionary = dictionaries2; const defines = diagram.states.flatMap((state2) => state2.notes?.defines ?? []); const inject = diagram.states.flatMap((state2) => state2.notes?.inject ?? []); const registered = /* @__PURE__ */ new Set(); const registerFunction = /* @__PURE__ */ __name((funcName, depth = 0) => { const defineFunction = defines.find((def) => def.identifier === funcName); const injectFunction = inject.find((def) => def.identifier === funcName); const isBuiltInIdentifier = Object.hasOwn(builtInFunctions2, funcName) && (Boolean(defineFunction) || Boolean(injectFunction)); if (registered.has(funcName)) { throw new TypeError(`Function identifier ${funcName} is already used`); } ; if (isBuiltInIdentifier) { throw new TypeError(`Function identifier ${funcName} should not be used as it is a built-in function`); } if (injectFunction && !injectFunctions.path) { throw new TypeError(`Function identifier ${funcName} is defined in the diagram but no inject file is provided`); } const dependencies = dependencyGraph.get(funcName) || /* @__PURE__ */ new Set(); for (const dep of dependencies) { if (!registered.has(dep)) { registerFunction(dep, depth + 1); } } if (injectFunction) { newDictionary.push(`functionDictionary.register('${funcName}', ${DEFAULT_USER_FUNCTIONS_NAMESPACE}['${funcName}']);`); registered.add(funcName); } else if (defineFunction) { const functionBody = getFunctionBody2({ expression: defineFunction.expression, expressions: expressions2 }); newDictionary.push(`functionDictionary.register('${funcName}', function(${defineFunction.Arguments.join(", ")}) { return ${functionBody}; });`); registered.add(funcName); } ; }, "registerFunction"); for (const funcName of dependencyGraph.keys()) { if (!registered.has(funcName)) { registerFunction(funcName); } } return newDictionary; } __name(registerCustomFunctions, "registerCustomFunctions"); // src/core/modules/JavaScript/JavaScriptCompiler/functions/index.ts var functions = { serializer: checkUserFunctionsDefined, functions: { registerCustomFunctions } }; // src/core/modules/JavaScript/JavaScriptCompiler/imports/core.ts import { ExpressionTypes as ExpressionTypes6 } from "@yantrix/yantrix-parser"; function buildDependencyGraph(props) { const defines = props.diagram.states.flatMap((state2) => state2.notes?.defines ?? []); const injects = props.diagram.states.flatMap((state2) => state2.notes?.inject ?? []); const addDependencies = /* @__PURE__ */ __name((expression, currentFunc) => { const { FunctionName, Arguments } = expression.FunctionDeclaration; if (!props.dependencyGraph.has(currentFunc)) { props.dependencyGraph.set(currentFunc, /* @__PURE__ */ new Set()); } props.dependencyGraph.get(currentFunc).add(FunctionName); for (const arg of Arguments) { if (arg.expressionType === "function") { addDependencies(arg, currentFunc); } } }, "addDependencies"); injects.forEach((inject) => { if (!props.dependencyGraph.has(inject.identifier)) { props.dependencyGraph.set(inject.identifier, /* @__PURE__ */ new Set()); } }); for (const define of defines) { if (define.expression.expressionType === ExpressionTypes6.Function) { addDependencies(define.expression, define.identifier); } } return { imports: props.imports, dependencyGraph: props.dependencyGraph }; } __name(buildDependencyGraph, "buildDependencyGraph"); function checkForCyclicDependencies(props) { const cycles = detectCycles({ dependencyGraph: props.dependencyGraph }); if (cycles.length > 0) { const cycleStrings = cycles.map((cycle) => cycle.join(" -> ")); throw new Error(`Cyclic dependencies detected in function definitions: ${cycleStrings.join("\n")}`); } } __name(checkForCyclicDependencies, "checkForCyclicDependencies"); function detectCycles(props) { const visited = /* @__PURE__ */ new Set(); const recursionStack = /* @__PURE__ */ new Set(); const cycles = []; const dfs = /* @__PURE__ */ __name((node, path2 = []) => { if (!visited.has(node)) { visited.add(node); recursionStack.add(node); const neighbors = props.dependencyGraph.get(node) || /* @__PURE__ */ new Set(); for (const neighbor of neighbors) { if (!visited.has(neighbor)) { if (dfs(neighbor, [...path2, node])) { return true; } } else if (recursionStack.has(neighbor)) { cycles.push([...path2, node, neighbor]); return true; } } } recursionStack.delete(node); return false; }, "dfs"); for (const node of props.dependencyGraph.keys()) { if (!visited.has(node)) { dfs(node); } } return cycles; } __name(detectCycles, "detectCycles"); // src/core/modules/JavaScript/JavaScriptCompiler/imports/serializer.ts var getImport = /* @__PURE__ */ __name(({ key, value }) => `import { ${value.join(", ")} } from '${key}'; `, "getImport"); var getNamespaceImport = /* @__PURE__ */ __name(({ key, value }) => `import * as ${value[0]} from '${key}'; `, "getNamespaceImport"); var processRecordImports = /* @__PURE__ */ __name((importsRecord, processImport) => { let imports2 = ""; for (const [key, value] of Object.entries(importsRecord)) { imports2 += processImport({ key, value }); } return imports2; }, "processRecordImports"); function getImportsCode(props) { return processRecordImports(props.imports, getImport); } __name(getImportsCode, "getImportsCode"); function importAll(props) { if (!props.importNamespaces) return ""; return processRecordImports(props.importNamespaces, getNamespaceImport); } __name(importAll, "importAll"); var importsSerializer = { getImportsCode, importAll }; // src/core/modules/JavaScript/JavaScriptCompiler/imports/index.ts var imports = { serializer: { getImportsCode: importsSerializer.getImportsCode, importAll: importsSerializer.importAll }, functions: { checkForCyclicDependencies, buildDependencyGraph } }; // src/core/modules/JavaScript/JavaScriptCompiler/dictionaries/core.ts function getObjectKeysMap(dict) { const obj = {}; Object.keys(dict).forEach((key) => { obj[key] = key; }); return obj; } __name(getObjectKeysMap, "getObjectKeysMap"); function setupDictionaries(props) { let dictionaries2 = []; dictionaries2.push( `export const statesDictionary = ${JSON.stringify(props.stateDictionary.getDictionary(), null, 2)}` ); dictionaries2.push( `export const actionsDictionary = ${JSON.stringify(props.actionDictionary.getDictionary(), null, 2)}` ); if (Object.keys(props.eventDictionary.getDictionary()).length > 0) { dictionaries2.push( `export const eventDictionary = ${JSON.stringify(props.eventDictionary.getDictionary(), null, 2)}` ); dictionaries2.push( `GlobalEventDictionary.addEvents({ keys: Object.keys(eventDictionary).filter(e => GlobalEventDictionary.getEventValues({ keys: [e] })[0] == null) });` ); } dictionaries2.push(`export const functionDictionary = new FunctionDictionary();`); dictionaries2.push(); imports.functions.checkForCyclicDependencies({ dependencyGraph: props.dependencyGraph }); dictionaries2 = functions.functions.registerCustomFunctions({ diagram: props.diagram, expressions: props.expressionRecord, dependencyGraph: props.dependencyGraph, dictionaries: dictionaries2, injectFunctions: props.injectedFunctions }); return dictionaries2; } __name(setupDictionaries, "setupDictionaries"); // src/core/modules/JavaScript/JavaScriptCompiler/dictionaries/serializer.ts import { StartState as StartState4 } from "@yantrix/mermaid-parser"; function getActionToStateDict(props) { const dict = {}; Object.entries(props.transitions).forEach(([key, transition]) => { const newState = props.stateDictionary.getStateValues({ keys: [key] })[0]; transition.actionsPath.forEach(({ action }) => { const actionValue = props.actionDictionary.getActionValues({ keys: action })[0]; if (!actionValue) throw new Error(`Action ${action} not found`); if (!newState) throw new Error(`State ${key} not found`); if (!dict[actionValue]) { dict[actionValue] = []; } if (!dict[actionValue].includes(newState)) { dict[actionValue].push(newState); } }); }); const res = Object.entries(dict).map(([actionId, possibleStates]) => { const predicateString = possibleStates.length > 1 ? `, predicate: predicates[${props.currentState}][${actionId}]` : ""; return ` ${actionId}: { state: [${possibleStates}]${predicateString} } `; }).join(",\n"); return res; } __name(getActionToStateDict, "getActionToStateDict"); function getActionToStateFromStateDict(props) { const actionToStartStateMatrix = {}; Object.entries(props.diagram.transitions).forEach(([state2, transitions]) => { if (state2 === StartState4) { const entries = Object.entries(transitions); entries.forEach(([state3, action]) => { action.actionsPath.forEach(({ action: action2 }) => { actionToStartStateMatrix[state3] = { actionsPath: [{ action: action2, note: [] }] }; }); }); } }); return Object.entries(props.diagram.transitions).map(([currentState, transitions]) => { const transitionsWithStartState = { ...transitions, ...actionToStartStateMatrix }; const value = props.stateDictionary.getStateValues({ keys: [currentState] })[0]; if (!value) throw new Error(`State ${currentState} not found`); return `${value}: {${getActionToStateDict({ currentState: value, transitions: transitionsWithStartState, stateDictionary: props.stateDictionary, actionDictionary: props.actionDictionary }).concat("\n ")}},`; }); } __name(getActionToStateFromStateDict, "getActionToStateFromStateDict"); function getDictionariesCode(props) { return props.dictionaries.join("\n"); } __name(getDictionariesCode, "getDictionariesCode"); function getActionsMap(props) { return `const actionsMap = ${JSON.stringify(getObjectKeysMap(props.actionDictionary.getDictionary()), null, 2)}`; } __name(getActionsMap, "getActionsMap"); function getStatesMap(props) { return `const statesMap = ${JSON.stringify(getObjectKeysMap(props.stateDictionary.getDictionary()), null, 2)}`; } __name(getStatesMap, "getStatesMap"); var getSerializedSetByPassed = /* @__PURE__ */ __name((props) => { return `const byPassedStates = new Set([${props.byPassedList.join(",")}])`; }, "getSerializedSetByPassed"); function getActionToStateFromState(props) { return `const actionToStateFromStateDict = {${props.dictionariesSerializer.getActionToStateFromStateDict({ diagram: props.diagram, stateDictionary: props.stateDictionary, actionDictionary: props.actionDictionary }).join("\n ")}}`; } __name(getActionToStateFromState, "getActionToStateFromState"); function getAutomataEpochCounterCode() { const lines = []; lines.push(`const epoch = { val: 1 };`); lines.push(`const incrementEpoch = () => { epoch.val++ };`); lines.push(`const getEpoch = () => epoch.val;`); return lines.join("\n"); } __name(getAutomataEpochCounterCode, "getAutomataEpochCounterCode"); function getAutomataInternalsRegisterCode(props) { const { class