@nanobpm/bojtos-react 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -2
- package/dist/BpmnRuntimeView.d.ts +12 -1
- package/dist/BpmnRuntimeView.js +20 -4
- package/dist/TraceTimeline.d.ts +50 -0
- package/dist/TraceTimeline.js +81 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +5 -1
- package/dist/runState.d.ts +47 -0
- package/dist/runState.js +64 -0
- package/dist/useBojtos.d.ts +71 -4
- package/dist/useBojtos.js +51 -9
- package/package.json +5 -3
- package/src/BpmnRuntimeView.tsx +51 -4
- package/src/TraceTimeline.tsx +303 -0
- package/src/index.ts +30 -0
- package/src/runState.ts +72 -0
- package/src/useBojtos.ts +215 -11
package/README.md
CHANGED
|
@@ -10,6 +10,15 @@ React binding for the **Bojtos** in-browser BPMN demo framework
|
|
|
10
10
|
- **`<BpmnRuntimeView xml activeIds incidentIds />`** — the live diagram: it
|
|
11
11
|
imports the XML once and updates token (`nano-active`) / incident
|
|
12
12
|
(`nano-incident`) markers in place, so zoom/scroll survive stepping.
|
|
13
|
+
- **`<TraceTimeline rows />`** — the shared activity log: it renders the
|
|
14
|
+
framework-agnostic trace model from [`@nanobpm/bojtos-kit`](../bojtos-kit) as a
|
|
15
|
+
turn-by-turn story (consecutive same-`turn` rows fold into one card; rows with
|
|
16
|
+
no `turn` render as plain lines). Feed it a kit adapter —
|
|
17
|
+
`foldEngineEvents(run.events)` for a plain engine run, or
|
|
18
|
+
`traceEntriesToRows(entries)` for handler-emitted agent/tool/turn entries. It
|
|
19
|
+
imports **only** the kit and React, so a trace-only import tree-shakes `bpmn-js`
|
|
20
|
+
out (the package is `sideEffects: false`); a test walks the built module graph
|
|
21
|
+
to pin that.
|
|
13
22
|
|
|
14
23
|
## Install
|
|
15
24
|
|
|
@@ -42,6 +51,26 @@ function Demo({ bpmn }: { bpmn: string }) {
|
|
|
42
51
|
}
|
|
43
52
|
```
|
|
44
53
|
|
|
54
|
+
## Trace timeline
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useBojtos, TraceTimeline, foldEngineEvents } from "@nanobpm/bojtos-react";
|
|
58
|
+
|
|
59
|
+
function RunLog({ bpmn }: { bpmn: string }) {
|
|
60
|
+
const run = useBojtos({ bpmn });
|
|
61
|
+
// Engine-event fold — the non-agentic / test-view case.
|
|
62
|
+
return <TraceTimeline rows={foldEngineEvents(run.events)} />;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For an agentic run, emit `TraceEntry` lines from your handlers (with the additive
|
|
67
|
+
`turn` / `elementId` / `args` / `result` fields) and pass
|
|
68
|
+
`traceEntriesToRows(entries)` instead — same component, turn-grouped card view.
|
|
69
|
+
`TraceTimeline` keeps the class names (`timeline`, `timeline-turn`,
|
|
70
|
+
`log-line log-<kind>`, …) the demo stylesheet already targets, so your CSS applies
|
|
71
|
+
unchanged. It never imports `bpmn-js`, so importing it alone won't pull the
|
|
72
|
+
diagram bundle in.
|
|
73
|
+
|
|
45
74
|
## Peer requirements
|
|
46
75
|
|
|
47
76
|
`react` and `bpmn-js` are peer dependencies (the consumer already has them). The
|
|
@@ -51,6 +80,7 @@ consumer must import bpmn-js's diagram CSS once and provide the `.nano-active` /
|
|
|
51
80
|
## Build
|
|
52
81
|
|
|
53
82
|
`dist/` (the tsc-emitted JS + `.d.ts`, with JSX already compiled to
|
|
54
|
-
`react/jsx-runtime` so consumers never re-transform node_modules) is
|
|
55
|
-
|
|
83
|
+
`react/jsx-runtime` so consumers never re-transform node_modules) is what ships,
|
|
84
|
+
built by `prepack` on publish. It is **not** committed — `.gitignore` covers it —
|
|
85
|
+
so build before pointing a `file:` consumer at this workspace. Regenerate with
|
|
56
86
|
`npm run build`.
|
|
@@ -7,6 +7,17 @@ export interface BpmnRuntimeViewProps {
|
|
|
7
7
|
incidentIds: string[];
|
|
8
8
|
/** Optional class for the container element (it always fills its parent). */
|
|
9
9
|
className?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Accessible name for the diagram. The token and incident highlights are
|
|
12
|
+
* purely visual, so without this a screen-reader user is told nothing at all
|
|
13
|
+
* about what is running.
|
|
14
|
+
*/
|
|
15
|
+
label?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Map an element id to a human name for the live status announcement — pass
|
|
18
|
+
* the diagram's element names if you have them. Defaults to the raw id.
|
|
19
|
+
*/
|
|
20
|
+
elementName?: (elementId: string) => string;
|
|
10
21
|
}
|
|
11
22
|
/**
|
|
12
23
|
* Read-only diagram that imports the XML once and updates token/incident markers
|
|
@@ -20,4 +31,4 @@ export interface BpmnRuntimeViewProps {
|
|
|
20
31
|
* and provide the `.nano-active` / `.nano-incident` marker styles plus a
|
|
21
32
|
* `.nano-token` style for the token badge overlaid on each active element.
|
|
22
33
|
*/
|
|
23
|
-
export declare function BpmnRuntimeView({ xml, activeIds, incidentIds, className, }: BpmnRuntimeViewProps): import("react").JSX.Element;
|
|
34
|
+
export declare function BpmnRuntimeView({ xml, activeIds, incidentIds, className, label, elementName, }: BpmnRuntimeViewProps): import("react").JSX.Element;
|
package/dist/BpmnRuntimeView.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
import NavigatedViewer from "bpmn-js/lib/NavigatedViewer";
|
|
4
|
+
import { describeRunState, markerKey } from "./runState.js";
|
|
4
5
|
/**
|
|
5
6
|
* Read-only diagram that imports the XML once and updates token/incident markers
|
|
6
7
|
* in place (no re-import, so the zoom/scroll position is preserved while
|
|
@@ -13,7 +14,7 @@ import NavigatedViewer from "bpmn-js/lib/NavigatedViewer";
|
|
|
13
14
|
* and provide the `.nano-active` / `.nano-incident` marker styles plus a
|
|
14
15
|
* `.nano-token` style for the token badge overlaid on each active element.
|
|
15
16
|
*/
|
|
16
|
-
export function BpmnRuntimeView({ xml, activeIds, incidentIds, className, }) {
|
|
17
|
+
export function BpmnRuntimeView({ xml, activeIds, incidentIds, className, label = "BPMN process diagram", elementName, }) {
|
|
17
18
|
const containerRef = useRef(null);
|
|
18
19
|
const viewerRef = useRef(null);
|
|
19
20
|
const importedRef = useRef(false);
|
|
@@ -101,9 +102,24 @@ export function BpmnRuntimeView({ xml, activeIds, incidentIds, className, }) {
|
|
|
101
102
|
}
|
|
102
103
|
tokenOverlaysRef.current = nextOverlays;
|
|
103
104
|
}
|
|
105
|
+
// `activeIds` / `incidentIds` are almost always fresh arrays (`snapshot?.x ??
|
|
106
|
+
// []`), so depending on their identity re-painted every marker and re-created
|
|
107
|
+
// every token overlay on every render of the parent — visible churn on a busy
|
|
108
|
+
// diagram. Depend on the ids themselves instead.
|
|
109
|
+
const key = markerKey(activeIds, incidentIds);
|
|
104
110
|
useEffect(() => {
|
|
105
111
|
applyMarkers();
|
|
106
112
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
107
|
-
}, [
|
|
108
|
-
return (_jsx("div", { ref: containerRef,
|
|
113
|
+
}, [key]);
|
|
114
|
+
return (_jsxs("div", { className: className, style: { width: "100%", height: "100%", position: "relative" }, children: [_jsx("div", { ref: containerRef, role: "img", "aria-label": label, style: { width: "100%", height: "100%" } }), _jsx("div", { role: "status", "aria-live": "polite", style: {
|
|
115
|
+
position: "absolute",
|
|
116
|
+
width: 1,
|
|
117
|
+
height: 1,
|
|
118
|
+
margin: -1,
|
|
119
|
+
padding: 0,
|
|
120
|
+
overflow: "hidden",
|
|
121
|
+
clip: "rect(0 0 0 0)",
|
|
122
|
+
whiteSpace: "nowrap",
|
|
123
|
+
border: 0,
|
|
124
|
+
}, children: describeRunState(activeIds, incidentIds, elementName) })] }));
|
|
109
125
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type ElementStatDto, type IncidentDto, type TraceRow } from "@nanobpm/bojtos-kit";
|
|
2
|
+
/**
|
|
3
|
+
* The shared activity log — the run told as a story rather than a flat stack of
|
|
4
|
+
* lines. It is the single component that retired the two drifted, forked
|
|
5
|
+
* `TraceTimeline` copies (nanobpm/bojtos#9): the web-demo framework's agent/tool/
|
|
6
|
+
* turn view and the console test-view's engine-event fold.
|
|
7
|
+
*
|
|
8
|
+
* It renders the framework-agnostic {@link TraceRow} model from
|
|
9
|
+
* `@nanobpm/bojtos-kit` — feed it whichever adapter matches your source:
|
|
10
|
+
*
|
|
11
|
+
* - `foldEngineEvents(useBojtos().events)` for the non-agentic / test-view case, or
|
|
12
|
+
* - `traceEntriesToRows(entries)` for handler-emitted agent/tool/turn entries.
|
|
13
|
+
*
|
|
14
|
+
* Consecutive rows sharing a `turn` fold into one card (the model's raw LLM reply,
|
|
15
|
+
* each tool it activated with its arguments, and — once it lands — what that tool
|
|
16
|
+
* returned); rows with no `turn` render as plain lines in order, so a non-agentic
|
|
17
|
+
* run looks exactly like the flat log it replaces.
|
|
18
|
+
*
|
|
19
|
+
* This module deliberately imports **only** the kit and React — never
|
|
20
|
+
* `./BpmnRuntimeView` or `bpmn-js` — so a trace-only import tree-shakes the
|
|
21
|
+
* diagram renderer out (the package is `sideEffects: false`). See
|
|
22
|
+
* `test/trace.timeline.test.ts`, which walks the built module graph to pin that.
|
|
23
|
+
*
|
|
24
|
+
* The markup keeps the class names the forked copies' CSS already targets
|
|
25
|
+
* (`timeline`, `timeline-turn`, `timeline-tool`, `log-line log-<kind>`, …) so a
|
|
26
|
+
* consumer's existing stylesheet applies unchanged; no design-system dependency
|
|
27
|
+
* is pulled in.
|
|
28
|
+
*/
|
|
29
|
+
export interface TraceTimelineProps {
|
|
30
|
+
/**
|
|
31
|
+
* The normalized rows to render. Produce them with a kit adapter
|
|
32
|
+
* (`foldEngineEvents` / `traceEntriesToRows`) or your own {@link TraceRow[]}.
|
|
33
|
+
*/
|
|
34
|
+
rows: TraceRow[];
|
|
35
|
+
/** `snapshot.elementStats` — per-element completion/active counts, engine-side. */
|
|
36
|
+
elementStats?: ElementStatDto[];
|
|
37
|
+
/** Incidents on the current snapshot, with their reason. */
|
|
38
|
+
incidents?: IncidentDto[];
|
|
39
|
+
/** BPMN element id → human label, for both the timeline and the panels below. */
|
|
40
|
+
labelFor?: (elementId: string) => string;
|
|
41
|
+
/** Card heading. Defaults to "Activity". */
|
|
42
|
+
title?: string;
|
|
43
|
+
/** Sub-heading under the title. */
|
|
44
|
+
description?: string;
|
|
45
|
+
/** Shown when there are no rows yet. Defaults to "Press Run to start.". */
|
|
46
|
+
emptyText?: string;
|
|
47
|
+
/** Optional class for the outer container. */
|
|
48
|
+
className?: string;
|
|
49
|
+
}
|
|
50
|
+
export declare function TraceTimeline({ rows, elementStats, incidents, labelFor, title, description, emptyText, className, }: TraceTimelineProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { buildTraceItems, isTraceTurnGroup, } from "@nanobpm/bojtos-kit";
|
|
4
|
+
function safeStringify(value, space) {
|
|
5
|
+
// `JSON.stringify(undefined)` returns the JS value `undefined` (not a string),
|
|
6
|
+
// which would render as nothing. Emit the literal "undefined" so a handler/tool
|
|
7
|
+
// that actually returned `undefined` is shown explicitly rather than vanishing.
|
|
8
|
+
if (value === undefined)
|
|
9
|
+
return "undefined";
|
|
10
|
+
try {
|
|
11
|
+
// `JSON.stringify` throws on BigInt and circular structures; a replacer
|
|
12
|
+
// renders BigInt losslessly as its decimal string so trace payloads that
|
|
13
|
+
// carry engine-native BigInts don't crash serialization.
|
|
14
|
+
return JSON.stringify(value, (_key, val) => (typeof val === "bigint" ? val.toString() : val), space);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "[unserializable value]";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function ToolStep({ activation, result, labelFor, }) {
|
|
21
|
+
const elementId = activation.elementId ?? "";
|
|
22
|
+
return (_jsxs("div", { className: "timeline-tool", children: [_jsxs("div", { className: "timeline-tool-head", children: [_jsx("span", { className: "timeline-badge timeline-badge-info", children: "tool" }), _jsx("strong", { children: labelFor(elementId) || elementId }), _jsx("code", { children: elementId })] }), activation.args !== undefined &&
|
|
23
|
+
Object.keys(activation.args).length > 0 && (_jsxs("div", { className: "timeline-kv", children: [_jsx("span", { className: "timeline-kv-label", children: "arguments" }), _jsx("code", { children: safeStringify(activation.args) })] })), _jsxs("div", { className: "timeline-kv", children: [_jsx("span", { className: "timeline-kv-label", children: "returned" }), _jsx("code", { children: result
|
|
24
|
+
? safeStringify(result.result)
|
|
25
|
+
: "— waiting for the job to complete —" })] })] }));
|
|
26
|
+
}
|
|
27
|
+
function TurnCard({ group, labelFor, }) {
|
|
28
|
+
const reply = group.rows.find((e) => e.kind === "llm");
|
|
29
|
+
const activations = group.rows.filter((e) => e.kind === "agent" && e.elementId);
|
|
30
|
+
const results = group.rows.filter((e) => e.kind === "vars" && e.elementId);
|
|
31
|
+
const decisions = group.rows.filter((e) => e.kind === "agent" && !e.elementId);
|
|
32
|
+
const errors = group.rows.filter((e) => e.kind === "error");
|
|
33
|
+
// Entries a handler's own trace call emits (kind "tool") and any "vars" result
|
|
34
|
+
// that never paired with an activation above would otherwise vanish once
|
|
35
|
+
// stamped with a turn — render them as plain lines within the card, in order.
|
|
36
|
+
const activatedElementIds = new Set(activations.map((a) => a.elementId));
|
|
37
|
+
const loose = group.rows
|
|
38
|
+
.filter((e) => e.kind === "tool" ||
|
|
39
|
+
(e.kind === "vars" &&
|
|
40
|
+
e.elementId &&
|
|
41
|
+
!activatedElementIds.has(e.elementId)))
|
|
42
|
+
.sort((a, b) => a.id - b.id);
|
|
43
|
+
return (_jsxs("div", { className: "timeline-turn", children: [_jsxs("div", { className: "timeline-turn-head", children: [_jsxs("span", { className: `timeline-badge ${reply?.pending ? "timeline-badge-warning" : "timeline-badge-neutral"}`, children: ["Turn ", group.turn] }), reply?.pending && _jsx("span", { className: "timeline-pending", children: "thinking\u2026" })] }), reply && _jsx("blockquote", { className: "timeline-reply", children: reply.text }), decisions.map((d) => (_jsx("div", { className: "timeline-note", children: d.text }, d.key ?? d.id))), activations.map((a) => (_jsx(ToolStep, { activation: a, result: results.find((r) => r.elementId === a.elementId), labelFor: labelFor }, a.key ?? a.id))), loose.map((e) => (_jsxs("div", { className: `log-line log-${e.kind}`, children: [e.pending ? "⏳ " : "", e.text] }, e.key ?? e.id))), errors.map((e) => (_jsxs("div", { className: "timeline-error", children: ["\u26A0 ", e.text] }, e.id)))] }));
|
|
44
|
+
}
|
|
45
|
+
export function TraceTimeline({ rows, elementStats = [], incidents = [], labelFor = (id) => id, title = "Activity", description = "Agent turns, model replies, and tool calls — read top to bottom as a story.", emptyText = "Press Run to start.", className, }) {
|
|
46
|
+
const items = useMemo(() => buildTraceItems(rows), [rows]);
|
|
47
|
+
const [copied, setCopied] = useState(false);
|
|
48
|
+
const scrollRef = useRef(null);
|
|
49
|
+
// Keep the newest step in view as the run grows, same as the flat logs this
|
|
50
|
+
// replaces.
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const el = scrollRef.current;
|
|
53
|
+
if (el)
|
|
54
|
+
el.scrollTop = el.scrollHeight;
|
|
55
|
+
}, [items]);
|
|
56
|
+
const copyJson = () => {
|
|
57
|
+
const payload = {
|
|
58
|
+
log: rows.map(({ id: _id, ...rest }) => rest),
|
|
59
|
+
elementStats,
|
|
60
|
+
incidents,
|
|
61
|
+
};
|
|
62
|
+
const text = safeStringify(payload, 2);
|
|
63
|
+
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
64
|
+
navigator.clipboard
|
|
65
|
+
.writeText(text)
|
|
66
|
+
.then(() => {
|
|
67
|
+
setCopied(true);
|
|
68
|
+
setTimeout(() => setCopied(false), 1500);
|
|
69
|
+
})
|
|
70
|
+
.catch(() => {
|
|
71
|
+
// Clipboard access can be denied (permissions policy, insecure
|
|
72
|
+
// context, an embed iframe without a clipboard-write allowance) —
|
|
73
|
+
// fail quietly rather than surfacing an error for a convenience
|
|
74
|
+
// action, not a run-blocking one.
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return (_jsxs("div", { className: className ? `timeline-panel ${className}` : "timeline-panel", children: [_jsxs("div", { className: "timeline-header", children: [_jsx("div", { className: "timeline-title", children: title }), description && (_jsx("div", { className: "timeline-description", children: description }))] }), _jsx("div", { className: "timeline-toolbar", children: _jsx("button", { type: "button", onClick: copyJson, children: copied ? "Copied!" : "Copy run as JSON" }) }), _jsx("div", { className: "timeline", ref: scrollRef, children: items.length === 0 ? (_jsx("div", { className: "log-empty", children: emptyText })) : (items.map((item) => isTraceTurnGroup(item) ? (_jsx(TurnCard, { group: item, labelFor: labelFor }, `turn-${item.turn}-${item.rows[0].key ?? item.rows[0].id}`)) : (_jsxs("div", { className: `log-line log-${item.kind}`, children: [item.pending ? "⏳ " : "", item.text] }, item.key ?? item.id)))) }), (elementStats.length > 0 || incidents.length > 0) && (_jsxs("div", { className: "timeline-engine-view", children: [elementStats.length > 0 && (_jsxs("div", { className: "timeline-stats", children: [_jsx("span", { className: "timeline-kv-label", children: "Element completion" }), _jsx("ul", { children: elementStats
|
|
79
|
+
.filter((s) => s.completed > 0 || (s.active ?? 0) > 0)
|
|
80
|
+
.map((s) => (_jsxs("li", { children: [_jsx("code", { children: labelFor(s.elementId) || s.elementId }), " ", "completed ", s.completed, s.active ? `, ${s.active} active` : ""] }, s.elementId))) })] })), incidents.length > 0 && (_jsxs("div", { className: "timeline-incidents", children: [_jsx("span", { className: "timeline-kv-label", children: "Incidents" }), _jsx("ul", { children: incidents.map((inc, i) => (_jsxs("li", { children: [_jsx("code", { children: labelFor(inc.elementId) || inc.elementId }), " \u2014", " ", inc.reason] }, `${inc.elementId}-${i}`))) })] }))] }))] }));
|
|
81
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,7 @@ export { useBojtos, type UseBojtosOptions, type BojtosControls, type BojtosPhase
|
|
|
2
2
|
export { Bojtos, type BojtosProps, type TraceEvent } from "./Bojtos.js";
|
|
3
3
|
export { OrderFulfillmentDemo, ORDER_FULFILLMENT_BPMN, orderFulfillmentWorkers, } from "./examples/orderFulfillment.js";
|
|
4
4
|
export { BpmnRuntimeView, type BpmnRuntimeViewProps, } from "./BpmnRuntimeView.js";
|
|
5
|
-
export {
|
|
6
|
-
export
|
|
5
|
+
export { TraceTimeline, type TraceTimelineProps } from "./TraceTimeline.js";
|
|
6
|
+
export { describeRunState, markerKey, bpmnKey, resourceList, capEvents, } from "./runState.js";
|
|
7
|
+
export { JobFailure, settleReason, unhandledJobTypes, buildTraceItems, isTraceTurnGroup, foldEngineEvents, traceEntriesToRows, type JobHandler, type JobResult, type AgentHandler, type DispatchOptions, type DispatchResult, type RoundResult, type SettleReason, } from "@nanobpm/bojtos-kit";
|
|
8
|
+
export type { BojtosSession, Snapshot, InstanceDto, JobDto, ActivatedJob, IncidentDto, TimerDto, UserTaskDto, MessageSubscriptionDto, SignalSubscriptionDto, ElementStatDto, SequenceFlowDto, DecisionInstanceDto, ActiveEl, ActivateInstruction, AgentActivation, AgentResult, WasmEvent, TraceRowKind, TraceEntry, TraceRow, TraceTurnGroup, TraceItem, TraceAdapter, } from "@nanobpm/bojtos-kit";
|
package/dist/index.js
CHANGED
|
@@ -7,4 +7,8 @@ export { useBojtos, } from "./useBojtos.js";
|
|
|
7
7
|
export { Bojtos } from "./Bojtos.js";
|
|
8
8
|
export { OrderFulfillmentDemo, ORDER_FULFILLMENT_BPMN, orderFulfillmentWorkers, } from "./examples/orderFulfillment.js";
|
|
9
9
|
export { BpmnRuntimeView, } from "./BpmnRuntimeView.js";
|
|
10
|
-
|
|
10
|
+
// The shared activity log (#9). Trace-only imports tree-shake bpmn-js out —
|
|
11
|
+
// TraceTimeline imports only the kit + React, never BpmnRuntimeView.
|
|
12
|
+
export { TraceTimeline } from "./TraceTimeline.js";
|
|
13
|
+
export { describeRunState, markerKey, bpmnKey, resourceList, capEvents, } from "./runState.js";
|
|
14
|
+
export { JobFailure, settleReason, unhandledJobTypes, buildTraceItems, isTraceTurnGroup, foldEngineEvents, traceEntriesToRows, } from "@nanobpm/bojtos-kit";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers behind the React binding's decisions.
|
|
3
|
+
*
|
|
4
|
+
* Kept out of the component and the hook — and out of any module that imports
|
|
5
|
+
* bpmn-js — so they can be tested without a DOM or the peer dependency. The
|
|
6
|
+
* component and hook hold rendering and lifecycle; what counts as a change, how
|
|
7
|
+
* a run reads aloud, which resources to deploy and how far to trim the log are
|
|
8
|
+
* decisions, and decisions are worth testing.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Stable key for a marker set, so unchanged ids don't re-paint the diagram.
|
|
12
|
+
*
|
|
13
|
+
* Relies on BPMN element ids being XML NCNames (so they can't contain `,` or
|
|
14
|
+
* `|`) — that invariant is what keeps the two-field join collision-free. If ids
|
|
15
|
+
* could contain the delimiters, `["a,b"],[]` and `["a"],["b"]`-style pairs
|
|
16
|
+
* would key alike.
|
|
17
|
+
*/
|
|
18
|
+
export declare function markerKey(activeIds: string[], incidentIds: string[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Content key for the `bpmn` prop, so a fresh array identity each render doesn't
|
|
21
|
+
* re-create the engine but a real content change still does. Only the array
|
|
22
|
+
* case is serialized (with a boundary-preserving `JSON.stringify`, not a
|
|
23
|
+
* `join` — a delimiter join lets two different resource arrays collapse to one
|
|
24
|
+
* key when a resource borders/contains the delimiter, silently missing a real
|
|
25
|
+
* change). A lone string can't have array-boundary collisions and React deps
|
|
26
|
+
* already compare strings by value, so it passes through untouched — no needless
|
|
27
|
+
* re-walk of potentially large BPMN XML each render.
|
|
28
|
+
*/
|
|
29
|
+
export declare function bpmnKey(bpmn: string | string[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* Normalize the `bpmn` prop to an ordered resource list for deployment. Deploy
|
|
32
|
+
* order is significant: a later resource can reference an earlier one (a call
|
|
33
|
+
* activity's child), so the array order is preserved verbatim.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resourceList(bpmn: string | string[]): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Trim an event log to the consumer's cap, keeping the most recent events.
|
|
38
|
+
* `undefined` or a negative cap means "no cap"; `0` means "keep nothing". The
|
|
39
|
+
* input array is never mutated.
|
|
40
|
+
*/
|
|
41
|
+
export declare function capEvents<T>(all: T[], cap: number | undefined): T[];
|
|
42
|
+
/**
|
|
43
|
+
* A one-line description of run state, for the diagram's live region. The token
|
|
44
|
+
* and incident highlights are purely visual; this is the same information as
|
|
45
|
+
* text.
|
|
46
|
+
*/
|
|
47
|
+
export declare function describeRunState(activeIds: string[], incidentIds: string[], name?: (id: string) => string): string;
|
package/dist/runState.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers behind the React binding's decisions.
|
|
3
|
+
*
|
|
4
|
+
* Kept out of the component and the hook — and out of any module that imports
|
|
5
|
+
* bpmn-js — so they can be tested without a DOM or the peer dependency. The
|
|
6
|
+
* component and hook hold rendering and lifecycle; what counts as a change, how
|
|
7
|
+
* a run reads aloud, which resources to deploy and how far to trim the log are
|
|
8
|
+
* decisions, and decisions are worth testing.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Stable key for a marker set, so unchanged ids don't re-paint the diagram.
|
|
12
|
+
*
|
|
13
|
+
* Relies on BPMN element ids being XML NCNames (so they can't contain `,` or
|
|
14
|
+
* `|`) — that invariant is what keeps the two-field join collision-free. If ids
|
|
15
|
+
* could contain the delimiters, `["a,b"],[]` and `["a"],["b"]`-style pairs
|
|
16
|
+
* would key alike.
|
|
17
|
+
*/
|
|
18
|
+
export function markerKey(activeIds, incidentIds) {
|
|
19
|
+
return `${activeIds.join(",")}|${incidentIds.join(",")}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Content key for the `bpmn` prop, so a fresh array identity each render doesn't
|
|
23
|
+
* re-create the engine but a real content change still does. Only the array
|
|
24
|
+
* case is serialized (with a boundary-preserving `JSON.stringify`, not a
|
|
25
|
+
* `join` — a delimiter join lets two different resource arrays collapse to one
|
|
26
|
+
* key when a resource borders/contains the delimiter, silently missing a real
|
|
27
|
+
* change). A lone string can't have array-boundary collisions and React deps
|
|
28
|
+
* already compare strings by value, so it passes through untouched — no needless
|
|
29
|
+
* re-walk of potentially large BPMN XML each render.
|
|
30
|
+
*/
|
|
31
|
+
export function bpmnKey(bpmn) {
|
|
32
|
+
return Array.isArray(bpmn) ? JSON.stringify(bpmn) : bpmn;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Normalize the `bpmn` prop to an ordered resource list for deployment. Deploy
|
|
36
|
+
* order is significant: a later resource can reference an earlier one (a call
|
|
37
|
+
* activity's child), so the array order is preserved verbatim.
|
|
38
|
+
*/
|
|
39
|
+
export function resourceList(bpmn) {
|
|
40
|
+
return Array.isArray(bpmn) ? bpmn : [bpmn];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Trim an event log to the consumer's cap, keeping the most recent events.
|
|
44
|
+
* `undefined` or a negative cap means "no cap"; `0` means "keep nothing". The
|
|
45
|
+
* input array is never mutated.
|
|
46
|
+
*/
|
|
47
|
+
export function capEvents(all, cap) {
|
|
48
|
+
return cap !== undefined && cap >= 0 && all.length > cap
|
|
49
|
+
? all.slice(all.length - cap)
|
|
50
|
+
: all;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A one-line description of run state, for the diagram's live region. The token
|
|
54
|
+
* and incident highlights are purely visual; this is the same information as
|
|
55
|
+
* text.
|
|
56
|
+
*/
|
|
57
|
+
export function describeRunState(activeIds, incidentIds, name = (id) => id) {
|
|
58
|
+
const parts = [];
|
|
59
|
+
if (activeIds.length)
|
|
60
|
+
parts.push(`Running: ${activeIds.map(name).join(", ")}`);
|
|
61
|
+
if (incidentIds.length)
|
|
62
|
+
parts.push(`Incident: ${incidentIds.map(name).join(", ")}`);
|
|
63
|
+
return parts.length ? parts.join(". ") : "Nothing running";
|
|
64
|
+
}
|
package/dist/useBojtos.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { type AgentResult, type DispatchOptions, type JobHandler, type RoundResult, type Snapshot, type WasmEvent, type WasmSource } from "@nanobpm/bojtos-kit";
|
|
1
|
+
import { type ActivateInstruction, type AgentResult, type DispatchOptions, type JobHandler, type RoundResult, type Snapshot, type WasmEvent, type WasmSource } from "@nanobpm/bojtos-kit";
|
|
2
2
|
/** Lifecycle of the in-browser engine load. */
|
|
3
3
|
export type BojtosPhase = "loading" | "ready" | "error";
|
|
4
4
|
export interface UseBojtosOptions {
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The BPMN to deploy. Re-deploys on a fresh engine when it changes.
|
|
7
|
+
*
|
|
8
|
+
* Pass an array to deploy several resources into one engine — a called
|
|
9
|
+
* process alongside its parent, say. `processIds` then lists every deployable
|
|
10
|
+
* process across all of them, in deployment order.
|
|
11
|
+
*/
|
|
12
|
+
bpmn: string | string[];
|
|
7
13
|
/**
|
|
8
14
|
* Optional engine wasm source. Pass a `URL` / bytes / `WebAssembly.Module`
|
|
9
15
|
* when the default `import.meta.url` loader can't resolve the binary (the
|
|
@@ -14,6 +20,15 @@ export interface UseBojtosOptions {
|
|
|
14
20
|
* reload the module.
|
|
15
21
|
*/
|
|
16
22
|
wasm?: WasmSource;
|
|
23
|
+
/**
|
|
24
|
+
* Cap the reactive `events` log at the most recent N entries.
|
|
25
|
+
*
|
|
26
|
+
* Every command re-reads the engine's full event log into React state, so a
|
|
27
|
+
* long-running demo copies an ever-growing array on each step. Set this when
|
|
28
|
+
* a page runs for a while and only shows a tail; leave it unset to keep the
|
|
29
|
+
* whole log, which stays the default so existing consumers are unaffected.
|
|
30
|
+
*/
|
|
31
|
+
maxEvents?: number;
|
|
17
32
|
}
|
|
18
33
|
export interface BojtosControls {
|
|
19
34
|
phase: BojtosPhase;
|
|
@@ -46,6 +61,58 @@ export interface BojtosControls {
|
|
|
46
61
|
correlateMessage(messageName: string, correlationKey: string, variablesJson: string): Snapshot | null;
|
|
47
62
|
/** Advance the virtual clock. */
|
|
48
63
|
advanceTime(byMs: number): Snapshot | null;
|
|
64
|
+
/**
|
|
65
|
+
* Throw a BPMN business error from a waiting job: interrupts the activity via
|
|
66
|
+
* a matching error boundary/event-subprocess catch, or raises an incident if
|
|
67
|
+
* uncaught. The job is consumed either way.
|
|
68
|
+
*/
|
|
69
|
+
throwError(jobKey: string, errorCode: string, errorMessage: string): Snapshot | null;
|
|
70
|
+
/**
|
|
71
|
+
* Set a job's remaining retries. Used to recover a job parked on a no-retries
|
|
72
|
+
* incident before resolving that incident; does not itself unblock the job.
|
|
73
|
+
*/
|
|
74
|
+
updateRetries(jobKey: string, retries: number): Snapshot | null;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve an open incident by key, retrying the work that failed. Pair with
|
|
77
|
+
* {@link updateRetries} to make a failed job activatable again — the
|
|
78
|
+
* incident/retry loop a demo needs to show recovery.
|
|
79
|
+
*/
|
|
80
|
+
resolveIncident(incidentKey: string): Snapshot | null;
|
|
81
|
+
/**
|
|
82
|
+
* Merge variables into a scope (a process-instance or element-instance key).
|
|
83
|
+
* With `local`, they are written strictly into that scope; otherwise they
|
|
84
|
+
* propagate up to the nearest ancestor defining each name.
|
|
85
|
+
*/
|
|
86
|
+
setVariables(scopeKey: string, variablesJson: string, local: boolean): Snapshot | null;
|
|
87
|
+
/** Broadcast a signal by name to every matching open subscription. */
|
|
88
|
+
broadcastSignal(signalName: string, variablesJson: string): Snapshot | null;
|
|
89
|
+
/** Cancel (terminate) a running process instance. */
|
|
90
|
+
cancelInstance(instanceKey: string): Snapshot | null;
|
|
91
|
+
/**
|
|
92
|
+
* Modify a running instance: terminate element instances and/or activate new
|
|
93
|
+
* ones (Zeebe "modify process instance").
|
|
94
|
+
*/
|
|
95
|
+
modify(instanceKey: string, activateInstructions: ActivateInstruction[], terminateElementInstanceKeys: string[]): Snapshot | null;
|
|
96
|
+
/**
|
|
97
|
+
* Complete a waiting user task, merging output variables.
|
|
98
|
+
*
|
|
99
|
+
* A `userTask` produces no job, so the dispatch loop cannot advance one: this
|
|
100
|
+
* is the only way a model with a human step reaches its end event. Drive it
|
|
101
|
+
* from `snapshot.userTasks`.
|
|
102
|
+
*/
|
|
103
|
+
completeUserTask(userTaskKey: string, variablesJson: string): Snapshot | null;
|
|
104
|
+
/**
|
|
105
|
+
* Assign a user task. With `allowOverride` false the command is rejected if
|
|
106
|
+
* the task already has an assignee.
|
|
107
|
+
*/
|
|
108
|
+
assignUserTask(userTaskKey: string, assignee: string, allowOverride: boolean): Snapshot | null;
|
|
109
|
+
/** Clear a user task's assignee. */
|
|
110
|
+
unassignUserTask(userTaskKey: string): Snapshot | null;
|
|
111
|
+
/**
|
|
112
|
+
* Update a user task's attributes from a JSON changeset (`candidateGroups`,
|
|
113
|
+
* `candidateUsers`, `dueDate`, `followUpDate`, `priority`).
|
|
114
|
+
*/
|
|
115
|
+
updateUserTask(userTaskKey: string, changesetJson: string): Snapshot | null;
|
|
49
116
|
/**
|
|
50
117
|
* Run the registered worker handlers until the process settles (activate →
|
|
51
118
|
* handler → complete/fail), then reflect the resulting snapshot/events.
|
|
@@ -76,4 +143,4 @@ export interface BojtosControls {
|
|
|
76
143
|
* test-run panel is its first consumer (§8 step 2 — dogfooding is the acceptance
|
|
77
144
|
* test).
|
|
78
145
|
*/
|
|
79
|
-
export declare function useBojtos({ bpmn, wasm }: UseBojtosOptions): BojtosControls;
|
|
146
|
+
export declare function useBojtos({ bpmn, wasm, maxEvents, }: UseBojtosOptions): BojtosControls;
|
package/dist/useBojtos.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { createBojtosSession, dispatchRound, dispatchWorkers, } from "@nanobpm/bojtos-kit";
|
|
3
|
+
import { bpmnKey, capEvents, resourceList } from "./runState.js";
|
|
3
4
|
/**
|
|
4
5
|
* React binding over a headless {@link BojtosSession}: owns the engine's
|
|
5
6
|
* lifecycle and the reactive `snapshot` / `events` / `processIds` state, and
|
|
@@ -11,7 +12,7 @@ import { createBojtosSession, dispatchRound, dispatchWorkers, } from "@nanobpm/b
|
|
|
11
12
|
* test-run panel is its first consumer (§8 step 2 — dogfooding is the acceptance
|
|
12
13
|
* test).
|
|
13
14
|
*/
|
|
14
|
-
export function useBojtos({ bpmn, wasm }) {
|
|
15
|
+
export function useBojtos({ bpmn, wasm, maxEvents, }) {
|
|
15
16
|
const sessionRef = useRef(null);
|
|
16
17
|
const [phase, setPhase] = useState("loading");
|
|
17
18
|
const [error, setError] = useState(null);
|
|
@@ -23,13 +24,32 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
23
24
|
// identity each render must not re-create the session.
|
|
24
25
|
const wasmRef = useRef(wasm);
|
|
25
26
|
wasmRef.current = wasm;
|
|
27
|
+
// An array prop has a fresh identity every render, which would re-create the
|
|
28
|
+
// engine on each one. Key the deploy effect on the content instead — see
|
|
29
|
+
// `bpmnKey` for why this is a boundary-preserving serialization, not a join.
|
|
30
|
+
const deployKey = bpmnKey(bpmn);
|
|
31
|
+
const bpmnRef = useRef(bpmn);
|
|
32
|
+
bpmnRef.current = bpmn;
|
|
33
|
+
// Trim the reactive event log when the consumer asked for a cap.
|
|
34
|
+
const maxEventsRef = useRef(maxEvents);
|
|
35
|
+
maxEventsRef.current = maxEvents;
|
|
36
|
+
const readEvents = useCallback((session) => {
|
|
37
|
+
return capEvents(session.events(), maxEventsRef.current);
|
|
38
|
+
}, []);
|
|
26
39
|
const deployInto = useCallback((session) => {
|
|
27
|
-
const
|
|
28
|
-
|
|
40
|
+
const resources = resourceList(bpmnRef.current);
|
|
41
|
+
// Deploy in order, collecting every deployable process id. A later
|
|
42
|
+
// resource can reference an earlier one (a call activity's child).
|
|
43
|
+
const ids = [];
|
|
44
|
+
for (const xml of resources)
|
|
45
|
+
ids.push(...session.deploy(xml).processIds);
|
|
46
|
+
setProcessIds(ids);
|
|
29
47
|
setSnapshot(null);
|
|
30
48
|
setEvents([]);
|
|
31
49
|
setError(null);
|
|
32
|
-
},
|
|
50
|
+
},
|
|
51
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
+
[deployKey]);
|
|
33
53
|
useEffect(() => {
|
|
34
54
|
let cancelled = false;
|
|
35
55
|
// A new diagram means a fresh engine: drop back to `loading` and clear the
|
|
@@ -81,7 +101,7 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
81
101
|
try {
|
|
82
102
|
const snap = fn(session);
|
|
83
103
|
setSnapshot(snap);
|
|
84
|
-
setEvents(session
|
|
104
|
+
setEvents(readEvents(session));
|
|
85
105
|
setError(null);
|
|
86
106
|
return snap;
|
|
87
107
|
}
|
|
@@ -96,6 +116,17 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
96
116
|
const failJob = useCallback((jobKey, retries, message) => run((s) => s.failJob(jobKey, retries, message)), [run]);
|
|
97
117
|
const advanceTime = useCallback((byMs) => run((s) => s.advanceTime(byMs)), [run]);
|
|
98
118
|
const correlateMessage = useCallback((messageName, correlationKey, variablesJson) => run((s) => s.correlateMessage(messageName, correlationKey, variablesJson)), [run]);
|
|
119
|
+
const throwError = useCallback((jobKey, errorCode, errorMessage) => run((s) => s.throwError(jobKey, errorCode, errorMessage)), [run]);
|
|
120
|
+
const updateRetries = useCallback((jobKey, retries) => run((s) => s.updateRetries(jobKey, retries)), [run]);
|
|
121
|
+
const resolveIncident = useCallback((incidentKey) => run((s) => s.resolveIncident(incidentKey)), [run]);
|
|
122
|
+
const setVariables = useCallback((scopeKey, variablesJson, local) => run((s) => s.setVariables(scopeKey, variablesJson, local)), [run]);
|
|
123
|
+
const broadcastSignal = useCallback((signalName, variablesJson) => run((s) => s.broadcastSignal(signalName, variablesJson)), [run]);
|
|
124
|
+
const cancelInstance = useCallback((instanceKey) => run((s) => s.cancelInstance(instanceKey)), [run]);
|
|
125
|
+
const modify = useCallback((instanceKey, activateInstructions, terminateElementInstanceKeys) => run((s) => s.modify(instanceKey, activateInstructions, terminateElementInstanceKeys)), [run]);
|
|
126
|
+
const completeUserTask = useCallback((userTaskKey, variablesJson) => run((s) => s.completeUserTask(userTaskKey, variablesJson)), [run]);
|
|
127
|
+
const assignUserTask = useCallback((userTaskKey, assignee, allowOverride) => run((s) => s.assignUserTask(userTaskKey, assignee, allowOverride)), [run]);
|
|
128
|
+
const unassignUserTask = useCallback((userTaskKey) => run((s) => s.unassignUserTask(userTaskKey)), [run]);
|
|
129
|
+
const updateUserTask = useCallback((userTaskKey, changesetJson) => run((s) => s.updateUserTask(userTaskKey, changesetJson)), [run]);
|
|
99
130
|
const runWorkers = useCallback(async (workers, opts) => {
|
|
100
131
|
const session = sessionRef.current;
|
|
101
132
|
if (!session)
|
|
@@ -107,7 +138,7 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
107
138
|
if (sessionRef.current !== session)
|
|
108
139
|
return null;
|
|
109
140
|
setSnapshot(settled);
|
|
110
|
-
setEvents(session
|
|
141
|
+
setEvents(readEvents(session));
|
|
111
142
|
setError(null);
|
|
112
143
|
return settled;
|
|
113
144
|
}
|
|
@@ -117,7 +148,7 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
117
148
|
// Reflect whatever state the engine reached before the drain aborted
|
|
118
149
|
// (e.g. the maxRounds guard) so the view isn't left stale.
|
|
119
150
|
setSnapshot(session.snapshot());
|
|
120
|
-
setEvents(session
|
|
151
|
+
setEvents(readEvents(session));
|
|
121
152
|
setError(String(e));
|
|
122
153
|
return null;
|
|
123
154
|
}
|
|
@@ -132,7 +163,7 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
132
163
|
if (sessionRef.current !== session)
|
|
133
164
|
return null;
|
|
134
165
|
setSnapshot(round.snapshot);
|
|
135
|
-
setEvents(session
|
|
166
|
+
setEvents(readEvents(session));
|
|
136
167
|
setError(null);
|
|
137
168
|
return round;
|
|
138
169
|
}
|
|
@@ -140,7 +171,7 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
140
171
|
if (sessionRef.current !== session)
|
|
141
172
|
return null;
|
|
142
173
|
setSnapshot(session.snapshot());
|
|
143
|
-
setEvents(session
|
|
174
|
+
setEvents(readEvents(session));
|
|
144
175
|
setError(String(e));
|
|
145
176
|
return null;
|
|
146
177
|
}
|
|
@@ -172,6 +203,17 @@ export function useBojtos({ bpmn, wasm }) {
|
|
|
172
203
|
failJob,
|
|
173
204
|
advanceTime,
|
|
174
205
|
correlateMessage,
|
|
206
|
+
throwError,
|
|
207
|
+
updateRetries,
|
|
208
|
+
resolveIncident,
|
|
209
|
+
setVariables,
|
|
210
|
+
broadcastSignal,
|
|
211
|
+
cancelInstance,
|
|
212
|
+
modify,
|
|
213
|
+
completeUserTask,
|
|
214
|
+
assignUserTask,
|
|
215
|
+
unassignUserTask,
|
|
216
|
+
updateUserTask,
|
|
175
217
|
runWorkers,
|
|
176
218
|
stepWorkers,
|
|
177
219
|
reset,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/bojtos-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "React binding for the Bojtos in-browser BPMN demo framework (ADR 0043): the useBojtos hook (owns the engine session + reactive snapshot/event state) and the <BpmnRuntimeView> live token/incident diagram. Built on @nanobpm/bojtos-kit; the console test-run panel is its first consumer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,10 +27,12 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsc -p tsconfig.json",
|
|
29
29
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
30
|
-
"prepack": "npm run build"
|
|
30
|
+
"prepack": "npm run build",
|
|
31
|
+
"test": "npm run build && npm run test:ci",
|
|
32
|
+
"test:ci": "node --experimental-strip-types --test test/*.test.ts"
|
|
31
33
|
},
|
|
32
34
|
"dependencies": {
|
|
33
|
-
"@nanobpm/bojtos-kit": "^0.
|
|
35
|
+
"@nanobpm/bojtos-kit": "^0.6.0"
|
|
34
36
|
},
|
|
35
37
|
"peerDependencies": {
|
|
36
38
|
"bpmn-js": ">=17",
|