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

linforge

v0.3.2

Published

Embeddable workbench for LangGraph Agent apps — edit, run, debug, and monitor in one place

Downloads

498

Readme

Linforge

Embeddable workbench for LangGraph Agent applications — design topology on canvas, implement logic in code, compile into executable graphs.

Why Linforge

| LangGraph Studio (official) | Linforge | |------------------------------|----------| | Desktop app | Web-embeddable — integrates into your product UI | | Code-only graph definition | Visual canvas + code hybrid | | Dev-only tool | Product managers sketch graphs, developers implement nodes | | Dev-time debugging | Built-in run history, step replay, and prompt versioning |

Install

npm install linforge
# or
pnpm add linforge

Peer dependencies (install only what you need):

# Server
pnpm add koa @koa/router

# UI
pnpm add react react-dom @xyflow/react

Quick Start

A minimal server in ~30 lines:

import Koa from 'koa';
import cors from '@koa/cors';
import bodyParser from 'koa-bodyparser';
import { StateSchema } from '@langchain/langgraph';
import { z } from 'zod/v4';
import { defineNodeFor } from 'linforge/core';
import { linforgeMiddleware } from 'linforge/server';

// 1. Define state
const MyState = new StateSchema({
  messages: z.array(z.string()).default([]),
  result: z.string().default(''),
});

// 2. Define nodes — state type is inferred automatically
const defineMyNode = defineNodeFor(MyState);

const greeter = defineMyNode({
  key: 'greeter',
  label: 'Greeter',
  run: async (state) => ({
    messages: [...state.messages, '[greeter] Hello!'],
    result: 'Greeting complete.',
  }),
});

// 3. Start server
const app = new Koa();
app.use(cors());
app.use(bodyParser());
app.use(linforgeMiddleware({
  stateSchema: MyState,
  nodes: [greeter],
}));

app.listen(3001);

This gives you a full REST API at http://localhost:3001/linforge — graph CRUD, run execution, step recording, and prompt management.

Package Exports

linforge          Re-exports all submodules
linforge/core     defineNode, Registry, Compiler, RunManager, PromptLoader, Store interfaces
linforge/server   Koa HTTP API (linforgeMiddleware + mountRoutes)
linforge/react    React hooks for graph editing, runs, and prompts
linforge/testing  In-memory Store implementations (dev/test)

Core Concepts

Three-Layer Architecture

Code Layer:    Node implementations + StateSchema + Route predicates    [developer]
Visual Layer:  Topology + Wiring + Prompts + Parameters                [canvas / DB]
Auto Layer:    Compiler merges both into a LangGraph Runnable           [linforge]

defineNode

Create node implementations with defineNode() or the type-safe defineNodeFor():

import { defineNodeFor } from 'linforge/core';

const defineMyNode = defineNodeFor(MyState);

const planner = defineMyNode({
  key: 'planner',
  run: async (state) => ({
    plan: 'Step 1: fetch. Step 2: analyze.',
  }),
  // Optional: conditional routing
  routes: {
    has_plan: (state) => !!state.plan,
  },
});

Store Interfaces

Four database-agnostic interfaces — implement them to plug in any database:

| Interface | Purpose | |-----------|---------| | GraphStore | Graph definition CRUD | | RunStore | Run record lifecycle | | StepPersister | Step data write/query | | PromptStore | Prompt version management |

PromptLoader

createPromptLoader(store) creates a cached prompt loader with Mustache template rendering:

import { createPromptLoader, renderPrompt } from 'linforge/core';

const loader = createPromptLoader(promptStore);

// Load active prompt, render variables, with fallback
const result = await loader.render('node-id', { name: 'World' }, {
  template: 'Hello {{name}}!',
  temperature: 0.7,
});
// result.text → "Hello World!"
// result.source → 'store' | 'fallback'

// Pure function (no store needed)
const text = renderPrompt('Hello {{name}}!', { name: 'World' });

Linforge ships in-memory implementations (linforge/testing) for development. For production, use an adapter package (e.g., linforge-adapter-prisma) or implement the interfaces yourself.

Server API

linforgeMiddleware (recommended)

One-line setup that auto-creates all internal components:

// Single-agent mode (backward compatible)
app.use(linforgeMiddleware({
  stateSchema: MyState,     // required — LangGraph StateSchema
  nodes: [planner, tools],  // required — node definitions
  prefix: '/linforge',      // default: "/linforge"
  stores: {                 // optional — custom Store implementations
    graphStore,
    runStore,
    stepPersister,
    promptStore,
  },
}));

Multi-Agent mode

Bind distinct stateSchema and nodes per Agent:

app.use(linforgeMiddleware({
  agents: [
    { slug: 'qa-bot', name: 'QA Bot', stateSchema: QAState, nodes: [retriever, answerer] },
    { slug: 'coder', name: 'Coder', stateSchema: CoderState, nodes: [planner, coder] },
  ],
  sharedNodes: [logger],  // available to all agents
}));

Each agent gets its own NodeRegistry and GraphCompiler. Routes resolve context by slug. The middleware auto-creates empty graph definitions in GraphStore on first request (code-first mode).

| Option | Type | Default | Description | |--------|------|---------|-------------| | agents | AgentConfig[] | — | Multi-agent config (mutually exclusive with stateSchema/nodes) | | sharedNodes | NodeDefinition[] | [] | Common nodes registered to every agent | | stateSchema | StateSchema | — | Single-agent: LangGraph StateSchema | | nodes | NodeDefinition[] | — | Single-agent: node definitions |

mountRoutes (low-level)

For full control over component creation. See examples/full-stack/ for a complete example.

REST Endpoints

All endpoints are prefixed with /linforge by default.

| Method | Path | Description | |--------|------|-------------| | GET | /graphs | List graphs | | POST | /graphs | Create graph | | PATCH | /graphs/:slug | Update graph metadata | | GET | /graph/:slug | Get graph definition | | PUT | /graph/:slug | Save graph definition | | POST | /graph/:slug/run | Trigger a run | | GET | /graph/:slug/runs | List run history | | GET | /runs/:runId | Get run details | | GET | /runs/:runId/steps | List steps for a run | | GET | /registry/nodes | List registered nodes | | GET | /prompts/:nodeId | List prompt versions | | GET | /prompts/:nodeId/active | Get active prompt | | POST | /prompts/:nodeId | Create prompt version | | POST | /prompts/:nodeId/versions/:id/activate | Activate prompt version | | GET | /templates | List available templates | | POST | /graph/:slug/apply-template | Apply template to graph |

Optional stores (runStore, stepPersister, promptStore) return 501 when not provided.

React Hooks

import {
  useLinforgeGraph,
  useLinforgeGraphList,
  useLinforgeRuns,
  useLinforgePrompt,
} from 'linforge/react';

| Hook | Purpose | |------|---------| | useLinforgeGraph | Graph editing (nodes, edges, save) | | useLinforgeGraphList | Graph list CRUD | | useLinforgeRuns | Run management (trigger, list, detail) | | useLinforgePrompt | Prompt version management |

Custom Store Adapter

Implement the four interfaces to connect any database:

import type { GraphStore, RunStore, StepPersister, PromptStore } from 'linforge/core';

class MyGraphStore implements GraphStore {
  async getGraph(slug: string) { /* ... */ }
  async saveGraph(graph) { /* ... */ }
  async listGraphs() { /* ... */ }
}

// Pass to middleware
app.use(linforgeMiddleware({
  stateSchema: MyState,
  nodes: [planner],
  stores: { graphStore: new MyGraphStore() },
}));

See linforge-adapter-prisma for a reference implementation.

Examples

License

MIT