@bpmnkit/core

中文文档
1.9.0发布于 2026/11/06MITTypeScriptES Module构建来源已验证近 30 天下载 3585

TypeScript-first BPMN 2.0 SDK — parse, build, layout, and optimize diagrams

原始使用文档
文档目录25 节
  1. Overview
  2. Features
  3. Installation
  4. Quick Start
  5. Build a process from code
  6. Sub-processes, boundary events and multi-instance
  7. Parse and modify existing BPMN
  8. Auto-layout a process
  9. Optimize a diagram
  10. Compact format for AI/LLM workflows
  11. Rendering while the model is still writing
  12. API Reference
  13. BPMN
  14. Process builder
  15. Semantics
  16. Typed code generation
  17. Editing
  18. Writing files ( @bpmnkit/core/node )
  19. Connectors ( @bpmnkit/core/connectors )
  20. DMN
  21. Form
  22. Layout & Optimization
  23. Process documentation
  24. Related Packages
  25. License

Agents: read node_modules/@bpmnkit/core/AGENTS.md, then node_modules/@bpmnkit/core/agent-cards/<bpmn|dmn|form>.md (~1k tokens) instead of this README.

BPMN Kit logo

@bpmnkit/core

TypeScript-first BPMN 2.0 SDK — parse, build, layout, and optimize diagrams

npm license typescript ai-assisted tier: core

Website · Documentation · GitHub · Changelog

Core tier. Semver at 1.0: nothing breaks without a major release. See product tiers.


Overview

@bpmnkit/core is the foundation of the BPMN Kit. It gives you everything to work with BPMN 2.0, DMN 1.3, and Camunda Form definitions in pure TypeScript — no XML wrestling, no runtime dependencies.

Parse → Modify → Validate → Export

Features

  • BPMN 2.0 — parse, create, and export process diagrams with full Zeebe/Camunda 8 extension support
  • Fluent Builder API — construct valid processes programmatically, never touch raw XML
  • Sugiyama Layout Engine — auto-position elements with clean orthogonal edge routing
  • DMN 1.3 — decision tables, including FEEL expression support
  • Camunda Form Definitions — type-safe form schema builder
  • Optimizer — built-in rule engine to detect and auto-fix anti-patterns
  • Compact Format — 70% smaller token-efficient JSON representation for AI/LLM workflows
  • Connector catalog — all Camunda 8 out-of-the-box connector templates, applied deterministically (@bpmnkit/core/connectors)
  • Zero Dependencies — runs in browsers, Node.js, Deno, Bun, and edge runtimes

Installation

npm install @bpmnkit/core
pnpm add @bpmnkit/core

Quick Start

Build a process from code

Every element takes (id, options). Branches off a gateway are declared with .branch(), not by wiring flows by hand — the builder creates the sequence flows.

import { Bpmn } from "@bpmnkit/core"

const defs = Bpmn.createProcess("order-flow")
  .name("Order Flow")
  .startEvent("start", { name: "Order Received" })
  .serviceTask("validate", {
    name: "Validate Order",
    taskType: "order-validator",
    ioMapping: {
      inputs: [{ source: "=order", target: "order" }],
      outputs: [{ source: "=valid", target: "isValid" }],
    },
  })
  .exclusiveGateway("check", { name: "Order Valid?" })
  .branch("valid", (b) =>
    b
      .condition("=isValid = true")
      .serviceTask("fulfill", { name: "Fulfill Order", taskType: "fulfillment-service" })
      .endEvent("end", { name: "Order Complete" }),
  )
  .branch("invalid", (b) =>
    b.defaultFlow().endEvent("reject-end", { name: "Order Rejected" }),
  )
  .withAutoLayout()
  .build()

const xml = Bpmn.export(defs)

Sub-processes, boundary events and multi-instance

These are the constructs most often reached for and most often guessed at, so the exact shapes are worth stating. subProcess takes its body as a callback (second argument) and options third. A boundary event needs the id of the activity it attaches to; withBoundary supplies it from the preceding activity for you.

import { Bpmn } from "@bpmnkit/core"

