0.1.1 • Published 1 year ago

@flowgix/core v0.1.1

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

@flowgix/core

A powerful and flexible core library for the Flowgix plugin system, written in F# and compiled to JavaScript using Fable. This library provides the foundation for building node-based visual programming environments with a robust plugin architecture.

Features

  • Plugin System: A robust plugin architecture that allows for easy extension of functionality
    • Dynamic plugin registration and unregistration
    • Plugin lifecycle management (initialization and cleanup)
    • Custom plugin configurations and engines
  • Node Graph Engine: A powerful engine for evaluating node graphs with support for:
    • Custom node types with type-safe inputs and outputs
    • Port types with automatic type conversion
    • Control system for node configuration
    • Circular dependency detection
    • Caching for performance optimization
  • Type Safety: Full TypeScript support with comprehensive type definitions
    • Strong typing for all core components
    • Type-safe plugin development
    • Runtime type checking and validation
  • Extensibility: Easy to extend with custom node types, port types, and plugins
    • Flexible plugin API
    • Custom resolvers for ports and nodes
    • Support for custom renderers and controls

Installation

NPM (JavaScript/TypeScript)

npm install @flowgix/core

NuGet (.NET/C#/F#)

# Using the .NET CLI
dotnet add package Flowgix.Core

# Using the Package Manager Console
Install-Package Flowgix.Core

Package References

C# (.csproj)

<PackageReference Include="Flowgix.Core" Version="0.1.0" />

F# (.fsproj)

<PackageReference Include="Flowgix.Core" Version="0.1.0" />

Prerequisites

For development:

Development Setup

  1. Install Fable as a local tool:
dotnet new tool-manifest
dotnet tool install fable
  1. Install npm dependencies:
npm install

Building

One-time Build

npm run build

Watch Mode (for development)

npm run watch

Testing

Run Tests Once

npm test

Watch Mode for Tests

npm run test:watch

Project Structure

flowgix-core/
├── src/
│   ├── Types.fs        # Core type definitions
│   ├── Plugin.fs       # Plugin system implementation
│   ├── Engine.fs       # Node graph engine
│   ├── index.ts        # TypeScript declarations
│   └── example.ts      # Usage example
├── tests/              # Test files
├── dist/              # Compiled output
├── package.json       # npm package configuration
├── tsconfig.json      # TypeScript configuration
├── babel.config.js    # Babel configuration
└── jest.config.cjs    # Jest configuration

Usage

TS Example

import { IFlowgixEngine, IPluginManager, type NodeType, type PortType } from 'flowgix-core';

// Define port types
const numberPort: PortType = {
    name: 'number',
    type: 'number',
    resolver: (type, inputData, value) => Number(value)
};

// Define node types
const addNumbers: NodeType = {
    id: 'add-numbers',
    name: 'Add Numbers',
    description: 'Adds two numbers together',
    category: 'Math',
    inputs: ['a', 'b'],
    outputs: ['result'],
    controls: [],
    resolver: (node, inputData, nodeType, context) => {
        const a = Number(inputData['a']);
        const b = Number(inputData['b']);
        return { result: a + b };
    }
};

// Create a plugin
const mathPlugin = {
    id: 'math-plugin',
    name: 'Math Plugin',
    description: 'Basic mathematical operations',
    nodeTypes: [addNumbers],
    portTypes: [numberPort]
};

// Initialize the plugin manager
const pluginManager: IPluginManager = {
    registerPlugin: (registration) => { /* ... */ },
    unregisterPlugin: (pluginId) => { /* ... */ },
    getPluginConfig: (pluginId) => { /* ... */ },
    getPluginEngine: (pluginId) => { /* ... */ },
    getPlugins: () => { /* ... */ },
    onInit: () => { /* ... */ },
    onCleanup: () => { /* ... */ }
};

// Create and evaluate a graph
const engine: IFlowgixEngine = {
    createConfig: (nodeTypes, portTypes, controls) => { /* ... */ },
    createEngine: (config, portResolver, nodeResolver) => { /* ... */ },
    evaluateGraph: (engine, nodes) => { /* ... */ }
};

const config = engine.createConfig(
    [addNumbers],
    [numberPort],
    []
);

const rootEngine = engine.createEngine(
    config,
    (type, inputData, value) => Number(value),
    (node, inputData, nodeType, context) => {
        const resolver = nodeType.resolver;
        if (resolver) {
            return resolver(node, inputData, nodeType, context);
        }
        return {};
    }
);

const nodes = new Map([
    ['node1', {
        id: 'node1',
        type: 'add-numbers',
        root: true,
        inputData: { a: 5, b: 3 },
        connections: {}
    }]
]);

const result = engine.evaluateGraph(rootEngine, nodes);
console.log(result); // { result: 8 }

F# Example

open Flowgix.Core

// Define port types
let numberPort = {
    name = "number"
    type = "number"
    resolver = fun type inputData value -> 
        match value with
        | :? string as s -> float s
        | :? float as f -> f
        | _ -> 0.0
}

// Define node types
let addNumbers = {
    id = "add-numbers"
    name = "Add Numbers"
    description = "Adds two numbers together"
    category = "Math"
    inputs = ["a"; "b"]
    outputs = ["result"]
    controls = []
    resolver = fun node inputData nodeType context ->
        let a = float inputData["a"]
        let b = float inputData["b"]
        Map [("result", a + b)]
}

// Create a plugin
let mathPlugin = {
    id = "math-plugin"
    name = "Math Plugin"
    description = "Basic mathematical operations"
    nodeTypes = [addNumbers]
    portTypes = [numberPort]
}

// Initialize the plugin manager
let pluginManager = {
    registerPlugin = fun registration -> ()
    unregisterPlugin = fun pluginId -> ()
    getPluginConfig = fun pluginId -> Some mathPlugin
    getPluginEngine = fun pluginId -> None
    getPlugins = fun () -> [mathPlugin]
    onInit = fun () -> ()
    onCleanup = fun () -> ()
}

// Create and evaluate a graph
let engine = {
    createConfig = fun nodeTypes portTypes controls -> 
        { nodeTypes = nodeTypes; portTypes = portTypes; controls = controls }
    createEngine = fun config portResolver nodeResolver -> 
        { config = config; portResolver = portResolver; nodeResolver = nodeResolver }
    evaluateGraph = fun engine nodes -> 
        let node = Map.find "node1" nodes
        let result = engine.nodeResolver node node.inputData addNumbers Map.empty
        result
}

let config = engine.createConfig [addNumbers] [numberPort] []

let rootEngine = engine.createEngine(
    config,
    (fun type inputData value -> float value),
    (fun node inputData nodeType context ->
        match nodeType.resolver with
        | Some resolver -> resolver node inputData nodeType context
        | None -> Map.empty)
)

let nodes = Map [
    ("node1", {
        id = "node1"
        type = "add-numbers"
        root = true
        inputData = Map [("a", 5.0); ("b", 3.0)]
        connections = Map.empty
    })
]

let result = engine.evaluateGraph(rootEngine, nodes)
printfn "%A" result // Map [("result", 8.0)]

C# Example

using Flowgix.Core;

// Define port types
var numberPort = new PortType
{
    Name = "number",
    Type = "number",
    Resolver = (type, inputData, value) => Convert.ToDouble(value)
};

// Define node types
var addNumbers = new NodeType
{
    Id = "add-numbers",
    Name = "Add Numbers",
    Description = "Adds two numbers together",
    Category = "Math",
    Inputs = new[] { "a", "b" },
    Outputs = new[] { "result" },
    Controls = Array.Empty<ControlType>(),
    Resolver = (node, inputData, nodeType, context) =>
    {
        var a = Convert.ToDouble(inputData["a"]);
        var b = Convert.ToDouble(inputData["b"]);
        return new Dictionary<string, object> { { "result", a + b } };
    }
};

// Create a plugin
var mathPlugin = new FlowgixPlugin
{
    Id = "math-plugin",
    Name = "Math Plugin",
    Description = "Basic mathematical operations",
    NodeTypes = new[] { addNumbers },
    PortTypes = new[] { numberPort }
};

// Initialize the plugin manager
var pluginManager = new PluginManager
{
    RegisterPlugin = registration => { },
    UnregisterPlugin = pluginId => { },
    GetPluginConfig = pluginId => mathPlugin,
    GetPluginEngine = pluginId => null,
    GetPlugins = () => new[] { mathPlugin },
    OnInit = () => { },
    OnCleanup = () => { }
};

// Create and evaluate a graph
var engine = new FlowgixEngine
{
    CreateConfig = (nodeTypes, portTypes, controls) =>
        new FlowgixConfig { NodeTypes = nodeTypes, PortTypes = portTypes, Controls = controls },
    CreateEngine = (config, portResolver, nodeResolver) =>
        new FlowgixRootEngine { Config = config, PortResolver = portResolver, NodeResolver = nodeResolver },
    EvaluateGraph = (engine, nodes) =>
    {
        var node = nodes["node1"];
        var result = engine.NodeResolver(node, node.InputData, addNumbers, new Dictionary<string, object>());
        return result;
    }
};

var config = engine.CreateConfig(new[] { addNumbers }, new[] { numberPort }, Array.Empty<Control>());

var rootEngine = engine.CreateEngine(
    config,
    (type, inputData, value) => Convert.ToDouble(value),
    (node, inputData, nodeType, context) =>
    {
        if (nodeType.Resolver != null)
        {
            return nodeType.Resolver(node, inputData, nodeType, context);
        }
        return new Dictionary<string, object>();
    }
);

var nodes = new Dictionary<string, FlowgixNode>
{
    ["node1"] = new FlowgixNode
    {
        Id = "node1",
        Type = "add-numbers",
        Root = true,
        InputData = new Dictionary<string, object> { { "a", 5.0 }, { "b", 3.0 } },
        Connections = new Dictionary<string, object>()
    }
};

var result = engine.EvaluateGraph(rootEngine, nodes);
Console.WriteLine(string.Join(", ", result.Select(kvp => $"{kvp.Key}: {kvp.Value}"))); // result: 8

Core Components

Control Types

The library supports various control types for node configuration:

  • Text
  • Number
  • Checkbox
  • Select
  • MultiSelect
  • Custom
  • Boolean
  • Object

Port Types

Built-in port types include:

  • NumberPort
  • TextPort
  • BooleanPort
  • OutcomeActionCollectionPort
  • LLMAgentPort
  • CustomPort (for custom implementations)

Node Types

Core node types include:

  • AddNumbers
  • FlowgixResults
  • OutcomeAttributes
  • DivideNumbers
  • CustomNode (for custom implementations)

API Reference

Core Types

ControlType

interface ControlType {
    type: string;
    label: string;
    name: string;
    portName: string;
    defaultValue: any;
}

PortType

interface PortType {
    name: string;
    type: string;
    resolver?: (type: string, inputData: any, value: any) => any;
}

NodeType

interface NodeType {
    id: string;
    name: string;
    description: string;
    category: string;
    inputs: string[];
    outputs: string[];
    controls: ControlType[];
    resolver?: (node: any, inputData: any, nodeType: NodeType, context: any) => Record<string, any>;
}

Plugin System

IPluginManager

interface IPluginManager {
    registerPlugin(registration: PluginRegistration): void;
    unregisterPlugin(pluginId: string): void;
    getPluginConfig(pluginId: string): FlowgixPlugin | undefined;
    getPluginEngine(pluginId: string): FlowgixRootEngine | undefined;
    getPlugins(): FlowgixPlugin[];
    onInit(): void;
    onCleanup(): void;
}

Engine

IFlowgixEngine

interface IFlowgixEngine {
    createConfig(
        nodeTypes: NodeType[],
        portTypes: PortType[],
        controls: Control[]
    ): FlowgixConfig;

    createEngine(
        config: FlowgixConfig,
        portResolver: (type: string, inputData: any, value: any) => any,
        nodeResolver: (node: FlowgixNode, inputData: any, nodeType: NodeType, context: any) => Record<string, any>
    ): FlowgixRootEngine;

    evaluateGraph(engine: FlowgixRootEngine, nodes: Map<string, FlowgixNode>): Record<string, any>;
}

Publishing

To publish this package to npm:

  1. Build the package:
npm run build
  1. Test the package:
npm test
  1. Update the version in package.json:
npm version patch  # for bug fixes
npm version minor  # for new features
npm version major  # for breaking changes
  1. Login to npm (if not already logged in):
npm login
  1. Publish the package:
npm publish

For scoped packages (e.g., @your-org/flowgix-core):

npm publish --access public

Pre-publish Checklist

  • All tests pass
  • Build is successful
  • Version is updated
  • README is up to date
  • No sensitive data in the package
  • All necessary files are included
  • package.json has correct metadata

Contributing

  1. Fork the repository at github.com/flowgix/flowgix-core
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

For support, please open an issue in the GitHub repository.

0.1.1

1 year ago

0.2.0

1 year ago