@nanobpm/workflow 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 +19 -1
- package/dist/client.d.ts +113 -25
- package/dist/client.js +105 -66
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/worker.d.ts +37 -11
- package/dist/worker.js +77 -58
- package/package.json +8 -5
- package/src/client.ts +188 -103
- package/src/index.ts +8 -2
- package/src/worker.ts +108 -62
package/README.md
CHANGED
|
@@ -183,7 +183,25 @@ once.
|
|
|
183
183
|
| `externalJobTypes(flow)` | The job types of a flow's external `task` steps (each overridable per-step via `w.task(name, { jobType })`). |
|
|
184
184
|
| `WorkflowClient` | `deploy`, `start`, `signal`, `getInstance` over REST v2. |
|
|
185
185
|
| `Worker` | Generic job runtime; routes job types → handlers, hosts the replay loop. |
|
|
186
|
-
| `toBpmn(workflow)` | The
|
|
186
|
+
| `toBpmn(workflow)` | The DI-less semantic BPMN the engine runs (for inspection / deployment). |
|
|
187
|
+
| `toDeployableBpmn(workflow, { layout? })` | The deployable BPMN **with an auto-generated diagram (DI)** so the deployed model is inspectable; what `deploy` sends by default. |
|
|
188
|
+
| `layoutBpmn(xml)` / `declarativeToLayoutedBpmn(flow)` | Add DI to a model with `bpmn-auto-layout`. |
|
|
189
|
+
|
|
190
|
+
### Inspectable deployments (diagram layout)
|
|
191
|
+
|
|
192
|
+
`client.deploy(flow)` deploys the model **with an auto-generated diagram
|
|
193
|
+
interchange (DI)** so it opens rendered and inspectable in a modeller/Operate
|
|
194
|
+
rather than as a blank canvas. DI is generated with the optional peer dependency
|
|
195
|
+
[`bpmn-auto-layout`](https://www.npmjs.com/package/bpmn-auto-layout):
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
npm i bpmn-auto-layout
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
If it is not installed, `deploy` degrades gracefully — it warns once and deploys
|
|
202
|
+
the DI-less model. Pass `deploy(flow, { layout: false })` to skip layout
|
|
203
|
+
deliberately. The semantic model stays authoritative; DI is derived and
|
|
204
|
+
regenerable.
|
|
187
205
|
|
|
188
206
|
See [ADR 0044](../docs/adr/0044-code-first-durable-orchestration.md) for the design
|
|
189
207
|
rationale and [ADR 0047](../docs/adr/0047-declarative-flow-control-and-typed-envelopes.md)
|
package/dist/client.d.ts
CHANGED
|
@@ -1,36 +1,126 @@
|
|
|
1
1
|
import type { DeclarativeFlow, DeployResult, Job, JsonObject, StartResult, Workflow } from "./types.js";
|
|
2
2
|
/** Render a workflow (either surface) to its executable BPMN model. */
|
|
3
3
|
export declare function toBpmn(wf: Workflow): string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Render a workflow to *deployable* BPMN — the executable model plus diagram
|
|
6
|
+
* interchange (DI), auto-generated with `bpmn-auto-layout` — so the deployed
|
|
7
|
+
* process opens rendered and inspectable in a modeller/Operate rather than as a
|
|
8
|
+
* blank canvas. The semantic model stays authoritative; DI is derived.
|
|
9
|
+
*
|
|
10
|
+
* DI generation needs the optional peer dependency `bpmn-auto-layout`. When it is
|
|
11
|
+
* absent, this degrades gracefully: it warns once and returns the DI-less model
|
|
12
|
+
* so `deploy` still works everywhere. Pass `{ layout: false }` to skip layout
|
|
13
|
+
* deliberately. A genuine layout failure (the dep is installed but errors) is
|
|
14
|
+
* surfaced, not swallowed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function toDeployableBpmn(wf: Workflow, opts?: {
|
|
17
|
+
layout?: boolean;
|
|
18
|
+
}): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* The subset of the `@nanobpm/nano-sdk` (Camunda orchestration-cluster) client
|
|
21
|
+
* that the workflow surface uses. `createCamundaClient` returns a superset of
|
|
22
|
+
* this, so a test — or an author bringing their own transport (e.g. the embedded
|
|
23
|
+
* engine) — can inject any object satisfying it.
|
|
24
|
+
*/
|
|
25
|
+
export interface NanoSdkClient {
|
|
26
|
+
createDeployment(input: {
|
|
27
|
+
resources: File[];
|
|
28
|
+
[k: string]: unknown;
|
|
29
|
+
}, options?: unknown): Promise<Record<string, unknown>>;
|
|
30
|
+
createProcessInstance(input: {
|
|
31
|
+
processDefinitionId: string;
|
|
32
|
+
variables?: JsonObject;
|
|
33
|
+
}, options?: unknown): Promise<Record<string, unknown>>;
|
|
34
|
+
correlateMessage(input: {
|
|
35
|
+
name: string;
|
|
36
|
+
correlationKey: string;
|
|
37
|
+
variables?: JsonObject;
|
|
38
|
+
}, options?: unknown): Promise<Record<string, unknown>>;
|
|
39
|
+
getProcessInstance(input: {
|
|
40
|
+
processInstanceKey: string;
|
|
41
|
+
}, consistency: {
|
|
42
|
+
consistency: {
|
|
43
|
+
waitUpToMs: number;
|
|
44
|
+
};
|
|
45
|
+
}, options?: unknown): Promise<Record<string, unknown>>;
|
|
46
|
+
createJobWorker(cfg: JobWorkerConfig): NanoJobWorker;
|
|
47
|
+
}
|
|
48
|
+
/** Config for a nano-sdk job worker (the subset the Worker runtime sets). */
|
|
49
|
+
export interface JobWorkerConfig {
|
|
50
|
+
jobType: string;
|
|
51
|
+
jobHandler: (job: ActivatedJob) => Promise<unknown> | unknown;
|
|
52
|
+
workerName?: string;
|
|
53
|
+
maxParallelJobs?: number;
|
|
54
|
+
/** Job lock timeout in ms. */
|
|
55
|
+
jobTimeoutMs?: number;
|
|
56
|
+
/** Long-poll timeout in ms. */
|
|
57
|
+
pollTimeoutMs?: number;
|
|
58
|
+
/** Start polling immediately. The Worker runtime sets this false and starts explicitly. */
|
|
59
|
+
autoStart?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** The handle returned by `createJobWorker`. */
|
|
62
|
+
export interface NanoJobWorker {
|
|
63
|
+
start(): void;
|
|
64
|
+
stop(): void | Promise<void>;
|
|
65
|
+
stopGracefully?(opts?: {
|
|
66
|
+
waitUpToMs?: number;
|
|
67
|
+
}): Promise<void>;
|
|
10
68
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
69
|
+
/** An activated job as delivered to a nano-sdk job handler: the workflow `Job`
|
|
70
|
+
* fields plus the acknowledgement actions. */
|
|
71
|
+
export type ActivatedJob = Job & {
|
|
72
|
+
complete(variables?: JsonObject): Promise<unknown>;
|
|
73
|
+
fail(body: {
|
|
74
|
+
errorMessage: string;
|
|
75
|
+
retries?: number;
|
|
76
|
+
}): Promise<unknown>;
|
|
77
|
+
};
|
|
78
|
+
/** Options common to both ways of building a `WorkflowClient`. */
|
|
79
|
+
interface WorkflowClientCommon {
|
|
80
|
+
/** Auth token for the gateway (CAMUNDA_TOKEN). */
|
|
81
|
+
token?: string;
|
|
82
|
+
/** Transport mode passed to `createCamundaClient`: "auto" | "falcon" | "rest".
|
|
83
|
+
* Default "auto" (Falcon on a Nano server, REST elsewhere). */
|
|
84
|
+
transport?: "auto" | "falcon" | "rest";
|
|
18
85
|
}
|
|
86
|
+
/** Construct a `WorkflowClient` from **either** a `baseUrl` (a nano-sdk client is
|
|
87
|
+
* built for you) **or** a pre-built `client`. The union makes TypeScript enforce
|
|
88
|
+
* that exactly one is supplied, matching the constructor's runtime requirement. */
|
|
89
|
+
export type WorkflowClientOptions = (WorkflowClientCommon & {
|
|
90
|
+
/** Base URL of the nanobpmn gateway, e.g. `http://localhost:8080`. The
|
|
91
|
+
* nano-sdk client normalises this to the `/v2` REST address. */
|
|
92
|
+
baseUrl: string;
|
|
93
|
+
client?: never;
|
|
94
|
+
}) | (WorkflowClientCommon & {
|
|
95
|
+
/** Inject a pre-built nano-sdk client (or a compatible fake) instead of
|
|
96
|
+
* constructing one from `baseUrl`. Useful for tests, the embedded
|
|
97
|
+
* transport, and advanced authors who build the client themselves. */
|
|
98
|
+
client: NanoSdkClient;
|
|
99
|
+
baseUrl?: never;
|
|
100
|
+
});
|
|
19
101
|
export declare class WorkflowError extends Error {
|
|
20
102
|
readonly status?: number | undefined;
|
|
21
103
|
readonly body?: string | undefined;
|
|
22
104
|
constructor(message: string, status?: number | undefined, body?: string | undefined);
|
|
23
105
|
}
|
|
24
106
|
export declare class WorkflowClient {
|
|
25
|
-
|
|
26
|
-
|
|
107
|
+
/** The underlying nano-sdk client, exposed so the Worker runtime and app
|
|
108
|
+
* authors can reach the engine through the same transport (ADR 0055). The
|
|
109
|
+
* exported `NanoSdkClient` type intentionally models only the subset of
|
|
110
|
+
* methods this package uses (deploy, create, correlate, get, job workers);
|
|
111
|
+
* the runtime value is the full nano-sdk client. */
|
|
112
|
+
readonly sdk: NanoSdkClient;
|
|
27
113
|
constructor(opts: WorkflowClientOptions);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Deploy a workflow's derived BPMN model. The deployed model includes
|
|
116
|
+
* auto-generated diagram interchange (DI) so it is inspectable in a
|
|
117
|
+
* modeller/Operate; pass `{ layout: false }` to deploy the DI-less semantic
|
|
118
|
+
* model. DI needs the optional `bpmn-auto-layout` dependency — see
|
|
119
|
+
* `toDeployableBpmn` for the graceful-degradation behaviour when it is absent.
|
|
120
|
+
*/
|
|
121
|
+
deploy(wf: Workflow, opts?: {
|
|
122
|
+
layout?: boolean;
|
|
123
|
+
}): Promise<DeployResult>;
|
|
34
124
|
/**
|
|
35
125
|
* Start a workflow instance. For imperative workflows the engine variables are
|
|
36
126
|
* seeded with `{ input, journal: {}, wfDone: false }` (the replay state); for
|
|
@@ -41,10 +131,8 @@ export declare class WorkflowClient {
|
|
|
41
131
|
* unknown signal name (a typo would otherwise send an uncorrelatable message
|
|
42
132
|
* that the gateway silently drops). */
|
|
43
133
|
signal(flow: DeclarativeFlow, signalName: string, correlationKey: string, variables?: JsonObject): Promise<JsonObject>;
|
|
44
|
-
/** Fetch an instance (used by demos/tests to observe completion).
|
|
134
|
+
/** Fetch an instance (used by demos/tests to observe completion). Reads with
|
|
135
|
+
* zero-wait consistency; returns null when the instance is not (yet) visible. */
|
|
45
136
|
getInstance(processInstanceKey: string): Promise<JsonObject | null>;
|
|
46
|
-
activateJobs(type: string, opts: ActivateOptions): Promise<Job[]>;
|
|
47
|
-
completeJob(jobKey: string, variables?: JsonObject): Promise<void>;
|
|
48
|
-
failJob(jobKey: string, errorMessage: string, retries?: number): Promise<void>;
|
|
49
137
|
}
|
|
50
138
|
export {};
|
package/dist/client.js
CHANGED
|
@@ -1,14 +1,55 @@
|
|
|
1
|
-
// A
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// A client for a running nanobpmn gateway, built on `@nanobpm/nano-sdk` (the
|
|
2
|
+
// single engine-transport spine — ADR 0055). It derives a workflow's model,
|
|
3
|
+
// deploys it (with diagram interchange), starts instances, and correlates
|
|
4
|
+
// signals; the low-level job protocol used by the Worker runtime is the SDK's
|
|
5
|
+
// own job worker, reached through the exposed `sdk` client.
|
|
6
|
+
import { createCamundaClient } from "@nanobpm/nano-sdk";
|
|
4
7
|
import { declarativeToBpmn, walkNodes } from "./declarative.js";
|
|
5
8
|
import { imperativeToBpmn } from "./imperative.js";
|
|
9
|
+
import { layoutBpmn } from "./layout.js";
|
|
6
10
|
import { assertWorkflowIds, messageName } from "./xml.js";
|
|
7
11
|
/** Render a workflow (either surface) to its executable BPMN model. */
|
|
8
12
|
export function toBpmn(wf) {
|
|
9
13
|
assertWorkflowIds(wf);
|
|
10
14
|
return wf.kind === "imperative" ? imperativeToBpmn(wf) : declarativeToBpmn(wf);
|
|
11
15
|
}
|
|
16
|
+
let warnedNoLayout = false;
|
|
17
|
+
/**
|
|
18
|
+
* Render a workflow to *deployable* BPMN — the executable model plus diagram
|
|
19
|
+
* interchange (DI), auto-generated with `bpmn-auto-layout` — so the deployed
|
|
20
|
+
* process opens rendered and inspectable in a modeller/Operate rather than as a
|
|
21
|
+
* blank canvas. The semantic model stays authoritative; DI is derived.
|
|
22
|
+
*
|
|
23
|
+
* DI generation needs the optional peer dependency `bpmn-auto-layout`. When it is
|
|
24
|
+
* absent, this degrades gracefully: it warns once and returns the DI-less model
|
|
25
|
+
* so `deploy` still works everywhere. Pass `{ layout: false }` to skip layout
|
|
26
|
+
* deliberately. A genuine layout failure (the dep is installed but errors) is
|
|
27
|
+
* surfaced, not swallowed.
|
|
28
|
+
*/
|
|
29
|
+
export async function toDeployableBpmn(wf, opts = {}) {
|
|
30
|
+
const semantic = toBpmn(wf);
|
|
31
|
+
if (opts.layout === false)
|
|
32
|
+
return semantic;
|
|
33
|
+
try {
|
|
34
|
+
return await layoutBpmn(semantic);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
// Only fall back for the "optional dep not installed" case (layoutBpmn wraps
|
|
38
|
+
// it with an ERR_MODULE_NOT_FOUND cause). Any other failure is a real layout
|
|
39
|
+
// problem and must surface rather than silently deploy an uninspectable model.
|
|
40
|
+
const cause = e?.cause;
|
|
41
|
+
if (cause?.code !== "ERR_MODULE_NOT_FOUND")
|
|
42
|
+
throw e;
|
|
43
|
+
if (!warnedNoLayout) {
|
|
44
|
+
warnedNoLayout = true;
|
|
45
|
+
console.warn(`[@nanobpm/workflow] Deploying "${wf.id}" without a diagram: the optional ` +
|
|
46
|
+
'"bpmn-auto-layout" dependency is not installed, so the model will be ' +
|
|
47
|
+
"uninspectable in a modeller/Operate. Install it for diagram layout: " +
|
|
48
|
+
"npm i bpmn-auto-layout");
|
|
49
|
+
}
|
|
50
|
+
return semantic;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
12
53
|
export class WorkflowError extends Error {
|
|
13
54
|
status;
|
|
14
55
|
body;
|
|
@@ -19,44 +60,53 @@ export class WorkflowError extends Error {
|
|
|
19
60
|
this.name = "WorkflowError";
|
|
20
61
|
}
|
|
21
62
|
}
|
|
63
|
+
/** Normalise a thrown SDK/transport error into a WorkflowError, preserving an
|
|
64
|
+
* HTTP status when the SDK attached one. */
|
|
65
|
+
function asWorkflowError(e, what) {
|
|
66
|
+
if (e instanceof WorkflowError)
|
|
67
|
+
return e;
|
|
68
|
+
const err = e;
|
|
69
|
+
const status = err?.status ?? err?.response?.status;
|
|
70
|
+
return new WorkflowError(`${what} failed: ${err?.message ?? String(e)}`, status);
|
|
71
|
+
}
|
|
22
72
|
export class WorkflowClient {
|
|
23
|
-
|
|
24
|
-
|
|
73
|
+
/** The underlying nano-sdk client, exposed so the Worker runtime and app
|
|
74
|
+
* authors can reach the engine through the same transport (ADR 0055). The
|
|
75
|
+
* exported `NanoSdkClient` type intentionally models only the subset of
|
|
76
|
+
* methods this package uses (deploy, create, correlate, get, job workers);
|
|
77
|
+
* the runtime value is the full nano-sdk client. */
|
|
78
|
+
sdk;
|
|
25
79
|
constructor(opts) {
|
|
80
|
+
if (opts?.client) {
|
|
81
|
+
this.sdk = opts.client;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
26
84
|
if (!opts?.baseUrl)
|
|
27
|
-
throw new Error("WorkflowClient needs a baseUrl");
|
|
28
|
-
this.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const res = await this.send(path, init, what);
|
|
36
|
-
return (await res.json());
|
|
85
|
+
throw new Error("WorkflowClient needs a baseUrl or a client");
|
|
86
|
+
this.sdk = createCamundaClient({
|
|
87
|
+
config: {
|
|
88
|
+
CAMUNDA_REST_ADDRESS: opts.baseUrl.replace(/\/+$/, ""),
|
|
89
|
+
...(opts.token ? { CAMUNDA_TOKEN: opts.token } : {}),
|
|
90
|
+
CAMUNDA_TRANSPORT: opts.transport ?? "auto",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
37
93
|
}
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Deploy a workflow's derived BPMN model. The deployed model includes
|
|
96
|
+
* auto-generated diagram interchange (DI) so it is inspectable in a
|
|
97
|
+
* modeller/Operate; pass `{ layout: false }` to deploy the DI-less semantic
|
|
98
|
+
* model. DI needs the optional `bpmn-auto-layout` dependency — see
|
|
99
|
+
* `toDeployableBpmn` for the graceful-degradation behaviour when it is absent.
|
|
100
|
+
*/
|
|
101
|
+
async deploy(wf, opts = {}) {
|
|
102
|
+
const xml = await toDeployableBpmn(wf, opts);
|
|
103
|
+
const file = new File([xml], `${wf.id}.bpmn`, { type: "text/xml" });
|
|
42
104
|
try {
|
|
43
|
-
|
|
105
|
+
return (await this.sdk.createDeployment({ resources: [file] }));
|
|
44
106
|
}
|
|
45
107
|
catch (e) {
|
|
46
|
-
throw
|
|
47
|
-
}
|
|
48
|
-
if (!res.ok) {
|
|
49
|
-
const body = await res.text().catch(() => "");
|
|
50
|
-
throw new WorkflowError(`${what} failed: ${res.status}`, res.status, body);
|
|
108
|
+
throw asWorkflowError(e, "deploy");
|
|
51
109
|
}
|
|
52
|
-
return res;
|
|
53
|
-
}
|
|
54
|
-
/** Deploy a workflow's derived BPMN model. */
|
|
55
|
-
async deploy(wf) {
|
|
56
|
-
const xml = toBpmn(wf);
|
|
57
|
-
const form = new FormData();
|
|
58
|
-
form.append("resources", new Blob([xml], { type: "text/xml" }), `${wf.id}.bpmn`);
|
|
59
|
-
return this.json(`/v2/deployments`, { method: "POST", body: form }, "deploy");
|
|
60
110
|
}
|
|
61
111
|
/**
|
|
62
112
|
* Start a workflow instance. For imperative workflows the engine variables are
|
|
@@ -65,11 +115,15 @@ export class WorkflowClient {
|
|
|
65
115
|
*/
|
|
66
116
|
async start(wf, input = {}) {
|
|
67
117
|
const variables = wf.kind === "imperative" ? { input, journal: {}, wfDone: false } : input;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
118
|
+
try {
|
|
119
|
+
return (await this.sdk.createProcessInstance({
|
|
120
|
+
processDefinitionId: wf.id,
|
|
121
|
+
variables,
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
throw asWorkflowError(e, "start");
|
|
126
|
+
}
|
|
73
127
|
}
|
|
74
128
|
/** Correlate a signal to a parked declarative `signal` step. Fails fast on an
|
|
75
129
|
* unknown signal name (a typo would otherwise send an uncorrelatable message
|
|
@@ -85,40 +139,25 @@ export class WorkflowClient {
|
|
|
85
139
|
if (!signals.includes(signalName)) {
|
|
86
140
|
throw new WorkflowError(`unknown signal "${signalName}" on flow "${flow.id}" — declared signals: ${signals.length ? signals.map((s) => `"${s}"`).join(", ") : "(none)"}`);
|
|
87
141
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
142
|
+
try {
|
|
143
|
+
return (await this.sdk.correlateMessage({
|
|
144
|
+
name: messageName(flow.id, signalName),
|
|
145
|
+
correlationKey,
|
|
146
|
+
variables,
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
throw asWorkflowError(e, `signal "${signalName}"`);
|
|
151
|
+
}
|
|
93
152
|
}
|
|
94
|
-
/** Fetch an instance (used by demos/tests to observe completion).
|
|
153
|
+
/** Fetch an instance (used by demos/tests to observe completion). Reads with
|
|
154
|
+
* zero-wait consistency; returns null when the instance is not (yet) visible. */
|
|
95
155
|
async getInstance(processInstanceKey) {
|
|
96
156
|
try {
|
|
97
|
-
|
|
98
|
-
if (!res.ok)
|
|
99
|
-
return null;
|
|
100
|
-
return (await res.json());
|
|
157
|
+
return (await this.sdk.getProcessInstance({ processInstanceKey }, { consistency: { waitUpToMs: 0 } }));
|
|
101
158
|
}
|
|
102
159
|
catch {
|
|
103
160
|
return null;
|
|
104
161
|
}
|
|
105
162
|
}
|
|
106
|
-
// --- low-level job protocol (used by the Worker runtime) -------------------
|
|
107
|
-
async activateJobs(type, opts) {
|
|
108
|
-
const body = {
|
|
109
|
-
type,
|
|
110
|
-
worker: opts.worker,
|
|
111
|
-
maxJobsToActivate: opts.maxJobsToActivate ?? 1,
|
|
112
|
-
timeout: opts.timeout ?? 30000,
|
|
113
|
-
requestTimeout: opts.requestTimeout ?? 15000,
|
|
114
|
-
};
|
|
115
|
-
const res = await this.json(`/v2/jobs/activation`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }, `activate ${type}`);
|
|
116
|
-
return res.jobs ?? [];
|
|
117
|
-
}
|
|
118
|
-
async completeJob(jobKey, variables = {}) {
|
|
119
|
-
await this.send(`/v2/jobs/${jobKey}/completion`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ variables }) }, "complete job");
|
|
120
|
-
}
|
|
121
|
-
async failJob(jobKey, errorMessage, retries = 0) {
|
|
122
|
-
await this.send(`/v2/jobs/${jobKey}/failure`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ errorMessage, retries }) }, "fail job");
|
|
123
|
-
}
|
|
124
163
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ export type { FlowBuilder } from "./declarative.js";
|
|
|
5
5
|
export { layoutBpmn, declarativeToLayoutedBpmn } from "./layout.js";
|
|
6
6
|
export { envelope } from "./envelope.js";
|
|
7
7
|
export type { Envelope, EnvelopeType, EnvelopeField, FieldSpec, ScalarType, ScalarTs, FieldTs, } from "./envelope.js";
|
|
8
|
-
export { WorkflowClient, WorkflowError, toBpmn } from "./client.js";
|
|
9
|
-
export type { WorkflowClientOptions,
|
|
8
|
+
export { WorkflowClient, WorkflowError, toBpmn, toDeployableBpmn } from "./client.js";
|
|
9
|
+
export type { WorkflowClientOptions, NanoSdkClient, NanoJobWorker, JobWorkerConfig, ActivatedJob, } from "./client.js";
|
|
10
10
|
export { Worker } from "./worker.js";
|
|
11
11
|
export type { WorkerOptions, ActivityEvent } from "./worker.js";
|
|
12
12
|
export type { Json, JsonObject, Job, StepHandler, DeclarativeFlow, DeclarativeStep, FlowNode, SwitchCase, NodeEnvelopes, StepContract, FlowContracts, ImperativeWorkflow, Orchestration, WorkflowContext, Workflow, DeployResult, StartResult, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -42,5 +42,5 @@ export { defineWorkflow, imperativeToBpmn, replayOnce } from "./imperative.js";
|
|
|
42
42
|
export { defineFlow, declarativeToBpmn, externalJobTypes, walkNodes } from "./declarative.js";
|
|
43
43
|
export { layoutBpmn, declarativeToLayoutedBpmn } from "./layout.js";
|
|
44
44
|
export { envelope } from "./envelope.js";
|
|
45
|
-
export { WorkflowClient, WorkflowError, toBpmn } from "./client.js";
|
|
45
|
+
export { WorkflowClient, WorkflowError, toBpmn, toDeployableBpmn } from "./client.js";
|
|
46
46
|
export { Worker } from "./worker.js";
|
package/dist/worker.d.ts
CHANGED
|
@@ -9,36 +9,53 @@ export interface ActivityEvent {
|
|
|
9
9
|
/** For an imperative orchestrator turn: the journalled step key, or "__done". */
|
|
10
10
|
step?: string;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
baseUrl?: string;
|
|
15
|
-
client?: WorkflowClient;
|
|
12
|
+
/** Options common to both ways of building a `Worker`. */
|
|
13
|
+
interface WorkerCommon {
|
|
16
14
|
workflows: Workflow[];
|
|
17
15
|
/** Worker name reported to the gateway. Default "nanobpm-workflow". */
|
|
18
16
|
name?: string;
|
|
17
|
+
/** Transport for the worker's own client (used only when `client` is not
|
|
18
|
+
* supplied): "auto" | "falcon" | "rest". Defaults to "rest" — job serving is
|
|
19
|
+
* resilience-critical, and REST long-poll reconnects with backoff after an
|
|
20
|
+
* engine restart (the ADR 0044 property), whereas the Falcon push transport
|
|
21
|
+
* does not recover a mid-stream disconnect. Instance creation, by contrast,
|
|
22
|
+
* defaults to Falcon on its own `WorkflowClient` for throughput. */
|
|
23
|
+
transport?: "auto" | "falcon" | "rest";
|
|
19
24
|
/** Long-poll timeout per activation, ms. Also bounds stop() latency. Default 10000. */
|
|
20
25
|
pollTimeoutMs?: number;
|
|
21
26
|
/** Job lock timeout, ms. Default 30000. */
|
|
22
27
|
jobTimeoutMs?: number;
|
|
23
|
-
/**
|
|
24
|
-
|
|
28
|
+
/** Max jobs handled in parallel per derived job type. Default 1. */
|
|
29
|
+
maxParallelJobs?: number;
|
|
25
30
|
onActivity?: (e: ActivityEvent) => void | Promise<void>;
|
|
26
31
|
onError?: (err: Error, context: {
|
|
27
32
|
type: string;
|
|
28
33
|
}) => void;
|
|
29
34
|
}
|
|
35
|
+
/** Build a `Worker` from **either** a `baseUrl` (a `WorkflowClient` is created)
|
|
36
|
+
* **or** an existing `client`. The union makes TypeScript enforce that exactly
|
|
37
|
+
* one is supplied, matching the constructor's runtime requirement. */
|
|
38
|
+
export type WorkerOptions = (WorkerCommon & {
|
|
39
|
+
/** Base URL of the nanobpmn gateway; a `WorkflowClient` is created for you. */
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
client?: never;
|
|
42
|
+
}) | (WorkerCommon & {
|
|
43
|
+
/** An existing `WorkflowClient` to serve jobs through. */
|
|
44
|
+
client: WorkflowClient;
|
|
45
|
+
baseUrl?: never;
|
|
46
|
+
});
|
|
30
47
|
export declare class Worker {
|
|
31
48
|
private readonly client;
|
|
32
49
|
private readonly name;
|
|
33
50
|
private readonly pollTimeoutMs;
|
|
34
51
|
private readonly jobTimeoutMs;
|
|
35
|
-
private readonly
|
|
52
|
+
private readonly maxParallelJobs;
|
|
36
53
|
private readonly onActivity?;
|
|
37
54
|
private readonly onError?;
|
|
38
55
|
/** job type → { workflowId, handle } */
|
|
39
56
|
private readonly routes;
|
|
40
57
|
private running;
|
|
41
|
-
private
|
|
58
|
+
private workers;
|
|
42
59
|
constructor(opts: WorkerOptions);
|
|
43
60
|
private register;
|
|
44
61
|
/** Register a derived job type, failing fast on a collision. Two workflows can
|
|
@@ -55,9 +72,18 @@ export declare class Worker {
|
|
|
55
72
|
private emitActivity;
|
|
56
73
|
/** The derived job types this worker serves. */
|
|
57
74
|
get servedTypes(): string[];
|
|
58
|
-
/** Begin
|
|
75
|
+
/** Begin serving. Creates one nano-sdk job worker per derived job type with
|
|
76
|
+
* `autoStart: false`, then starts each explicitly, so the poll lifecycle is
|
|
77
|
+
* deterministic and owned here rather than left to the SDK's auto-start
|
|
78
|
+
* default (matching the `JobWorkerConfig.autoStart` contract). Nothing polls
|
|
79
|
+
* before `start()` is called. */
|
|
59
80
|
start(): void;
|
|
60
|
-
/** Stop
|
|
81
|
+
/** Stop serving and wait for in-flight jobs to drain. Stopping is best-effort
|
|
82
|
+
* per worker: the Falcon worker's `stop` throws if it never bound a transport
|
|
83
|
+
* (detection still pending), which must not fail the whole shutdown. */
|
|
61
84
|
stop(): Promise<void>;
|
|
62
|
-
|
|
85
|
+
/** Dispatch one activated job to its route handler and acknowledge it. Returns
|
|
86
|
+
* the nano-sdk action receipt (its `complete`/`fail` result). */
|
|
87
|
+
private handleJob;
|
|
63
88
|
}
|
|
89
|
+
export {};
|