const defs = Bpmn.createProcess("fulfilment")
  .name("Fulfilment")
  .startEvent("start")

  // Sub-process: id, content callback, then options.
  // multiInstance runs the body once per item in the collection.
  .subProcess(
    "provision",
    (sub) => {
      sub
        .startEvent("p-start")
        .serviceTask("provision-line", { name: "Provision Line", taskType: "provision" })
        .endEvent("p-end")
    },
    {
      name: "Provision Each Line",
      multiInstance: { collection: "=order.lines", elementVariable: "line", isSequential: false },
    },
  )

  // withBoundary attaches to the activity just added — no id repeated.
  .serviceTask("charge", { name: "Charge Payment", taskType: "payment" })
  .withBoundary("charge-failed", { errorCode: "PAYMENT_FAILED" }, (b) => {
    b.serviceTask("retry-charge", { name: "Retry Charge", taskType: "payment" }).endEvent("gave-up")
  })

  // Wait for whichever event arrives first.
  .eventBasedGateway("await-payment", { name: "Paid?" })
  .branch("paid", (b) =>
    b
      .intermediateCatchEvent("payment-confirmed", {
        messageName: "PaymentConfirmed",
        correlationKey: "=orderId",
      })
      .endEvent("done"),
  )
  .branch("timeout", (b) =>
    b.intermediateCatchEvent("payment-timeout", { timerDuration: "P14D" }).endEvent("written-off"),
  )
  .withAutoLayout()
  .build()

Attach a boundary event explicitly when it is not the preceding activity — both forms below are accepted, and a boundary event with no host throws rather than exporting BPMN that cannot be read back:

.boundaryEvent("timeout", { attachedTo: "charge", timerDuration: "PT30M" })
.boundaryEvent("timeout", "charge", { timerDuration: "PT30M" })

Event definitions are named options, not nested objects — timerDuration, timerDate, timerCycle, messageName (with correlationKey), errorCode, signalName, compensation. An option the builder does not know is dropped silently, so timer: { duration: "P7D" } produces a boundary event with no timer on it.

Parse and modify existing BPMN

import { Bpmn } from "@bpmnkit/core"

const defs = Bpmn.parse(xml)
const process = defs.processes[0]

// Access flow elements
for (const el of process.flowElements) {
  console.log(el.type, el.id, el.name)
}

// Serialize back to XML
const updated = Bpmn.export(defs)

Auto-layout a process

import { applyAutoLayout, Bpmn } from "@bpmnkit/core"

// Lays out every process and writes the diagram interchange back onto the model.
const laid = Bpmn.export(applyAutoLayout(Bpmn.parse(xml)))

Optimize a diagram

import { Bpmn, optimize } from "@bpmnkit/core"

const defs = Bpmn.parse(xml)
const report = optimize(defs)

console.log(`${report.summary.total} findings`)

for (const finding of report.findings) {
  console.log(`[${finding.severity}] ${finding.message}`)
  if (finding.applyFix) {
    const { description } = finding.applyFix(defs)
    console.log("Fixed:", description)
  }
}

Compact format for AI/LLM workflows

import { Bpmn, compactify, expand } from "@bpmnkit/core"

// Shrink for AI prompt
const defs = Bpmn.parse(xml)
const compact = compactify(defs)          // ~70% smaller JSON
const json = JSON.stringify(compact)      // send to LLM

// Restore full BPMN from AI response
const restored = expand(JSON.parse(json))
const outXml = Bpmn.export(restored)

Rendering while the model is still writing

A model emits a diagram one token at a time, and the outermost } — the one JSON.parse waits for — is the last character it sends. createCompactStream reads the elements out of the text as their own literals close, so there is something to draw long before the document is finished:

import { createCompactStream } from "@bpmnkit/core"

// `base` is the diagram being edited, so a frame shows the whole thing rather
// than the fragment the model is adding to it. Omit it to build from nothing.
const diagramStream = createCompactStream({ base: null })

function onModelChunk(chunk: string): void {
  const frame = diagramStream.push(chunk) // null until the frame changes
  if (frame) console.log("elements so far:", frame.processes[0]?.flowElements.length)
}

Frames are a guess at an unfinished document: push never throws, drops what it cannot place, and expects the caller to have an authoritative result coming.

API Reference

BPMN

ExportDescription
Bpmn.parse(xml)Parse BPMN XML → BpmnDefinitions
Bpmn.export(defs)Serialize BpmnDefinitions → XML
Bpmn.createProcess(id)Start a ProcessBuilder; set the name with .name(…)
Bpmn.makeEmpty(processId?, name?)Minimal BPMN XML with one start event
Bpmn.SAMPLE_XML3-node sample diagram string

Process builder

Every element method is (id, options) and returns the builder. The same methods exist inside .branch() and inside a sub-process body.

