npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@d-flow/engine

v0.2.0

Published

A storage agnostic workflow engine in continous development

Downloads

3

Readme

D-Flow

A storage agnostic data workflow engine. It takes care of maintaining a valid state for your multi-step processes.

Install it via NPM

npm i @d-flow/engine

In order to run the engine its first necessary to define the set of steps and transitions that your data will go through on each submission, this set is maintained within a FlowDefinition.

Data Step Definition

A Data Step Definition is the entity that will encapsulate your data during a specific state. It can include a set of Fields to give it appropiated format. This fields have a specific data type and can be marked as required in order for the submission to be valid. The validation is performed by a Processor that can be specified per step. If you do not define any fields for your step then the same data that comes in during the submission will be forwarded as output.

At the moment the only supported data type is JSON or Javascript Object. The validation for this data type is provided by the JSON Data Processor.

Transition

A transition is a connection between your step definitions. It can have conditions and/or require that other steps are completed before being able to complete itself.

Examples

A simple case

A simple flow

const { createDataInputStep, createFlowDefinition, createTransition, createFieldDefinition, FlowStatus } = require('@d-flow/engine');
const simpleFlow = createFlowDefinition("simple", "simple")
    .setStartStep(createDataInputStep("start", "Start Step").addField(createFieldDefinition("sa", "string", "Start A")))
    .addStep(createDataInputStep("a", "Step A").addField(createFieldDefinition("fa", "number", "Field A")))
    .addStep(createDataInputStep("b", "Step B").addField(createFieldDefinition("fb", "string", "Field B")))
    .addTransition(createTransition("start", "a"))
    .addTransition(createTransition("a", "b"))
    .setStatusOnCompletedStep("b", FlowStatus.Completed);

Submissions to the Engine

Once your workflow is defined you can start to submit data.

Each submission will be validated by the engine, specifically it will verify:

  • That the step that you want to reach is defined for the flow.
  • That the step that you want to reach is within the range.
  • If there are conditions for the transition they must be satisfied.
  • If there are other steps marked as requirements that they are completed as well (specificaly useful on parallel branches).
  • That the required fields defined for your step (if any) are completed.
const { Engine } = require('@d-flow/engine');
let flow = Engine.create(simpleFlow);
flow = Engine.submit(flow, {sa: "Hello"}, "start");
flow = Engine.submit(flow, {fa: "There"}, "a");
flow = Engine.submit(flow, {fb: "Flow"}, "b");

The submit function returns the modified flow instance for reuse.

The Flow Object

The Flow object holds different information relevant to the process such as:

  • createdAt: creation Date for the flow.
  • lastUpdatedAt: last update Date for the flow.
  • currentStep: hols a reference to the last accepted step.
  • steps: an array containing all the visited steps.
  • cycleCount: a counter that increases when a cycle is detected.
  • status: a value that indicates the current flow status (see below).

You can invoke the function toObject() on a flow instance to obtain an object representation.

The Flow Status

A flow can hold different single statuses during its execution such as:

  • Created: the initial status, obtained when calling Engine.create().
  • Active: indicates that a flow can still receive step submissions.
  • Completed: indicates that a flow has arrived to a final controlled status and it's no longer able to receive step submissions.
  • Cancelled: indicates that a flow has arrived to a final non successful status.
  • Error: indicates an exceptional status.

Changing a status

Created and Active are currently the only statuses managed by the Engine. In order to apply a different status to your Flow it is necessary to indicate it as part of the flow definition using the function setStatusOnCompletedStep() as indicated above

const simpleFlow = createFlowDefinition("simple", "simple")
	[...]
    .setStatusOnCompletedStep("b", FlowStatus.Completed);

This indicates that when the step "b" defined for the flow is submited and accepted the flow status will change to Completed.

More FlowDefinition Examples

Conditional Flows

A transition can also have a condition, which is a data value to be matched against.

A conditional flow

