Skip to content

joshuaisaact/petri-ts

Repository files navigation

petri-ts

A small TypeScript Petri net engine. Define places and transitions, fire them, analyse reachable states, or wire up a load/fire/save dispatcher for production use.

For background on Petri nets and the ideas behind this library, see the blog post.

Install

npm install petri-ts

Quick start

import { type PetriNet, fire, reachableStates, terminalStates } from "petri-ts";

type Place = "idle" | "running" | "done";

const net: PetriNet<Place> = {
  transitions: [
    { name: "start", inputs: ["idle"], outputs: ["running"] },
    { name: "finish", inputs: ["running"], outputs: ["done"] },
  ],
  initialMarking: { idle: 1, running: 0, done: 0 },
};

let m = net.initialMarking;
m = fire(m, net.transitions[0]); // { idle: 0, running: 1, done: 0 }

reachableStates(net);  // all reachable markings from initialMarking
terminalStates(net);   // markings where nothing can fire

Analyse

Run all analysis in one call — reachable states, terminal states, deadlock check, invariant verification, and optional DOT output:

import { analyse } from "petri-ts";

const result = analyse(net, {
  invariants: [
    { weights: { idle: 1, running: 1, done: 1 } },
  ],
  dot: true,
});

result.reachableStateCount; // 3
result.terminalStates;      // [{ idle: 0, running: 0, done: 1 }]
result.isDeadlockFree;      // false (done is terminal)
result.invariants[0].holds; // true (tokens conserved)
result.dot;                 // Graphviz DOT string

Dispatcher

For the "marking as a JSON column" pattern — one net definition, one row per instance, load/fire/save inside a transaction:

import { createDispatcher, memoryAdapter } from "petri-ts";

const d = createDispatcher(net, memoryAdapter());
await d.create("order-1");
await d.dispatch("order-1", "start");
const { marking } = await d.inspect("order-1");

memoryAdapter() is a Map-backed store for tests. In production, write your own PersistenceAdapter that wraps your database — the lib doesn't handle locking, your transaction does.

API

Function Description
canFire(marking, transition) Check if a transition is enabled
fire(marking, transition) Fire a transition, returns new marking
reachableStates(net, limit?) BFS all reachable markings (default limit: 10,000)
terminalStates(net, states?) Reachable markings with no enabled transitions
isDeadlockFree(net, states?) True if no terminal states exist
enabledTransitions(net, marking) Which transitions can fire
checkInvariant(net, weights, states?) Verify a weighted token sum is constant
analyse(net, options?) All-in-one analysis: states, deadlocks, invariants, DOT
toDot(net, marking?) Graphviz DOT output
createDispatcher(net, adapter, options?) Load/fire/save dispatcher
memoryAdapter() In-memory persistence for tests

Dev

npm test        # vitest
npm run build   # JS bundle + .d.ts
npm run check   # tsc --noEmit
npm run lint    # oxlint
npm run fmt     # oxfmt

About

A lightweight TypeScript Petri net engine for modeling and analyzing state machines and workflows

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors