@yantrix/codegen
Version:
Yantrix framework code generator API
1,411 lines (1,337 loc) • 97.4 kB
JavaScript
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