const { createDataInputStep, createFlowDefinition, createTransition, createFieldDefinition, ObjectConditions} = require('@d-flow/engine');
const decisionFlow = createFlowDefinition("decision", "decision")
    .setStartStep(createDataInputStep("start", "Start Decision"))
    .addStep(createDataInputStep("a", "Step A").addField(createFieldDefinition("fa", "number", "Field A")))
    .addStep(createDataInputStep("b1", "Step B1"))
    .addStep(createDataInputStep("b2", "Step B2"))
    .addTransition(createTransition("start", "a"))
    .addTransition(createTransition("a", "b1").setCondition(ObjectConditions.equals("fa", 10)))
    .addTransition(createTransition("a", "b2").setCondition(ObjectConditions.equals("fa", 20)))
    ;

If you want to define your own condition evaluators you can just implement the interface

function satisfies(data: any): boolean;

And pass this function into .setCondition()

Parallel Flows

A transition can also require other steps to be completed in order to advance.

A parallel flow

const { createDataInputStep, createFlowDefinition, createTransition, Requirements} = require('@d-flow/engine');

const parallelFlow = createFlowDefinition("parallel", "parallel")
    .setStartStep(createDataInputStep("start", "Start Parallel"))
    .addStep(createDataInputStep("a1", "A1"))
    .addStep(createDataInputStep("a2", "A2"))
    .addStep(createDataInputStep("a3", "A3"))
    .addStep(createDataInputStep("b", "B"))
    .addTransition(createTransition("start", "a1"))
    .addTransition(createTransition("start", "a2"))
    .addTransition(createTransition("start", "a3"))
    .addTransition(createTransition("a1", "b").setRequirements([Requirements.requiresAll(["a2", "a3"])]))
    .addTransition(createTransition("a2", "b").setRequirements([Requirements.requiresAll(["a1", "a3"])]))
    .addTransition(createTransition("a3", "b").setRequirements([Requirements.requiresAll(["a2", "a1"])]))
    ;

Parallel with condition flow

A combination of a parallel flow that includes conditionals.

A conditional flow

const { createDataInputStep, createFlowDefinition, createTransition, createFieldDefinition, ObjectConditions, Requirements} = require('@d-flow/engine');

const parallelFlowWithDecision = createFlowDefinition("parallel", "parallel")
    .setStartStep(createDataInputStep("start", "Start Parallel"))
    .addStep(createDataInputStep("a", "A").addField(createFieldDefinition("fa", "number", "Field A")))
    .addStep(createDataInputStep("a1", "A1"))
    .addStep(createDataInputStep("a2", "A2"))
    .addStep(createDataInputStep("b", "B"))
    .addStep(createDataInputStep("c", "C"))
    .addStep(createDataInputStep("d", "D"))
    .addTransition(createTransition("start", "a"))
    .addTransition(createTransition("a", "a1").setCondition(ObjectConditions.equals("fa", 10)))
    .addTransition(createTransition("a", "a2").setCondition(ObjectConditions.equals("fa", 20)))
    .addTransition(createTransition("start", "b"))
    .addTransition(createTransition("start", "c"))
    .addTransition(createTransition("a1", "d").setRequirements([Requirements.requiresAll(["b", "c"])]))
    .addTransition(createTransition("a2", "d").setRequirements([Requirements.requiresAll(["b", "c"])]))
    .addTransition(createTransition("b", "d").setRequirements([Requirements.requiresAny(["a2", "a1"]), Requirements.requiresAll(["c"])]))
    .addTransition(createTransition("c", "d").setRequirements([Requirements.requiresAny(["a2", "a1"]), Requirements.requiresAll(["b"])]))
    ;

Cycles

Every time you run into a cycle an internal cycle count for the flow gets increased.

A cyclic flow

const { createDataInputStep, createFlowDefinition, createTransition } = require('@d-flow/engine');
const cyclicFlow = createFlowDefinition("cyclic", "cyclic")
    .setStartStep(createDataInputStep("start", "Start Cyclic"))
    .addStep(createDataInputStep("a", "A"))
    .addTransition(createTransition("start", "a"))
    .addTransition(createTransition("a", "start"));