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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sinomoe/workflow-kit

v0.1.1

Published

A production-oriented React workflow editor kit with typed nodes, validation, history, and host-owned UI.

Downloads

35

Readme

Workflow Kit

A React workflow composition kit built on React Flow 12.

Workflow Kit provides the editor runtime, graph model, node contracts, validation helpers, history, persistence hooks, and React Flow integration needed to build a host-owned workflow builder. Product teams keep control of chrome, node UI, copy, publishing flows, and business validation.

Status

This package is in early extraction. The public API is usable for local experiments, but the project is still hardening toward a stable open-source release.

Install

npm install @sinomoe/workflow-kit react react-dom @xyflow/react zustand

Import the library styles once and render the composer inside a sized container:

import '@sinomoe/workflow-kit/style.css'

export function Page() {
  return (
    <div style={{ height: '100vh' }}>
      <Builder />
    </div>
  )
}

Quick Start

import {
  Composer,
  defineNode,
  type Graph,
} from '@sinomoe/workflow-kit'
import '@sinomoe/workflow-kit/style.css'

const startNode = defineNode({
  type: 'start',
  defaultData: { triggerMode: 'chat' },
  createTitle: () => 'Start',
  getPorts: () => ({
    outputs: [{ id: 'out', label: 'Start' }],
  }),
})

const answerNode = defineNode({
  type: 'answer',
  defaultData: { text: '' },
  createTitle: count => `Answer ${count}`,
  getPorts: () => ({
    inputs: [{ id: 'in', label: 'Input' }],
  }),
  renderInspector: ({ node, readOnly, actions }) => (
    <textarea
      value={String(node.data.text ?? '')}
      disabled={readOnly}
      onChange={event => actions.updateNodeData({ text: event.target.value })}
    />
  ),
})

const initialGraph: Graph = {
  nodes: [],
  edges: [],
}

export function Builder() {
  return (
    <Composer
      registry={[startNode, answerNode]}
      defaultValue={initialGraph}
      onChange={(graph, meta) => {
        console.log(graph)
        console.log(meta.action)
      }}
      autoSave={{ delayMs: 750 }}
      onSaveDraft={async (graph) => {
        await saveDraft(graph)
      }}
      onPublish={async (graph) => {
        await publishWorkflow(graph)
      }}
    />
  )
}

This is enough for an uncontrolled editor. For a controlled editor, pass value and update it in onChange(graph, meta).

Draft saving is host-owned. onSaveDraft defines the save operation, while autoSave opts the composer into calling it after committed graph changes. If autoSave is omitted, use custom chrome to call context.persistence.saveDraft() or run your own policy from onChange(graph, meta).

Documentation

Repository Layout

  • src/: reusable library source.
  • docs/: architecture notes and public API boundary decisions.
  • examples/workflow-lab/: standalone Vite app with demo chrome, custom nodes, inspector UI, note editing, and theme switching.

The library must not import from examples/. The example may depend on the library and any host-side dependencies it needs. The example is not the minimal API shape; it is a fuller product-style reference.

Boundary Principles

  • Core owns graph data, editor interaction state, React Flow integration, history, persistence hooks, validation plumbing, and extension contracts.
  • Host code owns chrome, palette UI, inspector UI, node card presentation, localized copy, publish UX, node-specific forms, icons, and business validation.
  • Core exposes graph and edge-level query helpers. It does not infer variable scope, execution reachability, or upstream/downstream semantics.
  • History entries store stable action codes, not display labels. Host chrome maps those codes to localized text.
  • React Flow is an intentional dependency. The kit is not trying to be a portable graph engine.
  • React Flow remains an internal renderer boundary. Public graph contracts use Workflow Kit types such as Position and Viewport instead of exposing React Flow node, edge, viewport, or instance types.

Core Concepts

Workflow Kit keeps the persisted workflow model small and host-owned:

type Graph = {
  nodes: GraphNode[]
  edges: GraphEdge[]
  viewport?: Viewport
}

GraphNode.data is the only node-specific payload. The kit never interprets that object beyond passing it back to your node definition and render callbacks. This keeps serialization, persistence, validation, and migrations under the host application's control.

NodeDefinition describes how a node type behaves inside the editor:

  • type is the stable node kind stored on each graph node.
  • defaultData seeds newly created nodes.
  • createTitle and createDescription provide defaults for new nodes.
  • getPorts returns connectable input and output handles.
  • getLayoutSize helps auto layout and fit-to-view use realistic node sizes.
  • renderCanvasNode renders fully custom canvas node content.
  • renderInspector can render node-specific settings inside the inspector.
  • execution: 'none' marks visual/helper nodes that should be ignored by execution-structure validation.

renderCanvasNode is the foundation for canvas customization. It remains a fully custom render path: the kit supplies handles and interaction context, and your renderer owns the node content. When you want the kit's visual shell without adopting any built-in copy or layout, wrap your content in NodeShell:

