@bpmnkit/engine 0.1.13 → 0.1.15

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 CHANGED
@@ -124,6 +124,8 @@ const instance = engine.start("my-process", {}, {
124
124
  | [`@bpmnkit/cli-sdk`](https://www.npmjs.com/package/@bpmnkit/cli-sdk) | Plugin authoring SDK for the casen CLI |
125
125
  | [`@bpmnkit/create-casen-plugin`](https://www.npmjs.com/package/@bpmnkit/create-casen-plugin) | Scaffold a new casen CLI plugin in seconds |
126
126
  | [`@bpmnkit/casen-report`](https://www.npmjs.com/package/@bpmnkit/casen-report) | HTML reports from Camunda 8 incident and SLA data |
127
+ | [`@bpmnkit/casen-worker-http`](https://www.npmjs.com/package/@bpmnkit/casen-worker-http) | Example HTTP worker plugin — completes jobs with live JSONPlaceholder API data |
128
+ | [`@bpmnkit/casen-worker-ai`](https://www.npmjs.com/package/@bpmnkit/casen-worker-ai) | AI task worker — classify, summarize, extract, and decide using Claude |
127
129
 
128
130
  ## License
129
131
 
package/dist/index.d.ts CHANGED
@@ -7,4 +7,6 @@ export { evaluateDecision } from "./dmn.js";
7
7
  export { scheduleTimer, parseDurationMs } from "./timers.js";
8
8
  export { parseZeebeExt } from "./zeebe.js";
9
9
  export type { ParsedZeebeExt } from "./zeebe.js";
10
+ export { runScenario } from "./scenario.js";
11
+ export type { ProcessScenario, ScenarioMock, ScenarioExpect, ScenarioResult } from "./scenario.js";
10
12
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -4,4 +4,5 @@ export { VariableStore } from "./variables.js";
4
4
  export { evaluateDecision } from "./dmn.js";
5
5
  export { scheduleTimer, parseDurationMs } from "./timers.js";
6
6
  export { parseZeebeExt } from "./zeebe.js";
7
+ export { runScenario } from "./scenario.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,63 @@
1
+ import type { BpmnDefinitions } from "@bpmnkit/core";
2
+ import type { Engine } from "./engine.js";
3
+ /** A worker mock for a specific task type. */
4
+ export interface ScenarioMock {
5
+ /** Variables to output when the task completes. */
6
+ outputs?: Record<string, unknown>;
7
+ /** If set, the worker throws this error instead of completing. */
8
+ error?: string;
9
+ }
10
+ /** Expected results that the scenario should assert. */
11
+ export interface ScenarioExpect {
12
+ /** Ordered list of element IDs that must be visited (in order, may be a subset). */
13
+ path?: string[];
14
+ /** Variables that must be present in the final process state. */
15
+ variables?: Record<string, unknown>;
16
+ }
17
+ /** A single test scenario for a BPMN process. */
18
+ export interface ProcessScenario {
19
+ id: string;
20
+ name: string;
21
+ /** Process ID to run. Defaults to the first process in the definitions. */
22
+ processId?: string;
23
+ /** Initial variables passed to the process. */
24
+ inputs?: Record<string, unknown>;
25
+ /** Job worker mocks keyed by task type. */
26
+ mocks?: Record<string, ScenarioMock>;
27
+ /** Assertions to check after the run. */
28
+ expect?: ScenarioExpect;
29
+ }
30
+ /** Result of a single scenario run. */
31
+ export interface ScenarioResult {
32
+ scenarioId: string;
33
+ scenarioName: string;
34
+ passed: boolean;
35
+ /** Elements visited (type: element:entered) in order. */
36
+ visitedElements: string[];
37
+ /** Final variable state. */
38
+ finalVariables: Record<string, unknown>;
39
+ /** Errors collected during the run. */
40
+ errors: Array<{
41
+ elementId?: string;
42
+ message: string;
43
+ }>;
44
+ /** Assertion failures, each describing what was expected vs. actual. */
45
+ failures: Array<{
46
+ field: string;
47
+ expected: unknown;
48
+ actual: unknown;
49
+ }>;
50
+ /** Time taken in milliseconds. */
51
+ durationMs: number;
52
+ }
53
+ /**
54
+ * Run a single test scenario against a deployed BPMN definition.
55
+ * Registers mock workers, runs the process, then checks assertions.
56
+ *
57
+ * @param engine An `Engine` instance (fresh or shared — caller decides).
58
+ * @param defs BPMN definitions to deploy.
59
+ * @param scenario The scenario to run.
60
+ * @param timeoutMs How long to wait for the process to complete (default 5 s).
61
+ */
62
+ export declare function runScenario(engine: Engine, defs: BpmnDefinitions, scenario: ProcessScenario, timeoutMs?: number): Promise<ScenarioResult>;
63
+ //# sourceMappingURL=scenario.d.ts.map
@@ -0,0 +1,150 @@
1
+ // ── Runner ─────────────────────────────────────────────────────────────────────
2
+ const DEFAULT_TIMEOUT_MS = 5_000;
3
+ /**
4
+ * Run a single test scenario against a deployed BPMN definition.
5
+ * Registers mock workers, runs the process, then checks assertions.
6
+ *
7
+ * @param engine An `Engine` instance (fresh or shared — caller decides).
8
+ * @param defs BPMN definitions to deploy.
9
+ * @param scenario The scenario to run.
10
+ * @param timeoutMs How long to wait for the process to complete (default 5 s).
11
+ */
12
+ export function runScenario(engine, defs, scenario, timeoutMs = DEFAULT_TIMEOUT_MS) {
13
+ return new Promise((resolve) => {
14
+ const startMs = Date.now();
15
+ engine.deploy({ bpmn: defs });
16
+ const processId = scenario.processId ?? defs.processes[0]?.id;
17
+ if (processId === undefined) {
18
+ resolve({
19
+ scenarioId: scenario.id,
20
+ scenarioName: scenario.name,
21
+ passed: false,
22
+ visitedElements: [],
23
+ finalVariables: {},
24
+ errors: [{ message: "No process found in definitions." }],
25
+ failures: [{ field: "processId", expected: "a deployed process", actual: undefined }],
26
+ durationMs: Date.now() - startMs,
27
+ });
28
+ return;
29
+ }
30
+ // Register mock workers
31
+ const unregisterWorkers = [];
32
+ for (const [taskType, mock] of Object.entries(scenario.mocks ?? {})) {
33
+ const off = engine.registerJobWorker(taskType, (job) => {
34
+ if (mock.error !== undefined) {
35
+ job.fail(mock.error);
36
+ }
37
+ else {
38
+ job.complete(mock.outputs ?? {});
39
+ }
40
+ });
41
+ unregisterWorkers.push(off);
42
+ }
43
+ const visitedElements = [];
44
+ const variableState = new Map();
45
+ const errors = [];
46
+ let settled = false;
47
+ let timeoutHandle;
48
+ function finish(finalVars) {
49
+ if (settled)
50
+ return;
51
+ settled = true;
52
+ clearTimeout(timeoutHandle);
53
+ for (const off of unregisterWorkers)
54
+ off();
55
+ // Merge variable state with any final vars from process:completed
56
+ for (const [k, v] of Object.entries(finalVars))
57
+ variableState.set(k, v);
58
+ const finalVariables = Object.fromEntries(variableState);
59
+ // Evaluate assertions
60
+ const failures = [];
61
+ if (scenario.expect?.path !== undefined) {
62
+ const expectedPath = scenario.expect.path;
63
+ // Check that each expected element appears in order within visitedElements
64
+ let cursor = 0;
65
+ for (const expectedId of expectedPath) {
66
+ const idx = visitedElements.indexOf(expectedId, cursor);
67
+ if (idx === -1) {
68
+ failures.push({
69
+ field: `path[${expectedPath.indexOf(expectedId)}]`,
70
+ expected: expectedId,
71
+ actual: `not found after position ${cursor} in [${visitedElements.join(", ")}]`,
72
+ });
73
+ }
74
+ else {
75
+ cursor = idx + 1;
76
+ }
77
+ }
78
+ }
79
+ if (scenario.expect?.variables !== undefined) {
80
+ for (const [key, expectedValue] of Object.entries(scenario.expect.variables)) {
81
+ const actualValue = finalVariables[key];
82
+ const match = JSON.stringify(actualValue) === JSON.stringify(expectedValue);
83
+ if (!match) {
84
+ failures.push({
85
+ field: `variables.${key}`,
86
+ expected: expectedValue,
87
+ actual: actualValue,
88
+ });
89
+ }
90
+ }
91
+ }
92
+ resolve({
93
+ scenarioId: scenario.id,
94
+ scenarioName: scenario.name,
95
+ passed: failures.length === 0,
96
+ visitedElements,
97
+ finalVariables,
98
+ errors,
99
+ failures,
100
+ durationMs: Date.now() - startMs,
101
+ });
102
+ }
103
+ timeoutHandle = setTimeout(() => {
104
+ if (settled)
105
+ return;
106
+ errors.push({ message: `Scenario timed out after ${timeoutMs}ms` });
107
+ finish({});
108
+ }, timeoutMs);
109
+ let instance;
110
+ try {
111
+ instance = engine.start(processId, scenario.inputs ?? {});
112
+ }
113
+ catch (err) {
114
+ clearTimeout(timeoutHandle);
115
+ for (const off of unregisterWorkers)
116
+ off();
117
+ const msg = err instanceof Error ? err.message : String(err);
118
+ resolve({
119
+ scenarioId: scenario.id,
120
+ scenarioName: scenario.name,
121
+ passed: false,
122
+ visitedElements: [],
123
+ finalVariables: {},
124
+ errors: [{ message: msg }],
125
+ failures: [{ field: "start", expected: "process to start", actual: msg }],
126
+ durationMs: Date.now() - startMs,
127
+ });
128
+ return;
129
+ }
130
+ instance.onChange((evt) => {
131
+ if (evt.type === "element:entered") {
132
+ visitedElements.push(evt.elementId);
133
+ }
134
+ else if (evt.type === "variable:set") {
135
+ variableState.set(evt.name, evt.value);
136
+ }
137
+ else if (evt.type === "element:failed") {
138
+ errors.push({ elementId: evt.elementId, message: evt.error });
139
+ }
140
+ else if (evt.type === "process:failed") {
141
+ errors.push({ message: evt.error });
142
+ finish({});
143
+ }
144
+ else if (evt.type === "process:completed") {
145
+ finish(evt.variables);
146
+ }
147
+ });
148
+ });
149
+ }
150
+ //# sourceMappingURL=scenario.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpmnkit/engine",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -17,7 +17,7 @@
17
17
  "dist/**/*.d.ts"
18
18
  ],
19
19
  "dependencies": {
20
- "@bpmnkit/core": "0.0.14",
20
+ "@bpmnkit/core": "0.0.16",
21
21
  "@bpmnkit/feel": "0.0.13"
22
22
  },
23
23
  "description": "Lightweight BPMN 2.0 process execution engine for browsers and Node.js — zero dependencies",