Skip to content

arseem/ivy-flow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ivy Flow logo

ivy-flow

License: MIT

A database-agnostic, type-safe engine for executing and managing production-grade decision tree based surveys, diagnostics, and guided user journeys.

ivy-flow provides a predictable state machine for graph traversal and a strict versioning contract for managing survey lifecycles. It is designed to be the pure domain layer of your application—completely independent of any specific framework, UI library, or database.

Ecosystem

  • ivy-flow-studio: The official visual editor and admin UI for constructing ivy-flow decision graphs.

Features

  • Deterministic Traversal: Explicit state transitions for navigating complex decision graphs.
  • Framework & Database Agnostic: Bring your own database (Prisma, Drizzle, etc.) via simple repository contracts.
  • Draft & Publish Lifecycle: Built-in versioning service to safely manage survey revisions.
  • Strict Validation: Schema-level and reference-level integrity checks prevent broken graphs from being published.
  • Type-Safe: First-class TypeScript support for graph definitions and runtime state.
  • Built-in Adapters: Includes JSON and Prisma repository adapters that implement the same domain contract.

Installation

Quick Start

ivy-flow requires a repository adapter to manage graph persistence. Out of the box, it includes a read-only JSON string adapter for rapid prototyping.

import {
  createSurveyState,
  createSurveyDefinitionService,
  createJsonSurveyDefinitionRepository,
  validateGraph,
} from "ivy-flow";

// 1. Initialize the storage adapter
const repository = createJsonSurveyDefinitionRepository({
  graphJson: JSON.stringify({
    metadata: {
      title: "Main Survey",
      version: "1.0.0",
      description: "Survey loaded from JSON string",
      locale: "en",
    },
    startNode: "start",
    nodes: {
      start: { type: "start", label: "Start", next: "stop" },
      stop: { type: "stop", title: "Done", description: "Survey complete." },
    },
  }),
  sourceLabel: "bootstrap-graph",
  definitionKey: "main",
  definitionName: "Main Survey",
});

// 2. Initialize the versioning service
const service = createSurveyDefinitionService({
  repository,
  defaultSurveyKey: "main",
  defaultSurveyName: "Main Survey",
  systemEditorIdentifier: "system@local",
  validateGraph,
});

// 3. Fetch the active graph and initialize the runtime state
const activeGraph = await service.getActiveSurveyGraph();
const initialState = createSurveyState(activeGraph);

JSON adapter notes

  • createJsonSurveyDefinitionRepository(...) is read-only by design.
  • It validates schema and references before exposing graph data.
  • Admin write operations (createDraftRevision, publishDraftAndArchiveOthers, etc.) intentionally throw unsupported-operation errors.
  • Use Prisma adapter for full draft and publish workflows.

Prisma Adapter

ivy-flow includes a Prisma repository adapter via createPrismaSurveyDefinitionRepository(...).

The adapter expects mapped Prisma-like delegates, so you can use:

  • direct Prisma client,
  • wrapped data-layer objects,
  • custom delegate mappings.

Direct Prisma client example

import { SurveyRevisionStatus } from "@prisma/client";
import {
  createPrismaSurveyDefinitionRepository,
  createSurveyDefinitionService,
  validateGraph,
} from "ivy-flow";

const repository = createPrismaSurveyDefinitionRepository({
  db: prisma,
  statusMap: {
    draft: SurveyRevisionStatus.DRAFT,
    published: SurveyRevisionStatus.PUBLISHED,
    archived: SurveyRevisionStatus.ARCHIVED,
  },
});

const service = createSurveyDefinitionService({
  repository,
  defaultSurveyKey: "main",
  defaultSurveyName: "Main Survey",
  systemEditorIdentifier: "system@local",
  validateGraph,
});

Wrapped delegate mapping example

import { createPrismaSurveyDefinitionRepository } from "ivy-flow";

const repository = createPrismaSurveyDefinitionRepository({
  db: {
    surveyDefinition: dataLayer.surveys.definition,
    surveyDefinitionRevision: dataLayer.surveys.revision,
    $transaction: dataLayer.$transaction,
  },
  statusMap: {
    draft: "DRAFT",
    published: "PUBLISHED",
    archived: "ARCHIVED",
  },
});

Prisma adapter contract requirements

  • db.surveyDefinition delegate with findUnique, update, create.
  • db.surveyDefinitionRevision delegate with findFirst, findMany, create, update, updateMany.
  • db.$transaction function.
  • Revision status mapping via statusMap.

The adapter returns the standard SurveyDefinitionRepository, so it plugs directly into createSurveyDefinitionService(...).

API Reference

Runtime Engine

The runtime engine handles active user progression through a published graph.

Method Description
createSurveyState(graph) Initializes state at graph.startNode.
getCurrentNode(state, graph) Resolves the active node object from the current state.
selectOption(state, graph, index) Applies a selection on a decision node and advances history.
advanceNode(state, graph) Moves forward from start or info nodes via the next reference.
goBack(state) Reverts one history step.
isTerminal(state, graph) Returns true if the current node is a result or stop node.
getResult(state, graph) Returns the normalized result payload on terminal nodes.
getProgress(state, graph) Estimates progress as a percentage [0, 100].
validateGraph(graph) Performs structural and referential validation, returning an array of errors.

Versioning Service

The versioning service manages the administrative lifecycle (drafting, validating, and publishing) of survey definitions.

Method Description
getActiveSurveyGraph() Returns the current active, published revision.
getSurveyAdminData() Returns the admin payload, including draft-preferred graph and revision history.
upsertDraftSurveyRevision(input) Updates the latest draft or creates the next sequential draft version.
publishDraftSurveyRevision(input) Validates and promotes a draft to active, archiving previous revisions.

Graph Schema Requirements

Graphs must strictly adhere to the DecisionGraph type. validateGraph() enforces reference integrity to ensure no orphaned nodes or dead ends exist.

Supported node types:

  • start: Entry point. Requires label and next.
  • decision: Branching point. Requires question and an array of options (each with label and next).
  • info: Interstitial content. Requires content.
  • result: Terminal node yielding an outcome. Requires title, description, and category.
  • stop: Terminal node for dead-ends. Requires title and description.
  • helper: Supplementary content nodes attached to decisions.

License

MIT

About

A database-agnostic, type-safe engine for executing and managing production-grade decision tree based surveys, diagnostics, and guided user journeys.

Topics

Resources

License

Stars

Watchers

Forks

Contributors