import { NodeShell, defineNode } from '@sinomoe/workflow-kit'

const textNode = defineNode<{ prompt: string }>({
  type: 'text',
  defaultData: { prompt: '' },
  renderCanvasNode: context => (
    <NodeShell context={context}>
      <div>{context.node.data.prompt}</div>
    </NodeShell>
  ),
})

NodeShell only renders a styled container and state classes for selected, bundled, dragging, and read-only states. It does not render titles, descriptions, buttons, placeholders, or other product copy. For custom port placement, use NodeHandle from the kit instead of importing React Flow components directly.

Use defineNode<TData>() to keep each node definition's data type connected to its callbacks, renderers, and node actions:

const textNode = defineNode<{ prompt: string }>({
  type: 'text',
  defaultData: { prompt: '' },
  createTitle: count => `Text ${count}`,
  getPorts: () => ({
    inputs: [{ id: 'in', label: 'Input' }],
    outputs: [{ id: 'out', label: 'Output' }],
  }),
  renderCanvasNode: ({ node }) => (
    <span>{node.data.prompt || 'Empty prompt'}</span>
  ),
  renderInspector: ({ node, actions, readOnly }) => (
    <textarea
      value={node.data.prompt}
      disabled={readOnly}
      onChange={event => actions.updateNodeData({ prompt: event.target.value })}
    />
  ),
})

See Node API Review for the current stability notes on NodeDefinition, CanvasNodeRenderContext, NodeRenderContext, and node actions.

Host Integration

Composer owns canvas interaction, selection, connections, history, viewport, keyboard shortcuts, and React Flow integration. Host code provides product UI through render props:

  • renderChrome(context) renders top bars, bottom controls, palettes, validation panels, and publish controls. Use actions from ChromeContext instead of reaching into the canvas implementation.
  • renderInspectorPanel(context) renders the inspector shell. The provided InspectorContext includes the selected node, definition, validation issues, panel width, and title/description update actions.
  • InspectorShell is an optional styled container for custom inspector panels. It applies panel sizing, floating-layer behavior, theme tokens, and basic form-control inheritance without rendering any labels or product copy.
  • renderEdgeAction(context) renders optional controls on selected edges, such as an insert-node button.
  • renderDefaultNodeContent(context) renders the default card body for node definitions that do not provide renderCanvasNode, while still using the kit's default card and handles.

For app-level validation, pass validationIssues and optionally validateConnection. validateGraphStructure covers structural checks such as duplicate IDs, unknown node types, dangling edges, invalid ports, and disconnected execution nodes. Domain rules, permissions, runtime variables, and publish blockers should stay in the host application.

Public API

Consumers should import from the package root:

import {
  Composer,
  defineNode,
  validateGraphStructure,
  type Graph,
} from '@sinomoe/workflow-kit'

The intended public surface includes:

  • Composer
  • composer contracts such as ComposerProps, ChromeContext, InspectorContext, PaletteState, and HistoryActionMap
  • graph contracts such as Graph, GraphNode, GraphEdge, GraphChangeMeta, NodeDefinition, AnyNodeDefinition, and Port
  • helpers such as defineNode, createRegistry, createGraphQuery, getNodePorts, pruneNodePorts, createNode, updateNodeMeta, applyAutoLayout, and validateGraphStructure

See Mutation Contract for graph-change history and onChange metadata semantics.

Files below components/, hooks/, react-flow/, utils/, and most of composer/ are implementation details unless re-exported from src/index.ts.

Composer Options

Composer supports both uncontrolled and controlled graph state through defaultValue, value, and onChange. onChange receives metadata for every committed composer change:

<Composer
  registry={registry}
  value={graph}
  onChange={(nextGraph, meta) => {
    setGraph(nextGraph)
    console.log(meta.action, meta.source)
  }}
/>

Panel width persistence is enabled by default with the key workflow-kit-panel-width. For apps that render multiple composers, pass a stable panelStorageKey per instance, or set it to false to disable local storage persistence:

<Composer
  registry={registry}
  defaultValue={graph}
  panelStorageKey="acme-workflow-builder-panel-width"
  defaultPanelWidth={620}
  minPanelWidth={560}
/>

Keyboard shortcuts are enabled by default and listen at document scope. In pages with multiple workflow editors, use keyboardScope="composer" or disable the built-in shortcuts and provide your own chrome actions:

<Composer
  registry={registry}
  defaultValue={graph}
  keyboardScope="composer"
/>

Example App

npm install
npm run dev

The dev script starts examples/workflow-lab.

Development

npm install
npm run typecheck
npm run lint
npm test
npm run test:smoke
npm run build
npm run check:api
npm run check:consumer
npm run build:example

Use npm run pack:check before publishing to verify the npm package contents. It builds the library, checks the public API boundary, verifies a real tarball consumer project, and prints the exact npm package file list.

License

MIT