JDM Editor

repository·master·Indexed 43 days ago

https://github.com/gorules/jdm-editor

An open-source React component library for creating and managing JSON Decision Model (JDM) files through visual interfaces like Decision Graphs and Decision Tables. Version 1.52.0 includes components such as DecisionGraph and JdmConfigProvider, as well as integration with ZEN Engine WASM for expression validation, linting, and type checking.

Tokens
18.2K
Snippets
61
Records
79
Agent score
55%

What's inside JDM Editor

  1. Basic usage of DecisionGraph and JdmConfigProvider

    master

    To implement the JDM Editor, you must wrap your components in a JdmConfigProvider. The primary component for visual modeling is DecisionGraph. You should also import the required CSS styles for the editor to render correctly.

    import '@gorules/jdm-editor/dist/style.css';
    import { DecisionGraph, JdmConfigProvider } from '@gorules/jdm-editor';
    
    // ... inside your component
    <JdmConfigProvider>
      <DecisionGraph
        value={graph}
        onChange={(val) => setGraph(val as any)}
      />
    </JdmConfigProvider>
  2. Self-host Monaco Editor in Vite

    master

    If you are using Vite and need to self-host the Monaco Editor (used by the JDM Editor), you must configure the MonacoEnvironment to handle workers for different languages like JSON, CSS, HTML, and TypeScript. This ensures that language features like syntax highlighting and IntelliSense work correctly in your environment.

    import type { Monaco } from '@monaco-editor/react';
    import { loader } from '@monaco-editor/react';
    import * as monaco from 'monaco-editor';
    import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
    import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
    import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
    import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
    import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
    
    declare global {
      interface Window {
        monaco?: Monaco;
      }
    }
    
    self.monaco = monaco;
    
    self.MonacoEnvironment = {
      getWorker(workerId: string, label: string): Promise<Worker> | Worker {
        if (label === 'json') {
          return new jsonWorker();
        }
        if (label === 'css' || label === 'scss' || label === 'less') {
          return new cssWorker();
        }
        if (label === 'html' || label === 'handlebars' || label === 'razor') {
          return new htmlWorker();
        }
        if (label === 'typescript' || label === 'javascript') {
          return new tsWorker();
        }
    
        return new editorWorker();
      },
    };
    
    loader.config({ monaco });
  3. Structure of a Decision Model

    master

    A complete decision model is defined by the decisionModelSchema, which consists of an array of nodes and an array of edges.

    Each node must include common properties: id, name, and position (x and y coordinates). Edges connect nodes using sourceId and targetId.

    const model = {
      nodes: [
        {
          id: 'node-1',
          type: 'inputNode',
          name: 'My Input',
          position: { x: 100, y: 100 },
          content: { schema: '...' }
        }
      ],
      edges: [
        {
          id: 'edge-1',
          sourceId: 'node-1',
          targetId: 'node-2',
          type: 'edge'
        }
      ]
    };
  4. Define node inputs using InputSchema

    master

    When using createJdmNode, you can define a schema for the node's configuration data using the inputs array. This schema determines the shape of the config object within the node's content and dictates which UI controls are rendered in the node's settings form.

    Each input in the inputs array follows the InputSchema type:

    PropertyTypeDescription
    namestringThe key in the configuration object. Supports dot notation for nested objects (e.g., user.name).
    control'text' | 'bool'The UI control to use. 'text' uses a CodeEditor; 'bool' uses a Checkbox.
    labelstring (optional)The label displayed in the form.

    Nested Objects: If you use dot notation in the name (e.g., settings.threshold), the createJdmNode utility will automatically infer a nested object structure for the node's data type.

    // Example of defining inputs that create a nested config object
    const inputs = [
      { name: 'metadata.title', control: 'text', label: 'Title' },
      { name: 'metadata.active', control: 'bool', label: 'Is Active' }
    ] as const;
    
    // The resulting config object will be: 
    // { metadata: { title: string, active: boolean } }
  5. Integrate the DecisionTableProvider into your application

    master

    To use the Decision Table editor components and state management, wrap your component tree with the DecisionTableProvider. This provider initializes the state, listeners, and actions required for managing decision table data, columns, and rules.

    import { DecisionTableProvider } from '@gorules/jdm-editor/components/decision-table/context/dt-store.context';
    
    function App() {
      return (
        <DecisionTableProvider>
          <YourDecisionTableEditor />
        </DecisionTableProvider>
      );
    }
  6. Configure the DecisionTable component

    master

    The DecisionTable component allows for tabular decision modeling. Use these props to manage data, schema, and rendering.

    export type DecisionTableProps = {
      id?: string;
      defaultValue?: DecisionTableType;
      value?: DecisionTableType;
      onChange?: (decisionTable: DecisionTableType) => void;
      activeRules?: string[];
      permission?: DecisionTablePermission;
      disabled?: boolean;
      disableHitPolicy?: boolean;
      minColWidth?: number;
      colWidth?: number;
      inputsSchema?: SchemaSelectProps[];
      outputsSchema?: SchemaSelectProps[];
      cellRenderer?: (props: CellProps) => JSX.Element | null | undefined;
    };
  7. Configure the DecisionGraph component

    master

    The DecisionGraph component is used to design decision models visually. Use the following props to control its behavior and appearance.

    export type DecisionGraphProps = {
      id?: string;
      forwardedRef?: (instance: DecisionGraphRef) => void;
      defaultValue?: DecisionGraphType;
      value?: DecisionGraphType;
      disabled?: boolean;
      viewConfig?: ViewConfig;
      components?: CustomNodeType[];
      onChange?: (val: DecisionGraphType) => void;
      manager?: DragDropManager;
      reactFlowProOptions?: ProOptions;
      onReactFlowInit?: () => void;
    };
  8. Validate ZEN expressions with validateZenExpression()

    master

    Use validateZenExpression to perform manual linting and type checking on ZEN expressions. This function returns an array of CodeMirror Diagnostic objects. It checks for syntax errors (via the WASM engine) and validates the expression against provided types and expected variable types.

    Key Behaviors:

    • Syntax Errors: Detects parserError, lexerError, compilerError, and vmError.
    • Type Checking: Validates the expression against a list of ZenType objects. If a type error is found, it maps the error message to a warning, info, or hint based on the error string prefix.
    • Unary Expressions: If expressionType is set to 'unary', it ensures the result evaluates to a Bool type. If not, it reports a mismatch.
    • Variable Type Matching: If expectedVariableType is provided, it checks if the expression's result satisfies that type. In strict mode, matching 'Any' against specific types is disallowed.
    • Severity: In strict mode, type mismatches are reported as error; otherwise, they are reported as warning.

    Note: This function requires the ZEN Engine WASM to be available to perform syntax validation.

    import { validateZenExpression } from '@gorules/jdm-editor';
    
    const diagnostics = validateZenExpression({
      source: 'my_expression',
      expressionType: 'standard',
      strict: true,
      types: [...],
      expectedVariableType: ...
    });