MethodDescription
.name(name) / .versionTag(v)Process-level metadata
.startEvent(id?, options?) / .endEvent(id?, options?)Events; options.name labels them
.serviceTask(id, { taskType, ioMapping?, taskHeaders? })Zeebe job worker task
.userTask(id, opts) / .businessRuleTask(id, { decisionRef }) / .scriptTask(id, opts)Other task types
.receiveTask(id, { message }) / .sendTask(id, opts) / .callActivity(id, opts)Message and call activities
.restConnector(id, { method, url, … })Camunda 8 HTTP connector task
.exclusiveGateway(id, opts) / .parallelGateway / .inclusiveGateway / .eventBasedGatewayGateways
.branch(name, b => …)One path off the preceding gateway
b.condition(feel) / b.defaultFlow()Mark a branch's condition, or make it the default
.connectTo(id)Flow to an existing or later element — merges and loops
.subProcess(id, content, options?)Embedded sub-process; content is a callback
.transaction(id, content, options?) / .eventSubProcess / .adHocSubProcessOther containers
.withBoundary(id, options, handler)Boundary event on the preceding activity, plus its path
.boundaryEvent(id, options)Boundary event naming its host in options.attachedTo
.intermediateCatchEvent(id, opts) / .intermediateThrowEvent(id, opts)Intermediate events
.withAutoLayout()Compute coordinates on build() — no x/y by hand
.build()Produce BpmnDefinitions; pass to Bpmn.export

Event definitions are options on the element: timerDuration, timerDate, timerCycle, messageName + correlationKey, errorCode, signalName, compensation. Multi-instance is options.multiInstance = { collection, elementVariable, isSequential? }.

Semantics

ExportDescription
semanticHash(defs)SHA-256 of the model, excluding the diagram. Unchanged by layout
projectSemantics(defs)The canonical, presentation-free projection the hash covers
diffSemantics(a, b)What changed between two models, keyed by element id

Typed code generation

ExportDescription
generateProcessTypes(defs | defs[], options?)TypeScript source typing job types (variables, output, headers, errors), process ids, messages, signals, error and escalation codes. Deterministic
extractProcessContract(defs | defs[])The same contract as data — what casen gen types renders and --check-workers compares

Editing

ExportDescription
applyBpmnOperations(defs, ops)Apply edit operations to the full model. Strict: unresolved ids throw
reconcileCompact(defs, compact)Apply a compact diagram as changes, keeping what compact cannot carry
compactify(defs)Read-only token-efficient view for LLM prompts. Lossy — not an edit path

Writing files (@bpmnkit/core/node)

ExportDescription
writeBpmn(defs, opts)Serialize, read back, verify the model survived, then write atomically
import { writeBpmn } from "@bpmnkit/core/node"

// Refuses rather than overwrite; pass force: true to replace.
const { semanticHash, changes } = await writeBpmn(defs, { output: "flow.bpmn" })

Connectors (@bpmnkit/core/connectors)

The Camunda 8 out-of-the-box connector catalog, behind its own subpath so its data is only bundled by code that imports it. The templates leave out icons, groups, tooltips and placeholders; @bpmnkit/connectors adds those back for a property panel.

ExportDescription
listConnectors() / searchConnectors(query)Summaries: task type, direction, required and optional inputs
applyConnectorTemplate(id, values)Builder options for a bundled template, plus problems
applyTemplateToElement(defs, id, template, values)Apply a template to an existing element
validateElementTemplate(value)Structural check of an element template
findConnectorCards(query) / formatConnectorCard(card)One card per operation, with only its inputs — for a model's prompt
applyConnectorLines(defs, lines)Apply the with lines a model wrote in the line format
selectConnectors({ text, tasks })Pick the cards a model sees for each task of a diagram
import { applyConnectorTemplate } from "@bpmnkit/core/connectors"

const { serviceTask, problems } = applyConnectorTemplate("io.camunda.connectors.HttpJson.v2", {
  url: "https://api.example.com/orders",
})

DMN

ExportDescription
Dmn.parse(xml)Parse DMN XML → DmnDefinitions
Dmn.export(defs)Serialize → XML
Dmn.createDecisionTable(id, name?)Start a DecisionTableBuilder
Dmn.makeEmpty()Minimal DMN with one empty decision table

Form

ExportDescription
Form.create()Start a FormBuilder
Form.parse(json)Parse a form schema
Form.export(schema)Serialize → JSON string

Layout & Optimization

ExportDescription
layoutProcess(process)Auto-layout all elements; returns LayoutResult
optimize(defs)Run all optimization rules; returns OptimizeReport
analyzeCamundaCompat(defs, version?)What the targeted Camunda 8 version cannot run or requires, as compat/* findings — Camunda Modeler's @camunda/linting rules
compactify(defs)Convert to compact CompactDiagram
expand(compact)Restore full BpmnDefinitions
createCompactStream(opts?)Read a diagram out of a model's token stream, frame by frame
parseProcessText(text)Read a process a model wrote in the line format (PROCESS_TEXT_GUIDE); never throws
createProcessTextStream()Read the line format while it streams, one finished line at a time
writeProcessText(defs)Write an existing diagram in the line format, for a model to read; returns the text and an alias → element id map
parseProcessDelta(text)Read a change script (PROCESS_DELTA_GUIDE): only what changes, by id; never throws
generateId(prefix)Generate a unique short ID

Process documentation

ExportDescription
renderDocumentationHtml(defs, options?)Self-contained, print-ready HTML: diagram, contents, every element in flow order, linked DMN tables and forms. Print → Save as PDF
renderDocumentationMarkdown(defs, options?)The same content as Markdown, for wikis and Confluence
renderDocumentationDocx(defs, options?)The same content as a Word .docx (bytes), diagram as SVG
buildProcessDocumentation(defs, options?)The structured content, for a renderer of your own

options takes decisions (DmnDefinitions[]), forms (FormDefinition[]), title, subtitle and diagram. Output is deterministic and all model text is escaped.


PackageDescription
@bpmnkit/canvasZero-dependency SVG BPMN viewer
@bpmnkit/editorFull-featured interactive BPMN editor
@bpmnkit/engineLightweight BPMN process simulator for tests and demos
@bpmnkit/feelFEEL expression language parser & evaluator
@bpmnkit/plugins34 composable canvas plugins
@bpmnkit/apiCamunda 8 REST API TypeScript client
@bpmnkit/asciiRender BPMN diagrams as Unicode ASCII art
@bpmnkit/markdownBPMN diagrams in Markdown — remark, markdown-it and README pre-rendering
@bpmnkit/docspackBPMN Kit docs as an offline docspack package for AI agents
@bpmnkit/camunda-docspackCamunda 8 docs as an offline docspack package for AI agents
@bpmnkit/uiShared design tokens and UI components
@bpmnkit/profilesShared auth, profile storage, and client factories for CLI & proxy
@bpmnkit/operateMonitoring & operations frontend for Camunda clusters
@bpmnkit/connector-genGenerate connector templates from OpenAPI specs
@bpmnkit/connectorsCamunda 8 OOTB connector catalog and deterministic template application
@bpmnkit/cliCamunda 8 command-line interface (casen)
@bpmnkit/proxyLocal AI bridge and Camunda API proxy server
@bpmnkit/patternsDomain process patterns for BPMNKit AIKit
@bpmnkit/reebe-wasmWebAssembly BPMN engine for browser simulation
@bpmnkit/worker-clientThin Zeebe REST client for standalone workers
@bpmnkit/flowCode-first durable flows that derive BPMN, job types and workers
@bpmnkit/user-tasksEmbeddable user task widget for Camunda 8
@bpmnkit/cli-sdkPlugin authoring SDK for the casen CLI
@bpmnkit/create-casen-pluginScaffold a new casen CLI plugin in seconds
@bpmnkit/casen-reportHTML reports from Camunda 8 incident and SLA data
@bpmnkit/casen-worker-httpExample HTTP worker plugin — completes jobs with live JSONPlaceholder API data
@bpmnkit/casen-worker-aiAI task worker — classify, summarize, extract, and decide using Claude

License

MIT © BPMN Kit — made by u11g

BPMN Kit

版本与发布记录

@bpmnkit/core 共发布 40 个版本,首次发布于 2026/03/15;过去 12 个月发布了 40 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.9.0latest2026/11/062 天前
1.8.02026/11/062 天前
1.7.02026/11/052 天前
1.6.12026/10/2118 天前
1.6.02026/10/2018 天前
1.5.02026/10/0930 天前

关于 @bpmnkit/core 的常见问题

@bpmnkit/core 是什么?

TypeScript-first BPMN 2.0 SDK — parse, build, layout, and optimize diagrams

如何安装 @bpmnkit/core?

在项目目录运行 npm install @bpmnkit/core,或使用 pnpm add / yarn add / bun add。

@bpmnkit/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bpmnkit/core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bpmnkit/core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bpmnkit/core 可以商用吗?

@bpmnkit/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bpmnkit/core 的下载量有多少?

根据 npm 官方下载统计,@bpmnkit/core 在 2026/10/09 – 2026/11/07 这 30 天内被下载 3,585 次,日均约 120 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bpmnkit/core 还在维护吗?

最新版本 1.9.0 发布于 2026/11/06(2 天前);至今共发布 40 个版本,其中过去 12 个月发布了 40 个;首次发布于 2026/03/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。