@nanobpm/nano-sdk 1.0.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 +45 -0
- package/dist/index.cjs +423 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +119 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +385 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @nanobpm/nano-sdk
|
|
2
|
+
|
|
3
|
+
A **drop-in replacement** for [`@camunda8/orchestration-cluster-api`](https://www.npmjs.com/package/@camunda8/orchestration-cluster-api) that transparently upgrades to Nano's **command-stream** protocol when connected to a [Nano](../nanobpmn) server. Against stock Camunda 8 it behaves exactly like the upstream SDK.
|
|
4
|
+
|
|
5
|
+
```diff
|
|
6
|
+
- import { createCamundaClient } from "@camunda8/orchestration-cluster-api";
|
|
7
|
+
+ import { createCamundaClient } from "@nanobpm/nano-sdk";
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Everything else is re-exported unchanged — same types, same client, same API.
|
|
11
|
+
|
|
12
|
+
## What gets upgraded
|
|
13
|
+
|
|
14
|
+
When the client detects a Nano server, two hot paths move from REST onto a single persistent WebSocket (`/command-stream`):
|
|
15
|
+
|
|
16
|
+
- **`createProcessInstance`** → `createInstance` frame (with `awaitCompletion` resolved via `instanceCompleted`).
|
|
17
|
+
- **`createJobWorker`** → `subscribe` + pushed `job` frames, acked with `completeJob`/`failJob`/`throwError` and credit-replenished.
|
|
18
|
+
|
|
19
|
+
This avoids the ~125 creates/s/conn ceiling of the REST long-poll path.
|
|
20
|
+
|
|
21
|
+
## Detection & override
|
|
22
|
+
|
|
23
|
+
Detection is automatic: the SDK probes `GET /v2/topology` once and looks for the Nano `commandStreamPath` advertisement. Control it via the `CAMUNDA_TRANSPORT` config (or env var):
|
|
24
|
+
|
|
25
|
+
| value | behaviour |
|
|
26
|
+
| ---------------- | ------------------------------------------- |
|
|
27
|
+
| `auto` (default) | command-stream on Nano, REST elsewhere |
|
|
28
|
+
| `command-stream` | force the stream (assume Nano) |
|
|
29
|
+
| `rest` | never upgrade — pure upstream behaviour |
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const client = createCamundaClient({
|
|
33
|
+
config: { CAMUNDA_REST_ADDRESS: "http://localhost:8080", CAMUNDA_TRANSPORT: "auto" },
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Build & verify
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install
|
|
41
|
+
npm run build
|
|
42
|
+
npm run verify # against a running Nano server on :8080
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`upstream-ref/` holds the cloned upstream SDK source for reference; the published package is the thin wrapper in `src/`.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
CommandStreamTransport: () => CommandStreamTransport,
|
|
35
|
+
NanoJobWorker: () => NanoJobWorker,
|
|
36
|
+
createCamundaClient: () => createCamundaClient,
|
|
37
|
+
default: () => index_default,
|
|
38
|
+
detectNano: () => detectNano
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
var import_orchestration_cluster_api2 = require("@camunda8/orchestration-cluster-api");
|
|
42
|
+
|
|
43
|
+
// src/detect.ts
|
|
44
|
+
var cache = /* @__PURE__ */ new Map();
|
|
45
|
+
function normalizeBase(url) {
|
|
46
|
+
return url.replace(/\/+$/, "");
|
|
47
|
+
}
|
|
48
|
+
async function detectNano(restAddress, fetchImpl = fetch) {
|
|
49
|
+
const base = normalizeBase(restAddress);
|
|
50
|
+
if (cache.has(base)) return cache.get(base) ?? null;
|
|
51
|
+
let info = null;
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetchImpl(`${base}/v2/topology`, {
|
|
54
|
+
headers: { accept: "application/json" }
|
|
55
|
+
});
|
|
56
|
+
if (res.ok) {
|
|
57
|
+
const body = await res.json().catch(() => ({}));
|
|
58
|
+
const nano = body?.nano;
|
|
59
|
+
if (nano && typeof nano.commandStreamPath === "string") {
|
|
60
|
+
info = {
|
|
61
|
+
engine: String(nano.engine ?? "nanobpmn"),
|
|
62
|
+
version: nano.version ? String(nano.version) : void 0,
|
|
63
|
+
commandStreamPath: nano.commandStreamPath
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
info = null;
|
|
69
|
+
}
|
|
70
|
+
cache.set(base, info);
|
|
71
|
+
return info;
|
|
72
|
+
}
|
|
73
|
+
function commandStreamUrl(restAddress, path) {
|
|
74
|
+
let base = normalizeBase(restAddress);
|
|
75
|
+
if (base.startsWith("https://")) base = "wss://" + base.slice("https://".length);
|
|
76
|
+
else if (base.startsWith("http://")) base = "ws://" + base.slice("http://".length);
|
|
77
|
+
return base + path;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/ws.ts
|
|
81
|
+
var cached = null;
|
|
82
|
+
async function getWebSocket() {
|
|
83
|
+
if (cached) return cached;
|
|
84
|
+
const g = globalThis;
|
|
85
|
+
if (typeof g.WebSocket === "function") {
|
|
86
|
+
cached = g.WebSocket;
|
|
87
|
+
return cached;
|
|
88
|
+
}
|
|
89
|
+
const mod = await import("ws");
|
|
90
|
+
cached = mod.default ?? mod.WebSocket;
|
|
91
|
+
return cached;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/transport.ts
|
|
95
|
+
var CommandStreamTransport = class {
|
|
96
|
+
url;
|
|
97
|
+
ws = null;
|
|
98
|
+
open = false;
|
|
99
|
+
corr = 0;
|
|
100
|
+
pending = /* @__PURE__ */ new Map();
|
|
101
|
+
awaits = /* @__PURE__ */ new Map();
|
|
102
|
+
subs = /* @__PURE__ */ new Map();
|
|
103
|
+
heartbeat = null;
|
|
104
|
+
connectPromise = null;
|
|
105
|
+
closed = false;
|
|
106
|
+
constructor(restAddress, path) {
|
|
107
|
+
this.url = commandStreamUrl(restAddress, path);
|
|
108
|
+
}
|
|
109
|
+
nextCorr() {
|
|
110
|
+
this.corr += 1;
|
|
111
|
+
return this.corr;
|
|
112
|
+
}
|
|
113
|
+
send(frame) {
|
|
114
|
+
if (this.ws && this.open) this.ws.send(JSON.stringify(frame));
|
|
115
|
+
}
|
|
116
|
+
async connect() {
|
|
117
|
+
if (this.open) return;
|
|
118
|
+
if (this.connectPromise) return this.connectPromise;
|
|
119
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
120
|
+
void getWebSocket().then((WS) => {
|
|
121
|
+
const ws = new WS(this.url);
|
|
122
|
+
this.ws = ws;
|
|
123
|
+
ws.onopen = () => {
|
|
124
|
+
};
|
|
125
|
+
ws.onmessage = (ev) => {
|
|
126
|
+
let f;
|
|
127
|
+
try {
|
|
128
|
+
f = JSON.parse(typeof ev.data === "string" ? ev.data : String(ev.data));
|
|
129
|
+
} catch {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.handle(f, resolve);
|
|
133
|
+
};
|
|
134
|
+
ws.onerror = () => {
|
|
135
|
+
if (!this.open) reject(new Error(`command-stream connect failed: ${this.url}`));
|
|
136
|
+
};
|
|
137
|
+
ws.onclose = () => {
|
|
138
|
+
this.open = false;
|
|
139
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
140
|
+
for (const p of this.pending.values()) p.reject(new Error("command-stream closed"));
|
|
141
|
+
this.pending.clear();
|
|
142
|
+
if (!this.closed) setTimeout(() => void this.reconnect(), 1e3);
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
return this.connectPromise;
|
|
147
|
+
}
|
|
148
|
+
async reconnect() {
|
|
149
|
+
this.connectPromise = null;
|
|
150
|
+
this.open = false;
|
|
151
|
+
await this.connect();
|
|
152
|
+
for (const sub of this.subs.values()) this.sendSubscribe(sub);
|
|
153
|
+
}
|
|
154
|
+
handle(f, resolveConnect) {
|
|
155
|
+
switch (f.type) {
|
|
156
|
+
case "welcome": {
|
|
157
|
+
this.open = true;
|
|
158
|
+
const hb = Number(f.heartbeatMs ?? 0);
|
|
159
|
+
if (hb > 0) {
|
|
160
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
161
|
+
this.heartbeat = setInterval(() => this.send({ type: "heartbeat" }), hb);
|
|
162
|
+
}
|
|
163
|
+
for (const sub of this.subs.values()) this.sendSubscribe(sub);
|
|
164
|
+
resolveConnect();
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case "job": {
|
|
168
|
+
const job = f.job ?? {};
|
|
169
|
+
const sub = this.subs.get(job.type);
|
|
170
|
+
if (sub) sub.onJob(job);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case "commandResult": {
|
|
174
|
+
const corr = Number(f.corr ?? 0);
|
|
175
|
+
const p = this.pending.get(corr);
|
|
176
|
+
if (p) {
|
|
177
|
+
this.pending.delete(corr);
|
|
178
|
+
p.resolve({ status: Number(f.status ?? 0), body: f.body ?? null });
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case "instanceCompleted": {
|
|
183
|
+
const corr = Number(f.corr ?? 0);
|
|
184
|
+
const cb = this.awaits.get(corr);
|
|
185
|
+
if (cb) {
|
|
186
|
+
this.awaits.delete(corr);
|
|
187
|
+
cb({
|
|
188
|
+
processCompleted: Boolean(f.processCompleted),
|
|
189
|
+
variables: f.variables ?? {},
|
|
190
|
+
processInstanceKey: String(f.processInstanceKey ?? "")
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
sendSubscribe(sub) {
|
|
198
|
+
this.send({
|
|
199
|
+
type: "subscribe",
|
|
200
|
+
jobType: sub.jobType,
|
|
201
|
+
jobCredits: sub.credits,
|
|
202
|
+
worker: sub.worker,
|
|
203
|
+
timeout: sub.timeoutMs,
|
|
204
|
+
fetchVariable: sub.fetchVariables
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/** Create a process instance over the stream. */
|
|
208
|
+
async createInstance(input) {
|
|
209
|
+
await this.connect();
|
|
210
|
+
const corr = this.nextCorr();
|
|
211
|
+
let completionResolve = null;
|
|
212
|
+
const completion = input.awaitCompletion ? new Promise((r) => completionResolve = r) : null;
|
|
213
|
+
if (completionResolve) this.awaits.set(corr, completionResolve);
|
|
214
|
+
const result = await new Promise((resolve, reject) => {
|
|
215
|
+
this.pending.set(corr, { resolve, reject });
|
|
216
|
+
this.send({
|
|
217
|
+
type: "createInstance",
|
|
218
|
+
corr,
|
|
219
|
+
processDefinitionId: input.processDefinitionId ?? null,
|
|
220
|
+
processDefinitionKey: input.processDefinitionKey ?? null,
|
|
221
|
+
variables: input.variables ?? null,
|
|
222
|
+
awaitCompletion: input.awaitCompletion ?? false,
|
|
223
|
+
fetchVariables: input.fetchVariables ?? null,
|
|
224
|
+
requestTimeout: input.requestTimeoutMs ?? null
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
const done = completion ? await completion : void 0;
|
|
228
|
+
return { ...result, completion: done };
|
|
229
|
+
}
|
|
230
|
+
/** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */
|
|
231
|
+
async subscribe(sub) {
|
|
232
|
+
this.subs.set(sub.jobType, sub);
|
|
233
|
+
await this.connect();
|
|
234
|
+
this.sendSubscribe(sub);
|
|
235
|
+
}
|
|
236
|
+
unsubscribe(jobType) {
|
|
237
|
+
this.subs.delete(jobType);
|
|
238
|
+
}
|
|
239
|
+
completeJob(jobKey, variables) {
|
|
240
|
+
this.send({ type: "completeJob", corr: this.nextCorr(), jobKey, variables: variables ?? null });
|
|
241
|
+
}
|
|
242
|
+
failJob(jobKey, retries, errorMessage) {
|
|
243
|
+
this.send({ type: "failJob", corr: this.nextCorr(), jobKey, retries: retries ?? 0, errorMessage: errorMessage ?? null });
|
|
244
|
+
}
|
|
245
|
+
throwError(jobKey, errorCode, errorMessage) {
|
|
246
|
+
this.send({ type: "throwError", corr: this.nextCorr(), jobKey, errorCode, errorMessage: errorMessage ?? null });
|
|
247
|
+
}
|
|
248
|
+
grantCredits(jobType, n) {
|
|
249
|
+
this.send({ type: "jobCredits", jobType, n });
|
|
250
|
+
}
|
|
251
|
+
close() {
|
|
252
|
+
this.closed = true;
|
|
253
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
254
|
+
try {
|
|
255
|
+
this.ws?.close(1e3, "shutdown");
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/nanoWorker.ts
|
|
262
|
+
var import_orchestration_cluster_api = require("@camunda8/orchestration-cluster-api");
|
|
263
|
+
var NanoJobWorker = class {
|
|
264
|
+
transport;
|
|
265
|
+
cfg;
|
|
266
|
+
credits;
|
|
267
|
+
stopped = false;
|
|
268
|
+
jobType;
|
|
269
|
+
name;
|
|
270
|
+
constructor(transport, cfg) {
|
|
271
|
+
this.transport = transport;
|
|
272
|
+
this.cfg = cfg;
|
|
273
|
+
this.jobType = cfg.jobType;
|
|
274
|
+
this.credits = cfg.maxParallelJobs ?? 10;
|
|
275
|
+
this.name = cfg.workerName || `worker-${cfg.jobType}`;
|
|
276
|
+
if (cfg.autoStart !== false) void this.start();
|
|
277
|
+
}
|
|
278
|
+
/** Bind the transport after async Nano detection. */
|
|
279
|
+
bindTransport(transport) {
|
|
280
|
+
this.transport = transport;
|
|
281
|
+
}
|
|
282
|
+
async start() {
|
|
283
|
+
this.stopped = false;
|
|
284
|
+
await this.transport.subscribe({
|
|
285
|
+
jobType: this.jobType,
|
|
286
|
+
worker: this.name,
|
|
287
|
+
credits: this.credits,
|
|
288
|
+
timeoutMs: this.cfg.jobTimeoutMs ?? 6e4,
|
|
289
|
+
fetchVariables: this.cfg.fetchVariables ?? null,
|
|
290
|
+
onJob: (raw) => void this.dispatch(raw)
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
enrich(raw) {
|
|
294
|
+
let acted = false;
|
|
295
|
+
const ack = (replenish = true) => {
|
|
296
|
+
if (!acted) {
|
|
297
|
+
acted = true;
|
|
298
|
+
if (replenish) this.transport.grantCredits(this.jobType, 1);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
return {
|
|
302
|
+
...raw,
|
|
303
|
+
complete: async (variables = {}) => {
|
|
304
|
+
this.transport.completeJob(raw.jobKey, variables);
|
|
305
|
+
ack();
|
|
306
|
+
return import_orchestration_cluster_api.JobActionReceiptSymbol;
|
|
307
|
+
},
|
|
308
|
+
fail: async (reason = {}) => {
|
|
309
|
+
this.transport.failJob(raw.jobKey, reason.retries, reason.errorMessage);
|
|
310
|
+
ack();
|
|
311
|
+
return import_orchestration_cluster_api.JobActionReceiptSymbol;
|
|
312
|
+
},
|
|
313
|
+
error: async (e) => {
|
|
314
|
+
this.transport.throwError(raw.jobKey, e.errorCode, e.errorMessage);
|
|
315
|
+
ack();
|
|
316
|
+
return import_orchestration_cluster_api.JobActionReceiptSymbol;
|
|
317
|
+
},
|
|
318
|
+
ignore: async () => {
|
|
319
|
+
ack();
|
|
320
|
+
return import_orchestration_cluster_api.JobActionReceiptSymbol;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
async dispatch(raw) {
|
|
325
|
+
if (this.stopped) return;
|
|
326
|
+
const job = this.enrich(raw);
|
|
327
|
+
try {
|
|
328
|
+
await this.cfg.jobHandler(job);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
try {
|
|
331
|
+
this.transport.failJob(raw.jobKey, 0, String(err));
|
|
332
|
+
} finally {
|
|
333
|
+
this.transport.grantCredits(this.jobType, 1);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
stop() {
|
|
338
|
+
this.stopped = true;
|
|
339
|
+
this.transport.unsubscribe(this.jobType);
|
|
340
|
+
}
|
|
341
|
+
close() {
|
|
342
|
+
this.stop();
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// src/index.ts
|
|
347
|
+
__reExport(index_exports, require("@camunda8/orchestration-cluster-api"), module.exports);
|
|
348
|
+
function resolveMode(opts) {
|
|
349
|
+
const fromOpts = opts?.config?.CAMUNDA_TRANSPORT;
|
|
350
|
+
const fromEnv = typeof process !== "undefined" ? process.env?.CAMUNDA_TRANSPORT : void 0;
|
|
351
|
+
return fromOpts ?? fromEnv ?? "auto";
|
|
352
|
+
}
|
|
353
|
+
function baseFrom(restAddress) {
|
|
354
|
+
return normalizeBase(restAddress).replace(/\/v2$/, "");
|
|
355
|
+
}
|
|
356
|
+
function createCamundaClient(opts) {
|
|
357
|
+
const client = (0, import_orchestration_cluster_api2.createCamundaClient)(opts);
|
|
358
|
+
const mode = resolveMode(opts);
|
|
359
|
+
if (mode === "rest") return client;
|
|
360
|
+
const restAddress = client.getConfig().restAddress;
|
|
361
|
+
const base = baseFrom(restAddress);
|
|
362
|
+
let transport = null;
|
|
363
|
+
let nano;
|
|
364
|
+
const ensure = async () => {
|
|
365
|
+
if (nano === void 0) {
|
|
366
|
+
nano = mode === "command-stream" ? { engine: "nanobpmn", commandStreamPath: "/command-stream" } : await detectNano(base);
|
|
367
|
+
}
|
|
368
|
+
if (!nano) return null;
|
|
369
|
+
if (!transport) transport = new CommandStreamTransport(base, nano.commandStreamPath);
|
|
370
|
+
return transport;
|
|
371
|
+
};
|
|
372
|
+
return new Proxy(client, {
|
|
373
|
+
get(target, prop, receiver) {
|
|
374
|
+
if (prop === "createProcessInstance") {
|
|
375
|
+
return (input, options) => {
|
|
376
|
+
const useStream = ensure();
|
|
377
|
+
return useStream.then(async (t) => {
|
|
378
|
+
if (!t) return target.createProcessInstance(input, options);
|
|
379
|
+
const r = await t.createInstance({
|
|
380
|
+
processDefinitionId: input?.processDefinitionId,
|
|
381
|
+
processDefinitionKey: input?.processDefinitionKey,
|
|
382
|
+
variables: input?.variables,
|
|
383
|
+
awaitCompletion: input?.awaitCompletion ?? false,
|
|
384
|
+
fetchVariables: input?.fetchVariables
|
|
385
|
+
});
|
|
386
|
+
if (r.status >= 400) throw new Error(`createInstance failed: ${r.status} ${JSON.stringify(r.body)}`);
|
|
387
|
+
const body = r.body ?? {};
|
|
388
|
+
return r.completion ? { ...body, variables: r.completion.variables, processInstanceKey: r.completion.processInstanceKey } : body;
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
if (prop === "createJobWorker") {
|
|
393
|
+
return (cfg) => {
|
|
394
|
+
const w = new NanoJobWorker(null, { ...cfg, autoStart: false });
|
|
395
|
+
void ensure().then((t) => {
|
|
396
|
+
if (t) {
|
|
397
|
+
w.bindTransport(t);
|
|
398
|
+
void w.start();
|
|
399
|
+
} else target.createJobWorker(cfg);
|
|
400
|
+
});
|
|
401
|
+
return w;
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (prop === "stopAllWorkers") {
|
|
405
|
+
return () => {
|
|
406
|
+
transport?.close();
|
|
407
|
+
return target.stopAllWorkers();
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return Reflect.get(target, prop, receiver);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
var index_default = createCamundaClient;
|
|
415
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
416
|
+
0 && (module.exports = {
|
|
417
|
+
CommandStreamTransport,
|
|
418
|
+
NanoJobWorker,
|
|
419
|
+
createCamundaClient,
|
|
420
|
+
detectNano,
|
|
421
|
+
...require("@camunda8/orchestration-cluster-api")
|
|
422
|
+
});
|
|
423
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/ws.ts","../src/transport.ts","../src/nanoWorker.ts"],"sourcesContent":["// @nanobpm/nano-sdk — a drop-in replacement for @camunda8/orchestration-cluster-api.\n//\n// It re-exports the entire upstream SDK surface, then overrides\n// `createCamundaClient` (and the default export) so that, when connected to a\n// Nano server, process-instance creation and job workers transparently upgrade\n// to Nano's command-stream protocol. Against stock Camunda 8 it is a no-op: the\n// upstream REST behaviour is used unchanged.\nimport {\n createCamundaClient as createCamundaClientBase,\n} from \"@camunda8/orchestration-cluster-api\";\nimport { detectNano, normalizeBase, type NanoInfo } from \"./detect.js\";\nimport { CommandStreamTransport } from \"./transport.js\";\nimport { NanoJobWorker } from \"./nanoWorker.js\";\n\n// Re-export everything else so consumers can swap the import path and nothing\n// breaks.\nexport * from \"@camunda8/orchestration-cluster-api\";\nexport { detectNano, type NanoInfo } from \"./detect.js\";\nexport { CommandStreamTransport } from \"./transport.js\";\nexport { NanoJobWorker } from \"./nanoWorker.js\";\n\n/** auto: upgrade only on Nano. command-stream: force. rest: never upgrade. */\nexport type NanoTransport = \"auto\" | \"command-stream\" | \"rest\";\n\ntype AnyOpts = Parameters<typeof createCamundaClientBase>[0] & {\n config?: Record<string, unknown> & { CAMUNDA_TRANSPORT?: NanoTransport; CAMUNDA_REST_ADDRESS?: string };\n};\n\nfunction resolveMode(opts?: AnyOpts): NanoTransport {\n const fromOpts = opts?.config?.CAMUNDA_TRANSPORT as NanoTransport | undefined;\n const fromEnv = (typeof process !== \"undefined\" ? process.env?.CAMUNDA_TRANSPORT : undefined) as NanoTransport | undefined;\n return fromOpts ?? fromEnv ?? \"auto\";\n}\n\n/** restAddress is normalized by the SDK to end with /v2; strip it for base. */\nfunction baseFrom(restAddress: string): string {\n return normalizeBase(restAddress).replace(/\\/v2$/, \"\");\n}\n\n/**\n * Drop-in replacement for the upstream createCamundaClient. Returns the upstream\n * client wrapped in a Proxy that upgrades createProcessInstance + createJobWorker\n * to the command stream when connected to a Nano server (overridable via the\n * CAMUNDA_TRANSPORT config: \"auto\" | \"command-stream\" | \"rest\").\n */\nexport function createCamundaClient(opts?: AnyOpts): ReturnType<typeof createCamundaClientBase> {\n const client = createCamundaClientBase(opts as any);\n const mode = resolveMode(opts);\n if (mode === \"rest\") return client;\n\n const restAddress = client.getConfig().restAddress;\n const base = baseFrom(restAddress);\n\n let transport: CommandStreamTransport | null = null;\n let nano: NanoInfo | null | undefined; // undefined = not yet probed\n\n const ensure = async (): Promise<CommandStreamTransport | null> => {\n if (nano === undefined) {\n nano = mode === \"command-stream\"\n ? { engine: \"nanobpmn\", commandStreamPath: \"/command-stream\" }\n : await detectNano(base);\n }\n if (!nano) return null;\n if (!transport) transport = new CommandStreamTransport(base, nano.commandStreamPath);\n return transport;\n };\n\n return new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === \"createProcessInstance\") {\n return (input: any, options?: any) => {\n const useStream = ensure();\n return useStream.then(async (t) => {\n if (!t) return (target as any).createProcessInstance(input, options);\n const r = await t.createInstance({\n processDefinitionId: input?.processDefinitionId,\n processDefinitionKey: input?.processDefinitionKey,\n variables: input?.variables,\n awaitCompletion: input?.awaitCompletion ?? false,\n fetchVariables: input?.fetchVariables,\n });\n if (r.status >= 400) throw new Error(`createInstance failed: ${r.status} ${JSON.stringify(r.body)}`);\n const body: any = r.body ?? {};\n return r.completion ? { ...body, variables: r.completion.variables, processInstanceKey: r.completion.processInstanceKey } : body;\n });\n };\n }\n if (prop === \"createJobWorker\") {\n return (cfg: any) => {\n const w = new NanoJobWorker(null as any, { ...cfg, autoStart: false });\n void ensure().then((t) => {\n if (t) { w.bindTransport(t); void w.start(); }\n else (target as any).createJobWorker(cfg);\n });\n return w as any;\n };\n }\n if (prop === \"stopAllWorkers\") {\n return () => {\n transport?.close();\n return (target as any).stopAllWorkers();\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\nexport default createCamundaClient;\n","// Nano server detection. A nanobpmn gateway advertises itself on GET /v2/topology\n// via a `nano` block carrying `commandStreamPath`. Detecting it lets the SDK\n// upgrade to the command-stream protocol transparently.\n\nexport interface NanoInfo {\n engine: string;\n version?: string;\n commandStreamPath: string;\n}\n\nconst cache = new Map<string, NanoInfo | null>();\n\n/** Strip trailing slashes for a tidy REST base. */\nexport function normalizeBase(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Probes a REST address once (cached) and returns the Nano engine info if the\n * server is a nanobpmn gateway, or null for stock Camunda 8.\n */\nexport async function detectNano(\n restAddress: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<NanoInfo | null> {\n const base = normalizeBase(restAddress);\n if (cache.has(base)) return cache.get(base) ?? null;\n let info: NanoInfo | null = null;\n try {\n const res = await fetchImpl(`${base}/v2/topology`, {\n headers: { accept: \"application/json\" },\n });\n if (res.ok) {\n const body: any = await res.json().catch(() => ({}));\n const nano = body?.nano;\n if (nano && typeof nano.commandStreamPath === \"string\") {\n info = {\n engine: String(nano.engine ?? \"nanobpmn\"),\n version: nano.version ? String(nano.version) : undefined,\n commandStreamPath: nano.commandStreamPath,\n };\n }\n }\n } catch {\n info = null; // unreachable or non-Nano: stay on REST\n }\n cache.set(base, info);\n return info;\n}\n\n/** Test seam: clear the detection cache. */\nexport function _clearDetectionCache(): void {\n cache.clear();\n}\n\n/** Build the ws:// or wss:// command-stream URL from a REST base + path. */\nexport function commandStreamUrl(restAddress: string, path: string): string {\n let base = normalizeBase(restAddress);\n if (base.startsWith(\"https://\")) base = \"wss://\" + base.slice(\"https://\".length);\n else if (base.startsWith(\"http://\")) base = \"ws://\" + base.slice(\"http://\".length);\n return base + path;\n}\n","// Picks a WebSocket implementation: native global (browser/Deno/Node>=22) or the\n// `ws` package (Node). Kept tiny so the transport stays runtime-agnostic.\nexport type WSImpl = new (url: string) => WebSocket;\n\nlet cached: WSImpl | null = null;\n\nexport async function getWebSocket(): Promise<WSImpl> {\n if (cached) return cached;\n const g = globalThis as { WebSocket?: WSImpl };\n if (typeof g.WebSocket === \"function\") {\n cached = g.WebSocket;\n return cached;\n }\n const mod = await import(\"ws\");\n cached = (mod.default ?? mod.WebSocket) as unknown as WSImpl;\n return cached;\n}\n","// Command-stream transport. One persistent WebSocket per client multiplexes:\n// * createInstance (corr-correlated CommandResult, awaitCompletion via\n// InstanceCompleted), and\n// * job subscriptions (welcome -> subscribe -> job push, replenished by\n// jobCredits; completeJob/failJob/throwError as unmetered drains).\n// Mirrors the protocol implemented by the engine (server/src/command_stream.rs)\n// and the embedded worker SDK (server/src/console/worker_sdk.ts).\nimport { commandStreamUrl } from \"./detect.js\";\nimport { getWebSocket } from \"./ws.js\";\n\ntype Json = Record<string, unknown>;\n\nexport interface JobFrame {\n jobKey: string;\n type: string;\n processInstanceKey: string;\n variables?: Record<string, unknown>;\n customHeaders?: Record<string, unknown>;\n retries?: number;\n [k: string]: unknown;\n}\n\nexport interface Subscription {\n jobType: string;\n worker: string;\n credits: number;\n timeoutMs: number;\n fetchVariables: string[] | null;\n onJob: (job: JobFrame) => void;\n}\n\ninterface Pending {\n resolve: (v: { status: number; body: unknown }) => void;\n reject: (e: unknown) => void;\n}\n\nexport class CommandStreamTransport {\n private url: string;\n private ws: WebSocket | null = null;\n private open = false;\n private corr = 0;\n private pending = new Map<number, Pending>();\n private awaits = new Map<number, (v: { processCompleted: boolean; variables: unknown; processInstanceKey: string }) => void>();\n private subs = new Map<string, Subscription>();\n private heartbeat: ReturnType<typeof setInterval> | null = null;\n private connectPromise: Promise<void> | null = null;\n private closed = false;\n\n constructor(restAddress: string, path: string) {\n this.url = commandStreamUrl(restAddress, path);\n }\n\n private nextCorr(): number {\n this.corr += 1;\n return this.corr;\n }\n\n private send(frame: Json): void {\n if (this.ws && this.open) this.ws.send(JSON.stringify(frame));\n }\n\n async connect(): Promise<void> {\n if (this.open) return;\n if (this.connectPromise) return this.connectPromise;\n this.connectPromise = new Promise<void>((resolve, reject) => {\n void getWebSocket().then((WS) => {\n const ws = new WS(this.url);\n this.ws = ws;\n ws.onopen = () => {};\n ws.onmessage = (ev: MessageEvent) => {\n let f: Json;\n try {\n f = JSON.parse(typeof ev.data === \"string\" ? ev.data : String(ev.data));\n } catch {\n return;\n }\n this.handle(f, resolve);\n };\n ws.onerror = () => {\n if (!this.open) reject(new Error(`command-stream connect failed: ${this.url}`));\n };\n ws.onclose = () => {\n this.open = false;\n if (this.heartbeat) clearInterval(this.heartbeat);\n // Reject in-flight commands; resubscribe on reconnect.\n for (const p of this.pending.values()) p.reject(new Error(\"command-stream closed\"));\n this.pending.clear();\n if (!this.closed) setTimeout(() => void this.reconnect(), 1000);\n };\n });\n });\n return this.connectPromise;\n }\n\n private async reconnect(): Promise<void> {\n this.connectPromise = null;\n this.open = false;\n await this.connect();\n for (const sub of this.subs.values()) this.sendSubscribe(sub);\n }\n\n private handle(f: Json, resolveConnect: () => void): void {\n switch (f.type) {\n case \"welcome\": {\n this.open = true;\n const hb = Number(f.heartbeatMs ?? 0);\n if (hb > 0) {\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = setInterval(() => this.send({ type: \"heartbeat\" }), hb);\n }\n for (const sub of this.subs.values()) this.sendSubscribe(sub);\n resolveConnect();\n break;\n }\n case \"job\": {\n const job = (f.job as JobFrame) ?? ({} as JobFrame);\n const sub = this.subs.get(job.type);\n if (sub) sub.onJob(job);\n break;\n }\n case \"commandResult\": {\n const corr = Number(f.corr ?? 0);\n const p = this.pending.get(corr);\n if (p) {\n this.pending.delete(corr);\n p.resolve({ status: Number(f.status ?? 0), body: f.body ?? null });\n }\n break;\n }\n case \"instanceCompleted\": {\n const corr = Number(f.corr ?? 0);\n const cb = this.awaits.get(corr);\n if (cb) {\n this.awaits.delete(corr);\n cb({\n processCompleted: Boolean(f.processCompleted),\n variables: f.variables ?? {},\n processInstanceKey: String(f.processInstanceKey ?? \"\"),\n });\n }\n break;\n }\n // submissionCredits / pressure / heartbeat: no client action needed yet.\n }\n }\n\n private sendSubscribe(sub: Subscription): void {\n this.send({\n type: \"subscribe\",\n jobType: sub.jobType,\n jobCredits: sub.credits,\n worker: sub.worker,\n timeout: sub.timeoutMs,\n fetchVariable: sub.fetchVariables,\n });\n }\n\n /** Create a process instance over the stream. */\n async createInstance(input: {\n processDefinitionId?: string;\n processDefinitionKey?: string;\n variables?: Record<string, unknown>;\n awaitCompletion?: boolean;\n fetchVariables?: string[];\n requestTimeoutMs?: number;\n }): Promise<{ status: number; body: unknown; completion?: { processCompleted: boolean; variables: unknown; processInstanceKey: string } }> {\n await this.connect();\n const corr = this.nextCorr();\n let completionResolve: ((v: { processCompleted: boolean; variables: unknown; processInstanceKey: string }) => void) | null = null;\n const completion = input.awaitCompletion\n ? new Promise<{ processCompleted: boolean; variables: unknown; processInstanceKey: string }>((r) => (completionResolve = r))\n : null;\n if (completionResolve) this.awaits.set(corr, completionResolve);\n const result = await new Promise<{ status: number; body: unknown }>((resolve, reject) => {\n this.pending.set(corr, { resolve, reject });\n this.send({\n type: \"createInstance\",\n corr,\n processDefinitionId: input.processDefinitionId ?? null,\n processDefinitionKey: input.processDefinitionKey ?? null,\n variables: input.variables ?? null,\n awaitCompletion: input.awaitCompletion ?? false,\n fetchVariables: input.fetchVariables ?? null,\n requestTimeout: input.requestTimeoutMs ?? null,\n });\n });\n const done = completion ? await completion : undefined;\n return { ...result, completion: done };\n }\n\n /** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */\n async subscribe(sub: Subscription): Promise<void> {\n this.subs.set(sub.jobType, sub);\n await this.connect();\n this.sendSubscribe(sub);\n }\n\n unsubscribe(jobType: string): void {\n this.subs.delete(jobType);\n }\n\n completeJob(jobKey: string, variables?: Record<string, unknown>): void {\n this.send({ type: \"completeJob\", corr: this.nextCorr(), jobKey, variables: variables ?? null });\n }\n failJob(jobKey: string, retries?: number, errorMessage?: string): void {\n this.send({ type: \"failJob\", corr: this.nextCorr(), jobKey, retries: retries ?? 0, errorMessage: errorMessage ?? null });\n }\n throwError(jobKey: string, errorCode: string, errorMessage?: string): void {\n this.send({ type: \"throwError\", corr: this.nextCorr(), jobKey, errorCode, errorMessage: errorMessage ?? null });\n }\n grantCredits(jobType: string, n: number): void {\n this.send({ type: \"jobCredits\", jobType, n });\n }\n\n close(): void {\n this.closed = true;\n if (this.heartbeat) clearInterval(this.heartbeat);\n try {\n this.ws?.close(1000, \"shutdown\");\n } catch {\n /* ignore */\n }\n }\n}\n","// A drop-in JobWorker that drains jobs over the command stream. Mirrors the\n// upstream JobWorker surface (close/stop/start) but receives pushed jobs instead\n// of long-polling. Each handled job replenishes one credit, keeping demand at\n// maxParallelJobs.\nimport { JobActionReceiptSymbol as JobActionReceipt } from \"@camunda8/orchestration-cluster-api\";\nimport type { CommandStreamTransport, JobFrame } from \"./transport.js\";\n\nexport interface NanoJobWorkerConfig {\n jobType: string;\n workerName?: string;\n maxParallelJobs?: number;\n jobTimeoutMs?: number;\n fetchVariables?: string[];\n jobHandler: (job: any) => Promise<typeof JobActionReceipt> | typeof JobActionReceipt;\n autoStart?: boolean;\n}\n\nexport class NanoJobWorker {\n private transport: CommandStreamTransport;\n private cfg: NanoJobWorkerConfig;\n private credits: number;\n private stopped = false;\n readonly jobType: string;\n readonly name: string;\n\n constructor(transport: CommandStreamTransport, cfg: NanoJobWorkerConfig) {\n this.transport = transport;\n this.cfg = cfg;\n this.jobType = cfg.jobType;\n this.credits = cfg.maxParallelJobs ?? 10;\n this.name = cfg.workerName || `worker-${cfg.jobType}`;\n if (cfg.autoStart !== false) void this.start();\n }\n\n /** Bind the transport after async Nano detection. */\n bindTransport(transport: CommandStreamTransport): void {\n this.transport = transport;\n }\n\n async start(): Promise<void> {\n this.stopped = false;\n await this.transport.subscribe({\n jobType: this.jobType,\n worker: this.name,\n credits: this.credits,\n timeoutMs: this.cfg.jobTimeoutMs ?? 60_000,\n fetchVariables: this.cfg.fetchVariables ?? null,\n onJob: (raw) => void this.dispatch(raw),\n });\n }\n\n private enrich(raw: JobFrame) {\n let acted = false;\n const ack = (replenish = true) => {\n if (!acted) {\n acted = true;\n if (replenish) this.transport.grantCredits(this.jobType, 1);\n }\n };\n return {\n ...raw,\n complete: async (variables: Record<string, unknown> = {}) => {\n this.transport.completeJob(raw.jobKey, variables);\n ack();\n return JobActionReceipt;\n },\n fail: async (reason: { errorMessage?: string; retries?: number } = {}) => {\n this.transport.failJob(raw.jobKey, reason.retries, reason.errorMessage);\n ack();\n return JobActionReceipt;\n },\n error: async (e: { errorCode: string; errorMessage?: string }) => {\n this.transport.throwError(raw.jobKey, e.errorCode, e.errorMessage);\n ack();\n return JobActionReceipt;\n },\n ignore: async () => {\n ack();\n return JobActionReceipt;\n },\n };\n }\n\n private async dispatch(raw: JobFrame): Promise<void> {\n if (this.stopped) return;\n const job = this.enrich(raw);\n try {\n await this.cfg.jobHandler(job);\n } catch (err) {\n try {\n this.transport.failJob(raw.jobKey, 0, String(err));\n } finally {\n this.transport.grantCredits(this.jobType, 1);\n }\n }\n }\n\n stop(): void {\n this.stopped = true;\n this.transport.unsubscribe(this.jobType);\n }\n close(): void {\n this.stop();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,IAAAA,oCAEO;;;ACCP,IAAM,QAAQ,oBAAI,IAA6B;AAGxC,SAAS,cAAc,KAAqB;AACjD,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;AAMA,eAAsB,WACpB,aACA,YAA0B,OACA;AAC1B,QAAM,OAAO,cAAc,WAAW;AACtC,MAAI,MAAM,IAAI,IAAI,EAAG,QAAO,MAAM,IAAI,IAAI,KAAK;AAC/C,MAAI,OAAwB;AAC5B,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,GAAG,IAAI,gBAAgB;AAAA,MACjD,SAAS,EAAE,QAAQ,mBAAmB;AAAA,IACxC,CAAC;AACD,QAAI,IAAI,IAAI;AACV,YAAM,OAAY,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACnD,YAAM,OAAO,MAAM;AACnB,UAAI,QAAQ,OAAO,KAAK,sBAAsB,UAAU;AACtD,eAAO;AAAA,UACL,QAAQ,OAAO,KAAK,UAAU,UAAU;AAAA,UACxC,SAAS,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI;AAAA,UAC/C,mBAAmB,KAAK;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,IAAI;AACpB,SAAO;AACT;AAQO,SAAS,iBAAiB,aAAqB,MAAsB;AAC1E,MAAI,OAAO,cAAc,WAAW;AACpC,MAAI,KAAK,WAAW,UAAU,EAAG,QAAO,WAAW,KAAK,MAAM,WAAW,MAAM;AAAA,WACtE,KAAK,WAAW,SAAS,EAAG,QAAO,UAAU,KAAK,MAAM,UAAU,MAAM;AACjF,SAAO,OAAO;AAChB;;;ACzDA,IAAI,SAAwB;AAE5B,eAAsB,eAAgC;AACpD,MAAI,OAAQ,QAAO;AACnB,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,cAAc,YAAY;AACrC,aAAS,EAAE;AACX,WAAO;AAAA,EACT;AACA,QAAM,MAAM,MAAM,OAAO,IAAI;AAC7B,WAAU,IAAI,WAAW,IAAI;AAC7B,SAAO;AACT;;;ACoBO,IAAM,yBAAN,MAA6B;AAAA,EAC1B;AAAA,EACA,KAAuB;AAAA,EACvB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU,oBAAI,IAAqB;AAAA,EACnC,SAAS,oBAAI,IAAwG;AAAA,EACrH,OAAO,oBAAI,IAA0B;AAAA,EACrC,YAAmD;AAAA,EACnD,iBAAuC;AAAA,EACvC,SAAS;AAAA,EAEjB,YAAY,aAAqB,MAAc;AAC7C,SAAK,MAAM,iBAAiB,aAAa,IAAI;AAAA,EAC/C;AAAA,EAEQ,WAAmB;AACzB,SAAK,QAAQ;AACb,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,KAAK,OAAmB;AAC9B,QAAI,KAAK,MAAM,KAAK,KAAM,MAAK,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,KAAM;AACf,QAAI,KAAK,eAAgB,QAAO,KAAK;AACrC,SAAK,iBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,WAAK,aAAa,EAAE,KAAK,CAAC,OAAO;AAC/B,cAAM,KAAK,IAAI,GAAG,KAAK,GAAG;AAC1B,aAAK,KAAK;AACV,WAAG,SAAS,MAAM;AAAA,QAAC;AACnB,WAAG,YAAY,CAAC,OAAqB;AACnC,cAAI;AACJ,cAAI;AACF,gBAAI,KAAK,MAAM,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO,OAAO,GAAG,IAAI,CAAC;AAAA,UACxE,QAAQ;AACN;AAAA,UACF;AACA,eAAK,OAAO,GAAG,OAAO;AAAA,QACxB;AACA,WAAG,UAAU,MAAM;AACjB,cAAI,CAAC,KAAK,KAAM,QAAO,IAAI,MAAM,kCAAkC,KAAK,GAAG,EAAE,CAAC;AAAA,QAChF;AACA,WAAG,UAAU,MAAM;AACjB,eAAK,OAAO;AACZ,cAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAEhD,qBAAW,KAAK,KAAK,QAAQ,OAAO,EAAG,GAAE,OAAO,IAAI,MAAM,uBAAuB,CAAC;AAClF,eAAK,QAAQ,MAAM;AACnB,cAAI,CAAC,KAAK,OAAQ,YAAW,MAAM,KAAK,KAAK,UAAU,GAAG,GAAI;AAAA,QAChE;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,YAA2B;AACvC,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,UAAM,KAAK,QAAQ;AACnB,eAAW,OAAO,KAAK,KAAK,OAAO,EAAG,MAAK,cAAc,GAAG;AAAA,EAC9D;AAAA,EAEQ,OAAO,GAAS,gBAAkC;AACxD,YAAQ,EAAE,MAAM;AAAA,MACd,KAAK,WAAW;AACd,aAAK,OAAO;AACZ,cAAM,KAAK,OAAO,EAAE,eAAe,CAAC;AACpC,YAAI,KAAK,GAAG;AACV,cAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAChD,eAAK,YAAY,YAAY,MAAM,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC,GAAG,EAAE;AAAA,QACzE;AACA,mBAAW,OAAO,KAAK,KAAK,OAAO,EAAG,MAAK,cAAc,GAAG;AAC5D,uBAAe;AACf;AAAA,MACF;AAAA,MACA,KAAK,OAAO;AACV,cAAM,MAAO,EAAE,OAAqB,CAAC;AACrC,cAAM,MAAM,KAAK,KAAK,IAAI,IAAI,IAAI;AAClC,YAAI,IAAK,KAAI,MAAM,GAAG;AACtB;AAAA,MACF;AAAA,MACA,KAAK,iBAAiB;AACpB,cAAM,OAAO,OAAO,EAAE,QAAQ,CAAC;AAC/B,cAAM,IAAI,KAAK,QAAQ,IAAI,IAAI;AAC/B,YAAI,GAAG;AACL,eAAK,QAAQ,OAAO,IAAI;AACxB,YAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,UAAU,CAAC,GAAG,MAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,QACnE;AACA;AAAA,MACF;AAAA,MACA,KAAK,qBAAqB;AACxB,cAAM,OAAO,OAAO,EAAE,QAAQ,CAAC;AAC/B,cAAM,KAAK,KAAK,OAAO,IAAI,IAAI;AAC/B,YAAI,IAAI;AACN,eAAK,OAAO,OAAO,IAAI;AACvB,aAAG;AAAA,YACD,kBAAkB,QAAQ,EAAE,gBAAgB;AAAA,YAC5C,WAAW,EAAE,aAAa,CAAC;AAAA,YAC3B,oBAAoB,OAAO,EAAE,sBAAsB,EAAE;AAAA,UACvD,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,IAEF;AAAA,EACF;AAAA,EAEQ,cAAc,KAAyB;AAC7C,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,MACb,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI;AAAA,MACZ,SAAS,IAAI;AAAA,MACb,eAAe,IAAI;AAAA,IACrB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,eAAe,OAOsH;AACzI,UAAM,KAAK,QAAQ;AACnB,UAAM,OAAO,KAAK,SAAS;AAC3B,QAAI,oBAAyH;AAC7H,UAAM,aAAa,MAAM,kBACrB,IAAI,QAAuF,CAAC,MAAO,oBAAoB,CAAE,IACzH;AACJ,QAAI,kBAAmB,MAAK,OAAO,IAAI,MAAM,iBAAiB;AAC9D,UAAM,SAAS,MAAM,IAAI,QAA2C,CAAC,SAAS,WAAW;AACvF,WAAK,QAAQ,IAAI,MAAM,EAAE,SAAS,OAAO,CAAC;AAC1C,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN;AAAA,QACA,qBAAqB,MAAM,uBAAuB;AAAA,QAClD,sBAAsB,MAAM,wBAAwB;AAAA,QACpD,WAAW,MAAM,aAAa;AAAA,QAC9B,iBAAiB,MAAM,mBAAmB;AAAA,QAC1C,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,gBAAgB,MAAM,oBAAoB;AAAA,MAC5C,CAAC;AAAA,IACH,CAAC;AACD,UAAM,OAAO,aAAa,MAAM,aAAa;AAC7C,WAAO,EAAE,GAAG,QAAQ,YAAY,KAAK;AAAA,EACvC;AAAA;AAAA,EAGA,MAAM,UAAU,KAAkC;AAChD,SAAK,KAAK,IAAI,IAAI,SAAS,GAAG;AAC9B,UAAM,KAAK,QAAQ;AACnB,SAAK,cAAc,GAAG;AAAA,EACxB;AAAA,EAEA,YAAY,SAAuB;AACjC,SAAK,KAAK,OAAO,OAAO;AAAA,EAC1B;AAAA,EAEA,YAAY,QAAgB,WAA2C;AACrE,SAAK,KAAK,EAAE,MAAM,eAAe,MAAM,KAAK,SAAS,GAAG,QAAQ,WAAW,aAAa,KAAK,CAAC;AAAA,EAChG;AAAA,EACA,QAAQ,QAAgB,SAAkB,cAA6B;AACrE,SAAK,KAAK,EAAE,MAAM,WAAW,MAAM,KAAK,SAAS,GAAG,QAAQ,SAAS,WAAW,GAAG,cAAc,gBAAgB,KAAK,CAAC;AAAA,EACzH;AAAA,EACA,WAAW,QAAgB,WAAmB,cAA6B;AACzE,SAAK,KAAK,EAAE,MAAM,cAAc,MAAM,KAAK,SAAS,GAAG,QAAQ,WAAW,cAAc,gBAAgB,KAAK,CAAC;AAAA,EAChH;AAAA,EACA,aAAa,SAAiB,GAAiB;AAC7C,SAAK,KAAK,EAAE,MAAM,cAAc,SAAS,EAAE,CAAC;AAAA,EAC9C;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AACd,QAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAChD,QAAI;AACF,WAAK,IAAI,MAAM,KAAM,UAAU;AAAA,IACjC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AC3NA,uCAA2D;AAapD,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACT;AAAA,EACA;AAAA,EAET,YAAY,WAAmC,KAA0B;AACvE,SAAK,YAAY;AACjB,SAAK,MAAM;AACX,SAAK,UAAU,IAAI;AACnB,SAAK,UAAU,IAAI,mBAAmB;AACtC,SAAK,OAAO,IAAI,cAAc,UAAU,IAAI,OAAO;AACnD,QAAI,IAAI,cAAc,MAAO,MAAK,KAAK,MAAM;AAAA,EAC/C;AAAA;AAAA,EAGA,cAAc,WAAyC;AACrD,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,UAAU;AACf,UAAM,KAAK,UAAU,UAAU;AAAA,MAC7B,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,WAAW,KAAK,IAAI,gBAAgB;AAAA,MACpC,gBAAgB,KAAK,IAAI,kBAAkB;AAAA,MAC3C,OAAO,CAAC,QAAQ,KAAK,KAAK,SAAS,GAAG;AAAA,IACxC,CAAC;AAAA,EACH;AAAA,EAEQ,OAAO,KAAe;AAC5B,QAAI,QAAQ;AACZ,UAAM,MAAM,CAAC,YAAY,SAAS;AAChC,UAAI,CAAC,OAAO;AACV,gBAAQ;AACR,YAAI,UAAW,MAAK,UAAU,aAAa,KAAK,SAAS,CAAC;AAAA,MAC5D;AAAA,IACF;AACA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU,OAAO,YAAqC,CAAC,MAAM;AAC3D,aAAK,UAAU,YAAY,IAAI,QAAQ,SAAS;AAChD,YAAI;AACJ,eAAO,iCAAAC;AAAA,MACT;AAAA,MACA,MAAM,OAAO,SAAsD,CAAC,MAAM;AACxE,aAAK,UAAU,QAAQ,IAAI,QAAQ,OAAO,SAAS,OAAO,YAAY;AACtE,YAAI;AACJ,eAAO,iCAAAA;AAAA,MACT;AAAA,MACA,OAAO,OAAO,MAAoD;AAChE,aAAK,UAAU,WAAW,IAAI,QAAQ,EAAE,WAAW,EAAE,YAAY;AACjE,YAAI;AACJ,eAAO,iCAAAA;AAAA,MACT;AAAA,MACA,QAAQ,YAAY;AAClB,YAAI;AACJ,eAAO,iCAAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,KAA8B;AACnD,QAAI,KAAK,QAAS;AAClB,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI;AACF,YAAM,KAAK,IAAI,WAAW,GAAG;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI;AACF,aAAK,UAAU,QAAQ,IAAI,QAAQ,GAAG,OAAO,GAAG,CAAC;AAAA,MACnD,UAAE;AACA,aAAK,UAAU,aAAa,KAAK,SAAS,CAAC;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAa;AACX,SAAK,UAAU;AACf,SAAK,UAAU,YAAY,KAAK,OAAO;AAAA,EACzC;AAAA,EACA,QAAc;AACZ,SAAK,KAAK;AAAA,EACZ;AACF;;;AJxFA,0BAAc,gDAhBd;AA4BA,SAAS,YAAY,MAA+B;AAClD,QAAM,WAAW,MAAM,QAAQ;AAC/B,QAAM,UAAW,OAAO,YAAY,cAAc,QAAQ,KAAK,oBAAoB;AACnF,SAAO,YAAY,WAAW;AAChC;AAGA,SAAS,SAAS,aAA6B;AAC7C,SAAO,cAAc,WAAW,EAAE,QAAQ,SAAS,EAAE;AACvD;AAQO,SAAS,oBAAoB,MAA4D;AAC9F,QAAM,aAAS,kCAAAC,qBAAwB,IAAW;AAClD,QAAM,OAAO,YAAY,IAAI;AAC7B,MAAI,SAAS,OAAQ,QAAO;AAE5B,QAAM,cAAc,OAAO,UAAU,EAAE;AACvC,QAAM,OAAO,SAAS,WAAW;AAEjC,MAAI,YAA2C;AAC/C,MAAI;AAEJ,QAAM,SAAS,YAAoD;AACjE,QAAI,SAAS,QAAW;AACtB,aAAO,SAAS,mBACZ,EAAE,QAAQ,YAAY,mBAAmB,kBAAkB,IAC3D,MAAM,WAAW,IAAI;AAAA,IAC3B;AACA,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,CAAC,UAAW,aAAY,IAAI,uBAAuB,MAAM,KAAK,iBAAiB;AACnF,WAAO;AAAA,EACT;AAEA,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,QAAQ,MAAM,UAAU;AAC1B,UAAI,SAAS,yBAAyB;AACpC,eAAO,CAAC,OAAY,YAAkB;AACpC,gBAAM,YAAY,OAAO;AACzB,iBAAO,UAAU,KAAK,OAAO,MAAM;AACjC,gBAAI,CAAC,EAAG,QAAQ,OAAe,sBAAsB,OAAO,OAAO;AACnE,kBAAM,IAAI,MAAM,EAAE,eAAe;AAAA,cAC/B,qBAAqB,OAAO;AAAA,cAC5B,sBAAsB,OAAO;AAAA,cAC7B,WAAW,OAAO;AAAA,cAClB,iBAAiB,OAAO,mBAAmB;AAAA,cAC3C,gBAAgB,OAAO;AAAA,YACzB,CAAC;AACD,gBAAI,EAAE,UAAU,IAAK,OAAM,IAAI,MAAM,0BAA0B,EAAE,MAAM,IAAI,KAAK,UAAU,EAAE,IAAI,CAAC,EAAE;AACnG,kBAAM,OAAY,EAAE,QAAQ,CAAC;AAC7B,mBAAO,EAAE,aAAa,EAAE,GAAG,MAAM,WAAW,EAAE,WAAW,WAAW,oBAAoB,EAAE,WAAW,mBAAmB,IAAI;AAAA,UAC9H,CAAC;AAAA,QACH;AAAA,MACF;AACA,UAAI,SAAS,mBAAmB;AAC9B,eAAO,CAAC,QAAa;AACnB,gBAAM,IAAI,IAAI,cAAc,MAAa,EAAE,GAAG,KAAK,WAAW,MAAM,CAAC;AACrE,eAAK,OAAO,EAAE,KAAK,CAAC,MAAM;AACxB,gBAAI,GAAG;AAAE,gBAAE,cAAc,CAAC;AAAG,mBAAK,EAAE,MAAM;AAAA,YAAG,MACxC,CAAC,OAAe,gBAAgB,GAAG;AAAA,UAC1C,CAAC;AACD,iBAAO;AAAA,QACT;AAAA,MACF;AACA,UAAI,SAAS,kBAAkB;AAC7B,eAAO,MAAM;AACX,qBAAW,MAAM;AACjB,iBAAQ,OAAe,eAAe;AAAA,QACxC;AAAA,MACF;AACA,aAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAAA,IAC3C;AAAA,EACF,CAAC;AACH;AAEA,IAAO,gBAAQ;","names":["import_orchestration_cluster_api","JobActionReceipt","createCamundaClientBase"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { JobActionReceiptSymbol, createCamundaClient as createCamundaClient$1 } from '@camunda8/orchestration-cluster-api';
|
|
2
|
+
export * from '@camunda8/orchestration-cluster-api';
|
|
3
|
+
|
|
4
|
+
interface NanoInfo {
|
|
5
|
+
engine: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
commandStreamPath: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Probes a REST address once (cached) and returns the Nano engine info if the
|
|
11
|
+
* server is a nanobpmn gateway, or null for stock Camunda 8.
|
|
12
|
+
*/
|
|
13
|
+
declare function detectNano(restAddress: string, fetchImpl?: typeof fetch): Promise<NanoInfo | null>;
|
|
14
|
+
|
|
15
|
+
interface JobFrame {
|
|
16
|
+
jobKey: string;
|
|
17
|
+
type: string;
|
|
18
|
+
processInstanceKey: string;
|
|
19
|
+
variables?: Record<string, unknown>;
|
|
20
|
+
customHeaders?: Record<string, unknown>;
|
|
21
|
+
retries?: number;
|
|
22
|
+
[k: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
interface Subscription {
|
|
25
|
+
jobType: string;
|
|
26
|
+
worker: string;
|
|
27
|
+
credits: number;
|
|
28
|
+
timeoutMs: number;
|
|
29
|
+
fetchVariables: string[] | null;
|
|
30
|
+
onJob: (job: JobFrame) => void;
|
|
31
|
+
}
|
|
32
|
+
declare class CommandStreamTransport {
|
|
33
|
+
private url;
|
|
34
|
+
private ws;
|
|
35
|
+
private open;
|
|
36
|
+
private corr;
|
|
37
|
+
private pending;
|
|
38
|
+
private awaits;
|
|
39
|
+
private subs;
|
|
40
|
+
private heartbeat;
|
|
41
|
+
private connectPromise;
|
|
42
|
+
private closed;
|
|
43
|
+
constructor(restAddress: string, path: string);
|
|
44
|
+
private nextCorr;
|
|
45
|
+
private send;
|
|
46
|
+
connect(): Promise<void>;
|
|
47
|
+
private reconnect;
|
|
48
|
+
private handle;
|
|
49
|
+
private sendSubscribe;
|
|
50
|
+
/** Create a process instance over the stream. */
|
|
51
|
+
createInstance(input: {
|
|
52
|
+
processDefinitionId?: string;
|
|
53
|
+
processDefinitionKey?: string;
|
|
54
|
+
variables?: Record<string, unknown>;
|
|
55
|
+
awaitCompletion?: boolean;
|
|
56
|
+
fetchVariables?: string[];
|
|
57
|
+
requestTimeoutMs?: number;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: number;
|
|
60
|
+
body: unknown;
|
|
61
|
+
completion?: {
|
|
62
|
+
processCompleted: boolean;
|
|
63
|
+
variables: unknown;
|
|
64
|
+
processInstanceKey: string;
|
|
65
|
+
};
|
|
66
|
+
}>;
|
|
67
|
+
/** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */
|
|
68
|
+
subscribe(sub: Subscription): Promise<void>;
|
|
69
|
+
unsubscribe(jobType: string): void;
|
|
70
|
+
completeJob(jobKey: string, variables?: Record<string, unknown>): void;
|
|
71
|
+
failJob(jobKey: string, retries?: number, errorMessage?: string): void;
|
|
72
|
+
throwError(jobKey: string, errorCode: string, errorMessage?: string): void;
|
|
73
|
+
grantCredits(jobType: string, n: number): void;
|
|
74
|
+
close(): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface NanoJobWorkerConfig {
|
|
78
|
+
jobType: string;
|
|
79
|
+
workerName?: string;
|
|
80
|
+
maxParallelJobs?: number;
|
|
81
|
+
jobTimeoutMs?: number;
|
|
82
|
+
fetchVariables?: string[];
|
|
83
|
+
jobHandler: (job: any) => Promise<typeof JobActionReceiptSymbol> | typeof JobActionReceiptSymbol;
|
|
84
|
+
autoStart?: boolean;
|
|
85
|
+
}
|
|
86
|
+
declare class NanoJobWorker {
|
|
87
|
+
private transport;
|
|
88
|
+
private cfg;
|
|
89
|
+
private credits;
|
|
90
|
+
private stopped;
|
|
91
|
+
readonly jobType: string;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
constructor(transport: CommandStreamTransport, cfg: NanoJobWorkerConfig);
|
|
94
|
+
/** Bind the transport after async Nano detection. */
|
|
95
|
+
bindTransport(transport: CommandStreamTransport): void;
|
|
96
|
+
start(): Promise<void>;
|
|
97
|
+
private enrich;
|
|
98
|
+
private dispatch;
|
|
99
|
+
stop(): void;
|
|
100
|
+
close(): void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** auto: upgrade only on Nano. command-stream: force. rest: never upgrade. */
|
|
104
|
+
type NanoTransport = "auto" | "command-stream" | "rest";
|
|
105
|
+
type AnyOpts = Parameters<typeof createCamundaClient$1>[0] & {
|
|
106
|
+
config?: Record<string, unknown> & {
|
|
107
|
+
CAMUNDA_TRANSPORT?: NanoTransport;
|
|
108
|
+
CAMUNDA_REST_ADDRESS?: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Drop-in replacement for the upstream createCamundaClient. Returns the upstream
|
|
113
|
+
* client wrapped in a Proxy that upgrades createProcessInstance + createJobWorker
|
|
114
|
+
* to the command stream when connected to a Nano server (overridable via the
|
|
115
|
+
* CAMUNDA_TRANSPORT config: "auto" | "command-stream" | "rest").
|
|
116
|
+
*/
|
|
117
|
+
declare function createCamundaClient(opts?: AnyOpts): ReturnType<typeof createCamundaClient$1>;
|
|
118
|
+
|
|
119
|
+
export { CommandStreamTransport, type NanoInfo, NanoJobWorker, type NanoTransport, createCamundaClient, createCamundaClient as default, detectNano };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { JobActionReceiptSymbol, createCamundaClient as createCamundaClient$1 } from '@camunda8/orchestration-cluster-api';
|
|
2
|
+
export * from '@camunda8/orchestration-cluster-api';
|
|
3
|
+
|
|
4
|
+
interface NanoInfo {
|
|
5
|
+
engine: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
commandStreamPath: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Probes a REST address once (cached) and returns the Nano engine info if the
|
|
11
|
+
* server is a nanobpmn gateway, or null for stock Camunda 8.
|
|
12
|
+
*/
|
|
13
|
+
declare function detectNano(restAddress: string, fetchImpl?: typeof fetch): Promise<NanoInfo | null>;
|
|
14
|
+
|
|
15
|
+
interface JobFrame {
|
|
16
|
+
jobKey: string;
|
|
17
|
+
type: string;
|
|
18
|
+
processInstanceKey: string;
|
|
19
|
+
variables?: Record<string, unknown>;
|
|
20
|
+
customHeaders?: Record<string, unknown>;
|
|
21
|
+
retries?: number;
|
|
22
|
+
[k: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
interface Subscription {
|
|
25
|
+
jobType: string;
|
|
26
|
+
worker: string;
|
|
27
|
+
credits: number;
|
|
28
|
+
timeoutMs: number;
|
|
29
|
+
fetchVariables: string[] | null;
|
|
30
|
+
onJob: (job: JobFrame) => void;
|
|
31
|
+
}
|
|
32
|
+
declare class CommandStreamTransport {
|
|
33
|
+
private url;
|
|
34
|
+
private ws;
|
|
35
|
+
private open;
|
|
36
|
+
private corr;
|
|
37
|
+
private pending;
|
|
38
|
+
private awaits;
|
|
39
|
+
private subs;
|
|
40
|
+
private heartbeat;
|
|
41
|
+
private connectPromise;
|
|
42
|
+
private closed;
|
|
43
|
+
constructor(restAddress: string, path: string);
|
|
44
|
+
private nextCorr;
|
|
45
|
+
private send;
|
|
46
|
+
connect(): Promise<void>;
|
|
47
|
+
private reconnect;
|
|
48
|
+
private handle;
|
|
49
|
+
private sendSubscribe;
|
|
50
|
+
/** Create a process instance over the stream. */
|
|
51
|
+
createInstance(input: {
|
|
52
|
+
processDefinitionId?: string;
|
|
53
|
+
processDefinitionKey?: string;
|
|
54
|
+
variables?: Record<string, unknown>;
|
|
55
|
+
awaitCompletion?: boolean;
|
|
56
|
+
fetchVariables?: string[];
|
|
57
|
+
requestTimeoutMs?: number;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: number;
|
|
60
|
+
body: unknown;
|
|
61
|
+
completion?: {
|
|
62
|
+
processCompleted: boolean;
|
|
63
|
+
variables: unknown;
|
|
64
|
+
processInstanceKey: string;
|
|
65
|
+
};
|
|
66
|
+
}>;
|
|
67
|
+
/** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */
|
|
68
|
+
subscribe(sub: Subscription): Promise<void>;
|
|
69
|
+
unsubscribe(jobType: string): void;
|
|
70
|
+
completeJob(jobKey: string, variables?: Record<string, unknown>): void;
|
|
71
|
+
failJob(jobKey: string, retries?: number, errorMessage?: string): void;
|
|
72
|
+
throwError(jobKey: string, errorCode: string, errorMessage?: string): void;
|
|
73
|
+
grantCredits(jobType: string, n: number): void;
|
|
74
|
+
close(): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface NanoJobWorkerConfig {
|
|
78
|
+
jobType: string;
|
|
79
|
+
workerName?: string;
|
|
80
|
+
maxParallelJobs?: number;
|
|
81
|
+
jobTimeoutMs?: number;
|
|
82
|
+
fetchVariables?: string[];
|
|
83
|
+
jobHandler: (job: any) => Promise<typeof JobActionReceiptSymbol> | typeof JobActionReceiptSymbol;
|
|
84
|
+
autoStart?: boolean;
|
|
85
|
+
}
|
|
86
|
+
declare class NanoJobWorker {
|
|
87
|
+
private transport;
|
|
88
|
+
private cfg;
|
|
89
|
+
private credits;
|
|
90
|
+
private stopped;
|
|
91
|
+
readonly jobType: string;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
constructor(transport: CommandStreamTransport, cfg: NanoJobWorkerConfig);
|
|
94
|
+
/** Bind the transport after async Nano detection. */
|
|
95
|
+
bindTransport(transport: CommandStreamTransport): void;
|
|
96
|
+
start(): Promise<void>;
|
|
97
|
+
private enrich;
|
|
98
|
+
private dispatch;
|
|
99
|
+
stop(): void;
|
|
100
|
+
close(): void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** auto: upgrade only on Nano. command-stream: force. rest: never upgrade. */
|
|
104
|
+
type NanoTransport = "auto" | "command-stream" | "rest";
|
|
105
|
+
type AnyOpts = Parameters<typeof createCamundaClient$1>[0] & {
|
|
106
|
+
config?: Record<string, unknown> & {
|
|
107
|
+
CAMUNDA_TRANSPORT?: NanoTransport;
|
|
108
|
+
CAMUNDA_REST_ADDRESS?: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Drop-in replacement for the upstream createCamundaClient. Returns the upstream
|
|
113
|
+
* client wrapped in a Proxy that upgrades createProcessInstance + createJobWorker
|
|
114
|
+
* to the command stream when connected to a Nano server (overridable via the
|
|
115
|
+
* CAMUNDA_TRANSPORT config: "auto" | "command-stream" | "rest").
|
|
116
|
+
*/
|
|
117
|
+
declare function createCamundaClient(opts?: AnyOpts): ReturnType<typeof createCamundaClient$1>;
|
|
118
|
+
|
|
119
|
+
export { CommandStreamTransport, type NanoInfo, NanoJobWorker, type NanoTransport, createCamundaClient, createCamundaClient as default, detectNano };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
createCamundaClient as createCamundaClientBase
|
|
4
|
+
} from "@camunda8/orchestration-cluster-api";
|
|
5
|
+
|
|
6
|
+
// src/detect.ts
|
|
7
|
+
var cache = /* @__PURE__ */ new Map();
|
|
8
|
+
function normalizeBase(url) {
|
|
9
|
+
return url.replace(/\/+$/, "");
|
|
10
|
+
}
|
|
11
|
+
async function detectNano(restAddress, fetchImpl = fetch) {
|
|
12
|
+
const base = normalizeBase(restAddress);
|
|
13
|
+
if (cache.has(base)) return cache.get(base) ?? null;
|
|
14
|
+
let info = null;
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetchImpl(`${base}/v2/topology`, {
|
|
17
|
+
headers: { accept: "application/json" }
|
|
18
|
+
});
|
|
19
|
+
if (res.ok) {
|
|
20
|
+
const body = await res.json().catch(() => ({}));
|
|
21
|
+
const nano = body?.nano;
|
|
22
|
+
if (nano && typeof nano.commandStreamPath === "string") {
|
|
23
|
+
info = {
|
|
24
|
+
engine: String(nano.engine ?? "nanobpmn"),
|
|
25
|
+
version: nano.version ? String(nano.version) : void 0,
|
|
26
|
+
commandStreamPath: nano.commandStreamPath
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
info = null;
|
|
32
|
+
}
|
|
33
|
+
cache.set(base, info);
|
|
34
|
+
return info;
|
|
35
|
+
}
|
|
36
|
+
function commandStreamUrl(restAddress, path) {
|
|
37
|
+
let base = normalizeBase(restAddress);
|
|
38
|
+
if (base.startsWith("https://")) base = "wss://" + base.slice("https://".length);
|
|
39
|
+
else if (base.startsWith("http://")) base = "ws://" + base.slice("http://".length);
|
|
40
|
+
return base + path;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/ws.ts
|
|
44
|
+
var cached = null;
|
|
45
|
+
async function getWebSocket() {
|
|
46
|
+
if (cached) return cached;
|
|
47
|
+
const g = globalThis;
|
|
48
|
+
if (typeof g.WebSocket === "function") {
|
|
49
|
+
cached = g.WebSocket;
|
|
50
|
+
return cached;
|
|
51
|
+
}
|
|
52
|
+
const mod = await import("ws");
|
|
53
|
+
cached = mod.default ?? mod.WebSocket;
|
|
54
|
+
return cached;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/transport.ts
|
|
58
|
+
var CommandStreamTransport = class {
|
|
59
|
+
url;
|
|
60
|
+
ws = null;
|
|
61
|
+
open = false;
|
|
62
|
+
corr = 0;
|
|
63
|
+
pending = /* @__PURE__ */ new Map();
|
|
64
|
+
awaits = /* @__PURE__ */ new Map();
|
|
65
|
+
subs = /* @__PURE__ */ new Map();
|
|
66
|
+
heartbeat = null;
|
|
67
|
+
connectPromise = null;
|
|
68
|
+
closed = false;
|
|
69
|
+
constructor(restAddress, path) {
|
|
70
|
+
this.url = commandStreamUrl(restAddress, path);
|
|
71
|
+
}
|
|
72
|
+
nextCorr() {
|
|
73
|
+
this.corr += 1;
|
|
74
|
+
return this.corr;
|
|
75
|
+
}
|
|
76
|
+
send(frame) {
|
|
77
|
+
if (this.ws && this.open) this.ws.send(JSON.stringify(frame));
|
|
78
|
+
}
|
|
79
|
+
async connect() {
|
|
80
|
+
if (this.open) return;
|
|
81
|
+
if (this.connectPromise) return this.connectPromise;
|
|
82
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
83
|
+
void getWebSocket().then((WS) => {
|
|
84
|
+
const ws = new WS(this.url);
|
|
85
|
+
this.ws = ws;
|
|
86
|
+
ws.onopen = () => {
|
|
87
|
+
};
|
|
88
|
+
ws.onmessage = (ev) => {
|
|
89
|
+
let f;
|
|
90
|
+
try {
|
|
91
|
+
f = JSON.parse(typeof ev.data === "string" ? ev.data : String(ev.data));
|
|
92
|
+
} catch {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.handle(f, resolve);
|
|
96
|
+
};
|
|
97
|
+
ws.onerror = () => {
|
|
98
|
+
if (!this.open) reject(new Error(`command-stream connect failed: ${this.url}`));
|
|
99
|
+
};
|
|
100
|
+
ws.onclose = () => {
|
|
101
|
+
this.open = false;
|
|
102
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
103
|
+
for (const p of this.pending.values()) p.reject(new Error("command-stream closed"));
|
|
104
|
+
this.pending.clear();
|
|
105
|
+
if (!this.closed) setTimeout(() => void this.reconnect(), 1e3);
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
return this.connectPromise;
|
|
110
|
+
}
|
|
111
|
+
async reconnect() {
|
|
112
|
+
this.connectPromise = null;
|
|
113
|
+
this.open = false;
|
|
114
|
+
await this.connect();
|
|
115
|
+
for (const sub of this.subs.values()) this.sendSubscribe(sub);
|
|
116
|
+
}
|
|
117
|
+
handle(f, resolveConnect) {
|
|
118
|
+
switch (f.type) {
|
|
119
|
+
case "welcome": {
|
|
120
|
+
this.open = true;
|
|
121
|
+
const hb = Number(f.heartbeatMs ?? 0);
|
|
122
|
+
if (hb > 0) {
|
|
123
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
124
|
+
this.heartbeat = setInterval(() => this.send({ type: "heartbeat" }), hb);
|
|
125
|
+
}
|
|
126
|
+
for (const sub of this.subs.values()) this.sendSubscribe(sub);
|
|
127
|
+
resolveConnect();
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case "job": {
|
|
131
|
+
const job = f.job ?? {};
|
|
132
|
+
const sub = this.subs.get(job.type);
|
|
133
|
+
if (sub) sub.onJob(job);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case "commandResult": {
|
|
137
|
+
const corr = Number(f.corr ?? 0);
|
|
138
|
+
const p = this.pending.get(corr);
|
|
139
|
+
if (p) {
|
|
140
|
+
this.pending.delete(corr);
|
|
141
|
+
p.resolve({ status: Number(f.status ?? 0), body: f.body ?? null });
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case "instanceCompleted": {
|
|
146
|
+
const corr = Number(f.corr ?? 0);
|
|
147
|
+
const cb = this.awaits.get(corr);
|
|
148
|
+
if (cb) {
|
|
149
|
+
this.awaits.delete(corr);
|
|
150
|
+
cb({
|
|
151
|
+
processCompleted: Boolean(f.processCompleted),
|
|
152
|
+
variables: f.variables ?? {},
|
|
153
|
+
processInstanceKey: String(f.processInstanceKey ?? "")
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
sendSubscribe(sub) {
|
|
161
|
+
this.send({
|
|
162
|
+
type: "subscribe",
|
|
163
|
+
jobType: sub.jobType,
|
|
164
|
+
jobCredits: sub.credits,
|
|
165
|
+
worker: sub.worker,
|
|
166
|
+
timeout: sub.timeoutMs,
|
|
167
|
+
fetchVariable: sub.fetchVariables
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/** Create a process instance over the stream. */
|
|
171
|
+
async createInstance(input) {
|
|
172
|
+
await this.connect();
|
|
173
|
+
const corr = this.nextCorr();
|
|
174
|
+
let completionResolve = null;
|
|
175
|
+
const completion = input.awaitCompletion ? new Promise((r) => completionResolve = r) : null;
|
|
176
|
+
if (completionResolve) this.awaits.set(corr, completionResolve);
|
|
177
|
+
const result = await new Promise((resolve, reject) => {
|
|
178
|
+
this.pending.set(corr, { resolve, reject });
|
|
179
|
+
this.send({
|
|
180
|
+
type: "createInstance",
|
|
181
|
+
corr,
|
|
182
|
+
processDefinitionId: input.processDefinitionId ?? null,
|
|
183
|
+
processDefinitionKey: input.processDefinitionKey ?? null,
|
|
184
|
+
variables: input.variables ?? null,
|
|
185
|
+
awaitCompletion: input.awaitCompletion ?? false,
|
|
186
|
+
fetchVariables: input.fetchVariables ?? null,
|
|
187
|
+
requestTimeout: input.requestTimeoutMs ?? null
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
const done = completion ? await completion : void 0;
|
|
191
|
+
return { ...result, completion: done };
|
|
192
|
+
}
|
|
193
|
+
/** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */
|
|
194
|
+
async subscribe(sub) {
|
|
195
|
+
this.subs.set(sub.jobType, sub);
|
|
196
|
+
await this.connect();
|
|
197
|
+
this.sendSubscribe(sub);
|
|
198
|
+
}
|
|
199
|
+
unsubscribe(jobType) {
|
|
200
|
+
this.subs.delete(jobType);
|
|
201
|
+
}
|
|
202
|
+
completeJob(jobKey, variables) {
|
|
203
|
+
this.send({ type: "completeJob", corr: this.nextCorr(), jobKey, variables: variables ?? null });
|
|
204
|
+
}
|
|
205
|
+
failJob(jobKey, retries, errorMessage) {
|
|
206
|
+
this.send({ type: "failJob", corr: this.nextCorr(), jobKey, retries: retries ?? 0, errorMessage: errorMessage ?? null });
|
|
207
|
+
}
|
|
208
|
+
throwError(jobKey, errorCode, errorMessage) {
|
|
209
|
+
this.send({ type: "throwError", corr: this.nextCorr(), jobKey, errorCode, errorMessage: errorMessage ?? null });
|
|
210
|
+
}
|
|
211
|
+
grantCredits(jobType, n) {
|
|
212
|
+
this.send({ type: "jobCredits", jobType, n });
|
|
213
|
+
}
|
|
214
|
+
close() {
|
|
215
|
+
this.closed = true;
|
|
216
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
217
|
+
try {
|
|
218
|
+
this.ws?.close(1e3, "shutdown");
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/nanoWorker.ts
|
|
225
|
+
import { JobActionReceiptSymbol as JobActionReceipt } from "@camunda8/orchestration-cluster-api";
|
|
226
|
+
var NanoJobWorker = class {
|
|
227
|
+
transport;
|
|
228
|
+
cfg;
|
|
229
|
+
credits;
|
|
230
|
+
stopped = false;
|
|
231
|
+
jobType;
|
|
232
|
+
name;
|
|
233
|
+
constructor(transport, cfg) {
|
|
234
|
+
this.transport = transport;
|
|
235
|
+
this.cfg = cfg;
|
|
236
|
+
this.jobType = cfg.jobType;
|
|
237
|
+
this.credits = cfg.maxParallelJobs ?? 10;
|
|
238
|
+
this.name = cfg.workerName || `worker-${cfg.jobType}`;
|
|
239
|
+
if (cfg.autoStart !== false) void this.start();
|
|
240
|
+
}
|
|
241
|
+
/** Bind the transport after async Nano detection. */
|
|
242
|
+
bindTransport(transport) {
|
|
243
|
+
this.transport = transport;
|
|
244
|
+
}
|
|
245
|
+
async start() {
|
|
246
|
+
this.stopped = false;
|
|
247
|
+
await this.transport.subscribe({
|
|
248
|
+
jobType: this.jobType,
|
|
249
|
+
worker: this.name,
|
|
250
|
+
credits: this.credits,
|
|
251
|
+
timeoutMs: this.cfg.jobTimeoutMs ?? 6e4,
|
|
252
|
+
fetchVariables: this.cfg.fetchVariables ?? null,
|
|
253
|
+
onJob: (raw) => void this.dispatch(raw)
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
enrich(raw) {
|
|
257
|
+
let acted = false;
|
|
258
|
+
const ack = (replenish = true) => {
|
|
259
|
+
if (!acted) {
|
|
260
|
+
acted = true;
|
|
261
|
+
if (replenish) this.transport.grantCredits(this.jobType, 1);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
...raw,
|
|
266
|
+
complete: async (variables = {}) => {
|
|
267
|
+
this.transport.completeJob(raw.jobKey, variables);
|
|
268
|
+
ack();
|
|
269
|
+
return JobActionReceipt;
|
|
270
|
+
},
|
|
271
|
+
fail: async (reason = {}) => {
|
|
272
|
+
this.transport.failJob(raw.jobKey, reason.retries, reason.errorMessage);
|
|
273
|
+
ack();
|
|
274
|
+
return JobActionReceipt;
|
|
275
|
+
},
|
|
276
|
+
error: async (e) => {
|
|
277
|
+
this.transport.throwError(raw.jobKey, e.errorCode, e.errorMessage);
|
|
278
|
+
ack();
|
|
279
|
+
return JobActionReceipt;
|
|
280
|
+
},
|
|
281
|
+
ignore: async () => {
|
|
282
|
+
ack();
|
|
283
|
+
return JobActionReceipt;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
async dispatch(raw) {
|
|
288
|
+
if (this.stopped) return;
|
|
289
|
+
const job = this.enrich(raw);
|
|
290
|
+
try {
|
|
291
|
+
await this.cfg.jobHandler(job);
|
|
292
|
+
} catch (err) {
|
|
293
|
+
try {
|
|
294
|
+
this.transport.failJob(raw.jobKey, 0, String(err));
|
|
295
|
+
} finally {
|
|
296
|
+
this.transport.grantCredits(this.jobType, 1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
stop() {
|
|
301
|
+
this.stopped = true;
|
|
302
|
+
this.transport.unsubscribe(this.jobType);
|
|
303
|
+
}
|
|
304
|
+
close() {
|
|
305
|
+
this.stop();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// src/index.ts
|
|
310
|
+
export * from "@camunda8/orchestration-cluster-api";
|
|
311
|
+
function resolveMode(opts) {
|
|
312
|
+
const fromOpts = opts?.config?.CAMUNDA_TRANSPORT;
|
|
313
|
+
const fromEnv = typeof process !== "undefined" ? process.env?.CAMUNDA_TRANSPORT : void 0;
|
|
314
|
+
return fromOpts ?? fromEnv ?? "auto";
|
|
315
|
+
}
|
|
316
|
+
function baseFrom(restAddress) {
|
|
317
|
+
return normalizeBase(restAddress).replace(/\/v2$/, "");
|
|
318
|
+
}
|
|
319
|
+
function createCamundaClient(opts) {
|
|
320
|
+
const client = createCamundaClientBase(opts);
|
|
321
|
+
const mode = resolveMode(opts);
|
|
322
|
+
if (mode === "rest") return client;
|
|
323
|
+
const restAddress = client.getConfig().restAddress;
|
|
324
|
+
const base = baseFrom(restAddress);
|
|
325
|
+
let transport = null;
|
|
326
|
+
let nano;
|
|
327
|
+
const ensure = async () => {
|
|
328
|
+
if (nano === void 0) {
|
|
329
|
+
nano = mode === "command-stream" ? { engine: "nanobpmn", commandStreamPath: "/command-stream" } : await detectNano(base);
|
|
330
|
+
}
|
|
331
|
+
if (!nano) return null;
|
|
332
|
+
if (!transport) transport = new CommandStreamTransport(base, nano.commandStreamPath);
|
|
333
|
+
return transport;
|
|
334
|
+
};
|
|
335
|
+
return new Proxy(client, {
|
|
336
|
+
get(target, prop, receiver) {
|
|
337
|
+
if (prop === "createProcessInstance") {
|
|
338
|
+
return (input, options) => {
|
|
339
|
+
const useStream = ensure();
|
|
340
|
+
return useStream.then(async (t) => {
|
|
341
|
+
if (!t) return target.createProcessInstance(input, options);
|
|
342
|
+
const r = await t.createInstance({
|
|
343
|
+
processDefinitionId: input?.processDefinitionId,
|
|
344
|
+
processDefinitionKey: input?.processDefinitionKey,
|
|
345
|
+
variables: input?.variables,
|
|
346
|
+
awaitCompletion: input?.awaitCompletion ?? false,
|
|
347
|
+
fetchVariables: input?.fetchVariables
|
|
348
|
+
});
|
|
349
|
+
if (r.status >= 400) throw new Error(`createInstance failed: ${r.status} ${JSON.stringify(r.body)}`);
|
|
350
|
+
const body = r.body ?? {};
|
|
351
|
+
return r.completion ? { ...body, variables: r.completion.variables, processInstanceKey: r.completion.processInstanceKey } : body;
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (prop === "createJobWorker") {
|
|
356
|
+
return (cfg) => {
|
|
357
|
+
const w = new NanoJobWorker(null, { ...cfg, autoStart: false });
|
|
358
|
+
void ensure().then((t) => {
|
|
359
|
+
if (t) {
|
|
360
|
+
w.bindTransport(t);
|
|
361
|
+
void w.start();
|
|
362
|
+
} else target.createJobWorker(cfg);
|
|
363
|
+
});
|
|
364
|
+
return w;
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
if (prop === "stopAllWorkers") {
|
|
368
|
+
return () => {
|
|
369
|
+
transport?.close();
|
|
370
|
+
return target.stopAllWorkers();
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return Reflect.get(target, prop, receiver);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
var index_default = createCamundaClient;
|
|
378
|
+
export {
|
|
379
|
+
CommandStreamTransport,
|
|
380
|
+
NanoJobWorker,
|
|
381
|
+
createCamundaClient,
|
|
382
|
+
index_default as default,
|
|
383
|
+
detectNano
|
|
384
|
+
};
|
|
385
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/ws.ts","../src/transport.ts","../src/nanoWorker.ts"],"sourcesContent":["// @nanobpm/nano-sdk — a drop-in replacement for @camunda8/orchestration-cluster-api.\n//\n// It re-exports the entire upstream SDK surface, then overrides\n// `createCamundaClient` (and the default export) so that, when connected to a\n// Nano server, process-instance creation and job workers transparently upgrade\n// to Nano's command-stream protocol. Against stock Camunda 8 it is a no-op: the\n// upstream REST behaviour is used unchanged.\nimport {\n createCamundaClient as createCamundaClientBase,\n} from \"@camunda8/orchestration-cluster-api\";\nimport { detectNano, normalizeBase, type NanoInfo } from \"./detect.js\";\nimport { CommandStreamTransport } from \"./transport.js\";\nimport { NanoJobWorker } from \"./nanoWorker.js\";\n\n// Re-export everything else so consumers can swap the import path and nothing\n// breaks.\nexport * from \"@camunda8/orchestration-cluster-api\";\nexport { detectNano, type NanoInfo } from \"./detect.js\";\nexport { CommandStreamTransport } from \"./transport.js\";\nexport { NanoJobWorker } from \"./nanoWorker.js\";\n\n/** auto: upgrade only on Nano. command-stream: force. rest: never upgrade. */\nexport type NanoTransport = \"auto\" | \"command-stream\" | \"rest\";\n\ntype AnyOpts = Parameters<typeof createCamundaClientBase>[0] & {\n config?: Record<string, unknown> & { CAMUNDA_TRANSPORT?: NanoTransport; CAMUNDA_REST_ADDRESS?: string };\n};\n\nfunction resolveMode(opts?: AnyOpts): NanoTransport {\n const fromOpts = opts?.config?.CAMUNDA_TRANSPORT as NanoTransport | undefined;\n const fromEnv = (typeof process !== \"undefined\" ? process.env?.CAMUNDA_TRANSPORT : undefined) as NanoTransport | undefined;\n return fromOpts ?? fromEnv ?? \"auto\";\n}\n\n/** restAddress is normalized by the SDK to end with /v2; strip it for base. */\nfunction baseFrom(restAddress: string): string {\n return normalizeBase(restAddress).replace(/\\/v2$/, \"\");\n}\n\n/**\n * Drop-in replacement for the upstream createCamundaClient. Returns the upstream\n * client wrapped in a Proxy that upgrades createProcessInstance + createJobWorker\n * to the command stream when connected to a Nano server (overridable via the\n * CAMUNDA_TRANSPORT config: \"auto\" | \"command-stream\" | \"rest\").\n */\nexport function createCamundaClient(opts?: AnyOpts): ReturnType<typeof createCamundaClientBase> {\n const client = createCamundaClientBase(opts as any);\n const mode = resolveMode(opts);\n if (mode === \"rest\") return client;\n\n const restAddress = client.getConfig().restAddress;\n const base = baseFrom(restAddress);\n\n let transport: CommandStreamTransport | null = null;\n let nano: NanoInfo | null | undefined; // undefined = not yet probed\n\n const ensure = async (): Promise<CommandStreamTransport | null> => {\n if (nano === undefined) {\n nano = mode === \"command-stream\"\n ? { engine: \"nanobpmn\", commandStreamPath: \"/command-stream\" }\n : await detectNano(base);\n }\n if (!nano) return null;\n if (!transport) transport = new CommandStreamTransport(base, nano.commandStreamPath);\n return transport;\n };\n\n return new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === \"createProcessInstance\") {\n return (input: any, options?: any) => {\n const useStream = ensure();\n return useStream.then(async (t) => {\n if (!t) return (target as any).createProcessInstance(input, options);\n const r = await t.createInstance({\n processDefinitionId: input?.processDefinitionId,\n processDefinitionKey: input?.processDefinitionKey,\n variables: input?.variables,\n awaitCompletion: input?.awaitCompletion ?? false,\n fetchVariables: input?.fetchVariables,\n });\n if (r.status >= 400) throw new Error(`createInstance failed: ${r.status} ${JSON.stringify(r.body)}`);\n const body: any = r.body ?? {};\n return r.completion ? { ...body, variables: r.completion.variables, processInstanceKey: r.completion.processInstanceKey } : body;\n });\n };\n }\n if (prop === \"createJobWorker\") {\n return (cfg: any) => {\n const w = new NanoJobWorker(null as any, { ...cfg, autoStart: false });\n void ensure().then((t) => {\n if (t) { w.bindTransport(t); void w.start(); }\n else (target as any).createJobWorker(cfg);\n });\n return w as any;\n };\n }\n if (prop === \"stopAllWorkers\") {\n return () => {\n transport?.close();\n return (target as any).stopAllWorkers();\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\nexport default createCamundaClient;\n","// Nano server detection. A nanobpmn gateway advertises itself on GET /v2/topology\n// via a `nano` block carrying `commandStreamPath`. Detecting it lets the SDK\n// upgrade to the command-stream protocol transparently.\n\nexport interface NanoInfo {\n engine: string;\n version?: string;\n commandStreamPath: string;\n}\n\nconst cache = new Map<string, NanoInfo | null>();\n\n/** Strip trailing slashes for a tidy REST base. */\nexport function normalizeBase(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Probes a REST address once (cached) and returns the Nano engine info if the\n * server is a nanobpmn gateway, or null for stock Camunda 8.\n */\nexport async function detectNano(\n restAddress: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<NanoInfo | null> {\n const base = normalizeBase(restAddress);\n if (cache.has(base)) return cache.get(base) ?? null;\n let info: NanoInfo | null = null;\n try {\n const res = await fetchImpl(`${base}/v2/topology`, {\n headers: { accept: \"application/json\" },\n });\n if (res.ok) {\n const body: any = await res.json().catch(() => ({}));\n const nano = body?.nano;\n if (nano && typeof nano.commandStreamPath === \"string\") {\n info = {\n engine: String(nano.engine ?? \"nanobpmn\"),\n version: nano.version ? String(nano.version) : undefined,\n commandStreamPath: nano.commandStreamPath,\n };\n }\n }\n } catch {\n info = null; // unreachable or non-Nano: stay on REST\n }\n cache.set(base, info);\n return info;\n}\n\n/** Test seam: clear the detection cache. */\nexport function _clearDetectionCache(): void {\n cache.clear();\n}\n\n/** Build the ws:// or wss:// command-stream URL from a REST base + path. */\nexport function commandStreamUrl(restAddress: string, path: string): string {\n let base = normalizeBase(restAddress);\n if (base.startsWith(\"https://\")) base = \"wss://\" + base.slice(\"https://\".length);\n else if (base.startsWith(\"http://\")) base = \"ws://\" + base.slice(\"http://\".length);\n return base + path;\n}\n","// Picks a WebSocket implementation: native global (browser/Deno/Node>=22) or the\n// `ws` package (Node). Kept tiny so the transport stays runtime-agnostic.\nexport type WSImpl = new (url: string) => WebSocket;\n\nlet cached: WSImpl | null = null;\n\nexport async function getWebSocket(): Promise<WSImpl> {\n if (cached) return cached;\n const g = globalThis as { WebSocket?: WSImpl };\n if (typeof g.WebSocket === \"function\") {\n cached = g.WebSocket;\n return cached;\n }\n const mod = await import(\"ws\");\n cached = (mod.default ?? mod.WebSocket) as unknown as WSImpl;\n return cached;\n}\n","// Command-stream transport. One persistent WebSocket per client multiplexes:\n// * createInstance (corr-correlated CommandResult, awaitCompletion via\n// InstanceCompleted), and\n// * job subscriptions (welcome -> subscribe -> job push, replenished by\n// jobCredits; completeJob/failJob/throwError as unmetered drains).\n// Mirrors the protocol implemented by the engine (server/src/command_stream.rs)\n// and the embedded worker SDK (server/src/console/worker_sdk.ts).\nimport { commandStreamUrl } from \"./detect.js\";\nimport { getWebSocket } from \"./ws.js\";\n\ntype Json = Record<string, unknown>;\n\nexport interface JobFrame {\n jobKey: string;\n type: string;\n processInstanceKey: string;\n variables?: Record<string, unknown>;\n customHeaders?: Record<string, unknown>;\n retries?: number;\n [k: string]: unknown;\n}\n\nexport interface Subscription {\n jobType: string;\n worker: string;\n credits: number;\n timeoutMs: number;\n fetchVariables: string[] | null;\n onJob: (job: JobFrame) => void;\n}\n\ninterface Pending {\n resolve: (v: { status: number; body: unknown }) => void;\n reject: (e: unknown) => void;\n}\n\nexport class CommandStreamTransport {\n private url: string;\n private ws: WebSocket | null = null;\n private open = false;\n private corr = 0;\n private pending = new Map<number, Pending>();\n private awaits = new Map<number, (v: { processCompleted: boolean; variables: unknown; processInstanceKey: string }) => void>();\n private subs = new Map<string, Subscription>();\n private heartbeat: ReturnType<typeof setInterval> | null = null;\n private connectPromise: Promise<void> | null = null;\n private closed = false;\n\n constructor(restAddress: string, path: string) {\n this.url = commandStreamUrl(restAddress, path);\n }\n\n private nextCorr(): number {\n this.corr += 1;\n return this.corr;\n }\n\n private send(frame: Json): void {\n if (this.ws && this.open) this.ws.send(JSON.stringify(frame));\n }\n\n async connect(): Promise<void> {\n if (this.open) return;\n if (this.connectPromise) return this.connectPromise;\n this.connectPromise = new Promise<void>((resolve, reject) => {\n void getWebSocket().then((WS) => {\n const ws = new WS(this.url);\n this.ws = ws;\n ws.onopen = () => {};\n ws.onmessage = (ev: MessageEvent) => {\n let f: Json;\n try {\n f = JSON.parse(typeof ev.data === \"string\" ? ev.data : String(ev.data));\n } catch {\n return;\n }\n this.handle(f, resolve);\n };\n ws.onerror = () => {\n if (!this.open) reject(new Error(`command-stream connect failed: ${this.url}`));\n };\n ws.onclose = () => {\n this.open = false;\n if (this.heartbeat) clearInterval(this.heartbeat);\n // Reject in-flight commands; resubscribe on reconnect.\n for (const p of this.pending.values()) p.reject(new Error(\"command-stream closed\"));\n this.pending.clear();\n if (!this.closed) setTimeout(() => void this.reconnect(), 1000);\n };\n });\n });\n return this.connectPromise;\n }\n\n private async reconnect(): Promise<void> {\n this.connectPromise = null;\n this.open = false;\n await this.connect();\n for (const sub of this.subs.values()) this.sendSubscribe(sub);\n }\n\n private handle(f: Json, resolveConnect: () => void): void {\n switch (f.type) {\n case \"welcome\": {\n this.open = true;\n const hb = Number(f.heartbeatMs ?? 0);\n if (hb > 0) {\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = setInterval(() => this.send({ type: \"heartbeat\" }), hb);\n }\n for (const sub of this.subs.values()) this.sendSubscribe(sub);\n resolveConnect();\n break;\n }\n case \"job\": {\n const job = (f.job as JobFrame) ?? ({} as JobFrame);\n const sub = this.subs.get(job.type);\n if (sub) sub.onJob(job);\n break;\n }\n case \"commandResult\": {\n const corr = Number(f.corr ?? 0);\n const p = this.pending.get(corr);\n if (p) {\n this.pending.delete(corr);\n p.resolve({ status: Number(f.status ?? 0), body: f.body ?? null });\n }\n break;\n }\n case \"instanceCompleted\": {\n const corr = Number(f.corr ?? 0);\n const cb = this.awaits.get(corr);\n if (cb) {\n this.awaits.delete(corr);\n cb({\n processCompleted: Boolean(f.processCompleted),\n variables: f.variables ?? {},\n processInstanceKey: String(f.processInstanceKey ?? \"\"),\n });\n }\n break;\n }\n // submissionCredits / pressure / heartbeat: no client action needed yet.\n }\n }\n\n private sendSubscribe(sub: Subscription): void {\n this.send({\n type: \"subscribe\",\n jobType: sub.jobType,\n jobCredits: sub.credits,\n worker: sub.worker,\n timeout: sub.timeoutMs,\n fetchVariable: sub.fetchVariables,\n });\n }\n\n /** Create a process instance over the stream. */\n async createInstance(input: {\n processDefinitionId?: string;\n processDefinitionKey?: string;\n variables?: Record<string, unknown>;\n awaitCompletion?: boolean;\n fetchVariables?: string[];\n requestTimeoutMs?: number;\n }): Promise<{ status: number; body: unknown; completion?: { processCompleted: boolean; variables: unknown; processInstanceKey: string } }> {\n await this.connect();\n const corr = this.nextCorr();\n let completionResolve: ((v: { processCompleted: boolean; variables: unknown; processInstanceKey: string }) => void) | null = null;\n const completion = input.awaitCompletion\n ? new Promise<{ processCompleted: boolean; variables: unknown; processInstanceKey: string }>((r) => (completionResolve = r))\n : null;\n if (completionResolve) this.awaits.set(corr, completionResolve);\n const result = await new Promise<{ status: number; body: unknown }>((resolve, reject) => {\n this.pending.set(corr, { resolve, reject });\n this.send({\n type: \"createInstance\",\n corr,\n processDefinitionId: input.processDefinitionId ?? null,\n processDefinitionKey: input.processDefinitionKey ?? null,\n variables: input.variables ?? null,\n awaitCompletion: input.awaitCompletion ?? false,\n fetchVariables: input.fetchVariables ?? null,\n requestTimeout: input.requestTimeoutMs ?? null,\n });\n });\n const done = completion ? await completion : undefined;\n return { ...result, completion: done };\n }\n\n /** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */\n async subscribe(sub: Subscription): Promise<void> {\n this.subs.set(sub.jobType, sub);\n await this.connect();\n this.sendSubscribe(sub);\n }\n\n unsubscribe(jobType: string): void {\n this.subs.delete(jobType);\n }\n\n completeJob(jobKey: string, variables?: Record<string, unknown>): void {\n this.send({ type: \"completeJob\", corr: this.nextCorr(), jobKey, variables: variables ?? null });\n }\n failJob(jobKey: string, retries?: number, errorMessage?: string): void {\n this.send({ type: \"failJob\", corr: this.nextCorr(), jobKey, retries: retries ?? 0, errorMessage: errorMessage ?? null });\n }\n throwError(jobKey: string, errorCode: string, errorMessage?: string): void {\n this.send({ type: \"throwError\", corr: this.nextCorr(), jobKey, errorCode, errorMessage: errorMessage ?? null });\n }\n grantCredits(jobType: string, n: number): void {\n this.send({ type: \"jobCredits\", jobType, n });\n }\n\n close(): void {\n this.closed = true;\n if (this.heartbeat) clearInterval(this.heartbeat);\n try {\n this.ws?.close(1000, \"shutdown\");\n } catch {\n /* ignore */\n }\n }\n}\n","// A drop-in JobWorker that drains jobs over the command stream. Mirrors the\n// upstream JobWorker surface (close/stop/start) but receives pushed jobs instead\n// of long-polling. Each handled job replenishes one credit, keeping demand at\n// maxParallelJobs.\nimport { JobActionReceiptSymbol as JobActionReceipt } from \"@camunda8/orchestration-cluster-api\";\nimport type { CommandStreamTransport, JobFrame } from \"./transport.js\";\n\nexport interface NanoJobWorkerConfig {\n jobType: string;\n workerName?: string;\n maxParallelJobs?: number;\n jobTimeoutMs?: number;\n fetchVariables?: string[];\n jobHandler: (job: any) => Promise<typeof JobActionReceipt> | typeof JobActionReceipt;\n autoStart?: boolean;\n}\n\nexport class NanoJobWorker {\n private transport: CommandStreamTransport;\n private cfg: NanoJobWorkerConfig;\n private credits: number;\n private stopped = false;\n readonly jobType: string;\n readonly name: string;\n\n constructor(transport: CommandStreamTransport, cfg: NanoJobWorkerConfig) {\n this.transport = transport;\n this.cfg = cfg;\n this.jobType = cfg.jobType;\n this.credits = cfg.maxParallelJobs ?? 10;\n this.name = cfg.workerName || `worker-${cfg.jobType}`;\n if (cfg.autoStart !== false) void this.start();\n }\n\n /** Bind the transport after async Nano detection. */\n bindTransport(transport: CommandStreamTransport): void {\n this.transport = transport;\n }\n\n async start(): Promise<void> {\n this.stopped = false;\n await this.transport.subscribe({\n jobType: this.jobType,\n worker: this.name,\n credits: this.credits,\n timeoutMs: this.cfg.jobTimeoutMs ?? 60_000,\n fetchVariables: this.cfg.fetchVariables ?? null,\n onJob: (raw) => void this.dispatch(raw),\n });\n }\n\n private enrich(raw: JobFrame) {\n let acted = false;\n const ack = (replenish = true) => {\n if (!acted) {\n acted = true;\n if (replenish) this.transport.grantCredits(this.jobType, 1);\n }\n };\n return {\n ...raw,\n complete: async (variables: Record<string, unknown> = {}) => {\n this.transport.completeJob(raw.jobKey, variables);\n ack();\n return JobActionReceipt;\n },\n fail: async (reason: { errorMessage?: string; retries?: number } = {}) => {\n this.transport.failJob(raw.jobKey, reason.retries, reason.errorMessage);\n ack();\n return JobActionReceipt;\n },\n error: async (e: { errorCode: string; errorMessage?: string }) => {\n this.transport.throwError(raw.jobKey, e.errorCode, e.errorMessage);\n ack();\n return JobActionReceipt;\n },\n ignore: async () => {\n ack();\n return JobActionReceipt;\n },\n };\n }\n\n private async dispatch(raw: JobFrame): Promise<void> {\n if (this.stopped) return;\n const job = this.enrich(raw);\n try {\n await this.cfg.jobHandler(job);\n } catch (err) {\n try {\n this.transport.failJob(raw.jobKey, 0, String(err));\n } finally {\n this.transport.grantCredits(this.jobType, 1);\n }\n }\n }\n\n stop(): void {\n this.stopped = true;\n this.transport.unsubscribe(this.jobType);\n }\n close(): void {\n this.stop();\n }\n}\n"],"mappings":";AAOA;AAAA,EACE,uBAAuB;AAAA,OAClB;;;ACCP,IAAM,QAAQ,oBAAI,IAA6B;AAGxC,SAAS,cAAc,KAAqB;AACjD,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;AAMA,eAAsB,WACpB,aACA,YAA0B,OACA;AAC1B,QAAM,OAAO,cAAc,WAAW;AACtC,MAAI,MAAM,IAAI,IAAI,EAAG,QAAO,MAAM,IAAI,IAAI,KAAK;AAC/C,MAAI,OAAwB;AAC5B,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,GAAG,IAAI,gBAAgB;AAAA,MACjD,SAAS,EAAE,QAAQ,mBAAmB;AAAA,IACxC,CAAC;AACD,QAAI,IAAI,IAAI;AACV,YAAM,OAAY,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACnD,YAAM,OAAO,MAAM;AACnB,UAAI,QAAQ,OAAO,KAAK,sBAAsB,UAAU;AACtD,eAAO;AAAA,UACL,QAAQ,OAAO,KAAK,UAAU,UAAU;AAAA,UACxC,SAAS,KAAK,UAAU,OAAO,KAAK,OAAO,IAAI;AAAA,UAC/C,mBAAmB,KAAK;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,IAAI;AACpB,SAAO;AACT;AAQO,SAAS,iBAAiB,aAAqB,MAAsB;AAC1E,MAAI,OAAO,cAAc,WAAW;AACpC,MAAI,KAAK,WAAW,UAAU,EAAG,QAAO,WAAW,KAAK,MAAM,WAAW,MAAM;AAAA,WACtE,KAAK,WAAW,SAAS,EAAG,QAAO,UAAU,KAAK,MAAM,UAAU,MAAM;AACjF,SAAO,OAAO;AAChB;;;ACzDA,IAAI,SAAwB;AAE5B,eAAsB,eAAgC;AACpD,MAAI,OAAQ,QAAO;AACnB,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,cAAc,YAAY;AACrC,aAAS,EAAE;AACX,WAAO;AAAA,EACT;AACA,QAAM,MAAM,MAAM,OAAO,IAAI;AAC7B,WAAU,IAAI,WAAW,IAAI;AAC7B,SAAO;AACT;;;ACoBO,IAAM,yBAAN,MAA6B;AAAA,EAC1B;AAAA,EACA,KAAuB;AAAA,EACvB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU,oBAAI,IAAqB;AAAA,EACnC,SAAS,oBAAI,IAAwG;AAAA,EACrH,OAAO,oBAAI,IAA0B;AAAA,EACrC,YAAmD;AAAA,EACnD,iBAAuC;AAAA,EACvC,SAAS;AAAA,EAEjB,YAAY,aAAqB,MAAc;AAC7C,SAAK,MAAM,iBAAiB,aAAa,IAAI;AAAA,EAC/C;AAAA,EAEQ,WAAmB;AACzB,SAAK,QAAQ;AACb,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,KAAK,OAAmB;AAC9B,QAAI,KAAK,MAAM,KAAK,KAAM,MAAK,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,KAAM;AACf,QAAI,KAAK,eAAgB,QAAO,KAAK;AACrC,SAAK,iBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3D,WAAK,aAAa,EAAE,KAAK,CAAC,OAAO;AAC/B,cAAM,KAAK,IAAI,GAAG,KAAK,GAAG;AAC1B,aAAK,KAAK;AACV,WAAG,SAAS,MAAM;AAAA,QAAC;AACnB,WAAG,YAAY,CAAC,OAAqB;AACnC,cAAI;AACJ,cAAI;AACF,gBAAI,KAAK,MAAM,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO,OAAO,GAAG,IAAI,CAAC;AAAA,UACxE,QAAQ;AACN;AAAA,UACF;AACA,eAAK,OAAO,GAAG,OAAO;AAAA,QACxB;AACA,WAAG,UAAU,MAAM;AACjB,cAAI,CAAC,KAAK,KAAM,QAAO,IAAI,MAAM,kCAAkC,KAAK,GAAG,EAAE,CAAC;AAAA,QAChF;AACA,WAAG,UAAU,MAAM;AACjB,eAAK,OAAO;AACZ,cAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAEhD,qBAAW,KAAK,KAAK,QAAQ,OAAO,EAAG,GAAE,OAAO,IAAI,MAAM,uBAAuB,CAAC;AAClF,eAAK,QAAQ,MAAM;AACnB,cAAI,CAAC,KAAK,OAAQ,YAAW,MAAM,KAAK,KAAK,UAAU,GAAG,GAAI;AAAA,QAChE;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,YAA2B;AACvC,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,UAAM,KAAK,QAAQ;AACnB,eAAW,OAAO,KAAK,KAAK,OAAO,EAAG,MAAK,cAAc,GAAG;AAAA,EAC9D;AAAA,EAEQ,OAAO,GAAS,gBAAkC;AACxD,YAAQ,EAAE,MAAM;AAAA,MACd,KAAK,WAAW;AACd,aAAK,OAAO;AACZ,cAAM,KAAK,OAAO,EAAE,eAAe,CAAC;AACpC,YAAI,KAAK,GAAG;AACV,cAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAChD,eAAK,YAAY,YAAY,MAAM,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC,GAAG,EAAE;AAAA,QACzE;AACA,mBAAW,OAAO,KAAK,KAAK,OAAO,EAAG,MAAK,cAAc,GAAG;AAC5D,uBAAe;AACf;AAAA,MACF;AAAA,MACA,KAAK,OAAO;AACV,cAAM,MAAO,EAAE,OAAqB,CAAC;AACrC,cAAM,MAAM,KAAK,KAAK,IAAI,IAAI,IAAI;AAClC,YAAI,IAAK,KAAI,MAAM,GAAG;AACtB;AAAA,MACF;AAAA,MACA,KAAK,iBAAiB;AACpB,cAAM,OAAO,OAAO,EAAE,QAAQ,CAAC;AAC/B,cAAM,IAAI,KAAK,QAAQ,IAAI,IAAI;AAC/B,YAAI,GAAG;AACL,eAAK,QAAQ,OAAO,IAAI;AACxB,YAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,UAAU,CAAC,GAAG,MAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,QACnE;AACA;AAAA,MACF;AAAA,MACA,KAAK,qBAAqB;AACxB,cAAM,OAAO,OAAO,EAAE,QAAQ,CAAC;AAC/B,cAAM,KAAK,KAAK,OAAO,IAAI,IAAI;AAC/B,YAAI,IAAI;AACN,eAAK,OAAO,OAAO,IAAI;AACvB,aAAG;AAAA,YACD,kBAAkB,QAAQ,EAAE,gBAAgB;AAAA,YAC5C,WAAW,EAAE,aAAa,CAAC;AAAA,YAC3B,oBAAoB,OAAO,EAAE,sBAAsB,EAAE;AAAA,UACvD,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,IAEF;AAAA,EACF;AAAA,EAEQ,cAAc,KAAyB;AAC7C,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,MACb,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI;AAAA,MACZ,SAAS,IAAI;AAAA,MACb,eAAe,IAAI;AAAA,IACrB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,eAAe,OAOsH;AACzI,UAAM,KAAK,QAAQ;AACnB,UAAM,OAAO,KAAK,SAAS;AAC3B,QAAI,oBAAyH;AAC7H,UAAM,aAAa,MAAM,kBACrB,IAAI,QAAuF,CAAC,MAAO,oBAAoB,CAAE,IACzH;AACJ,QAAI,kBAAmB,MAAK,OAAO,IAAI,MAAM,iBAAiB;AAC9D,UAAM,SAAS,MAAM,IAAI,QAA2C,CAAC,SAAS,WAAW;AACvF,WAAK,QAAQ,IAAI,MAAM,EAAE,SAAS,OAAO,CAAC;AAC1C,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN;AAAA,QACA,qBAAqB,MAAM,uBAAuB;AAAA,QAClD,sBAAsB,MAAM,wBAAwB;AAAA,QACpD,WAAW,MAAM,aAAa;AAAA,QAC9B,iBAAiB,MAAM,mBAAmB;AAAA,QAC1C,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,gBAAgB,MAAM,oBAAoB;AAAA,MAC5C,CAAC;AAAA,IACH,CAAC;AACD,UAAM,OAAO,aAAa,MAAM,aAAa;AAC7C,WAAO,EAAE,GAAG,QAAQ,YAAY,KAAK;AAAA,EACvC;AAAA;AAAA,EAGA,MAAM,UAAU,KAAkC;AAChD,SAAK,KAAK,IAAI,IAAI,SAAS,GAAG;AAC9B,UAAM,KAAK,QAAQ;AACnB,SAAK,cAAc,GAAG;AAAA,EACxB;AAAA,EAEA,YAAY,SAAuB;AACjC,SAAK,KAAK,OAAO,OAAO;AAAA,EAC1B;AAAA,EAEA,YAAY,QAAgB,WAA2C;AACrE,SAAK,KAAK,EAAE,MAAM,eAAe,MAAM,KAAK,SAAS,GAAG,QAAQ,WAAW,aAAa,KAAK,CAAC;AAAA,EAChG;AAAA,EACA,QAAQ,QAAgB,SAAkB,cAA6B;AACrE,SAAK,KAAK,EAAE,MAAM,WAAW,MAAM,KAAK,SAAS,GAAG,QAAQ,SAAS,WAAW,GAAG,cAAc,gBAAgB,KAAK,CAAC;AAAA,EACzH;AAAA,EACA,WAAW,QAAgB,WAAmB,cAA6B;AACzE,SAAK,KAAK,EAAE,MAAM,cAAc,MAAM,KAAK,SAAS,GAAG,QAAQ,WAAW,cAAc,gBAAgB,KAAK,CAAC;AAAA,EAChH;AAAA,EACA,aAAa,SAAiB,GAAiB;AAC7C,SAAK,KAAK,EAAE,MAAM,cAAc,SAAS,EAAE,CAAC;AAAA,EAC9C;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AACd,QAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAChD,QAAI;AACF,WAAK,IAAI,MAAM,KAAM,UAAU;AAAA,IACjC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AC3NA,SAAS,0BAA0B,wBAAwB;AAapD,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACT;AAAA,EACA;AAAA,EAET,YAAY,WAAmC,KAA0B;AACvE,SAAK,YAAY;AACjB,SAAK,MAAM;AACX,SAAK,UAAU,IAAI;AACnB,SAAK,UAAU,IAAI,mBAAmB;AACtC,SAAK,OAAO,IAAI,cAAc,UAAU,IAAI,OAAO;AACnD,QAAI,IAAI,cAAc,MAAO,MAAK,KAAK,MAAM;AAAA,EAC/C;AAAA;AAAA,EAGA,cAAc,WAAyC;AACrD,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,UAAU;AACf,UAAM,KAAK,UAAU,UAAU;AAAA,MAC7B,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,WAAW,KAAK,IAAI,gBAAgB;AAAA,MACpC,gBAAgB,KAAK,IAAI,kBAAkB;AAAA,MAC3C,OAAO,CAAC,QAAQ,KAAK,KAAK,SAAS,GAAG;AAAA,IACxC,CAAC;AAAA,EACH;AAAA,EAEQ,OAAO,KAAe;AAC5B,QAAI,QAAQ;AACZ,UAAM,MAAM,CAAC,YAAY,SAAS;AAChC,UAAI,CAAC,OAAO;AACV,gBAAQ;AACR,YAAI,UAAW,MAAK,UAAU,aAAa,KAAK,SAAS,CAAC;AAAA,MAC5D;AAAA,IACF;AACA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU,OAAO,YAAqC,CAAC,MAAM;AAC3D,aAAK,UAAU,YAAY,IAAI,QAAQ,SAAS;AAChD,YAAI;AACJ,eAAO;AAAA,MACT;AAAA,MACA,MAAM,OAAO,SAAsD,CAAC,MAAM;AACxE,aAAK,UAAU,QAAQ,IAAI,QAAQ,OAAO,SAAS,OAAO,YAAY;AACtE,YAAI;AACJ,eAAO;AAAA,MACT;AAAA,MACA,OAAO,OAAO,MAAoD;AAChE,aAAK,UAAU,WAAW,IAAI,QAAQ,EAAE,WAAW,EAAE,YAAY;AACjE,YAAI;AACJ,eAAO;AAAA,MACT;AAAA,MACA,QAAQ,YAAY;AAClB,YAAI;AACJ,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,KAA8B;AACnD,QAAI,KAAK,QAAS;AAClB,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI;AACF,YAAM,KAAK,IAAI,WAAW,GAAG;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI;AACF,aAAK,UAAU,QAAQ,IAAI,QAAQ,GAAG,OAAO,GAAG,CAAC;AAAA,MACnD,UAAE;AACA,aAAK,UAAU,aAAa,KAAK,SAAS,CAAC;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAa;AACX,SAAK,UAAU;AACf,SAAK,UAAU,YAAY,KAAK,OAAO;AAAA,EACzC;AAAA,EACA,QAAc;AACZ,SAAK,KAAK;AAAA,EACZ;AACF;;;AJxFA,cAAc;AAYd,SAAS,YAAY,MAA+B;AAClD,QAAM,WAAW,MAAM,QAAQ;AAC/B,QAAM,UAAW,OAAO,YAAY,cAAc,QAAQ,KAAK,oBAAoB;AACnF,SAAO,YAAY,WAAW;AAChC;AAGA,SAAS,SAAS,aAA6B;AAC7C,SAAO,cAAc,WAAW,EAAE,QAAQ,SAAS,EAAE;AACvD;AAQO,SAAS,oBAAoB,MAA4D;AAC9F,QAAM,SAAS,wBAAwB,IAAW;AAClD,QAAM,OAAO,YAAY,IAAI;AAC7B,MAAI,SAAS,OAAQ,QAAO;AAE5B,QAAM,cAAc,OAAO,UAAU,EAAE;AACvC,QAAM,OAAO,SAAS,WAAW;AAEjC,MAAI,YAA2C;AAC/C,MAAI;AAEJ,QAAM,SAAS,YAAoD;AACjE,QAAI,SAAS,QAAW;AACtB,aAAO,SAAS,mBACZ,EAAE,QAAQ,YAAY,mBAAmB,kBAAkB,IAC3D,MAAM,WAAW,IAAI;AAAA,IAC3B;AACA,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,CAAC,UAAW,aAAY,IAAI,uBAAuB,MAAM,KAAK,iBAAiB;AACnF,WAAO;AAAA,EACT;AAEA,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,QAAQ,MAAM,UAAU;AAC1B,UAAI,SAAS,yBAAyB;AACpC,eAAO,CAAC,OAAY,YAAkB;AACpC,gBAAM,YAAY,OAAO;AACzB,iBAAO,UAAU,KAAK,OAAO,MAAM;AACjC,gBAAI,CAAC,EAAG,QAAQ,OAAe,sBAAsB,OAAO,OAAO;AACnE,kBAAM,IAAI,MAAM,EAAE,eAAe;AAAA,cAC/B,qBAAqB,OAAO;AAAA,cAC5B,sBAAsB,OAAO;AAAA,cAC7B,WAAW,OAAO;AAAA,cAClB,iBAAiB,OAAO,mBAAmB;AAAA,cAC3C,gBAAgB,OAAO;AAAA,YACzB,CAAC;AACD,gBAAI,EAAE,UAAU,IAAK,OAAM,IAAI,MAAM,0BAA0B,EAAE,MAAM,IAAI,KAAK,UAAU,EAAE,IAAI,CAAC,EAAE;AACnG,kBAAM,OAAY,EAAE,QAAQ,CAAC;AAC7B,mBAAO,EAAE,aAAa,EAAE,GAAG,MAAM,WAAW,EAAE,WAAW,WAAW,oBAAoB,EAAE,WAAW,mBAAmB,IAAI;AAAA,UAC9H,CAAC;AAAA,QACH;AAAA,MACF;AACA,UAAI,SAAS,mBAAmB;AAC9B,eAAO,CAAC,QAAa;AACnB,gBAAM,IAAI,IAAI,cAAc,MAAa,EAAE,GAAG,KAAK,WAAW,MAAM,CAAC;AACrE,eAAK,OAAO,EAAE,KAAK,CAAC,MAAM;AACxB,gBAAI,GAAG;AAAE,gBAAE,cAAc,CAAC;AAAG,mBAAK,EAAE,MAAM;AAAA,YAAG,MACxC,CAAC,OAAe,gBAAgB,GAAG;AAAA,UAC1C,CAAC;AACD,iBAAO;AAAA,QACT;AAAA,MACF;AACA,UAAI,SAAS,kBAAkB;AAC7B,eAAO,MAAM;AACX,qBAAW,MAAM;AACjB,iBAAQ,OAAe,eAAe;AAAA,QACxC;AAAA,MACF;AACA,aAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAAA,IAC3C;AAAA,EACF,CAAC;AACH;AAEA,IAAO,gBAAQ;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nanobpm/nano-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Drop-in replacement for @camunda8/orchestration-cluster-api that upgrades to the Nano command-stream protocol when connected to a Nano server.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/jwulf/nano-sdk-js.git"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.cjs",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"verify": "node scripts/verify.mjs"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@camunda8/orchestration-cluster-api": "10.0.0-alpha.13",
|
|
36
|
+
"ws": "^8.18.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
40
|
+
"@semantic-release/git": "^10.0.1",
|
|
41
|
+
"@types/node": "^20.11.0",
|
|
42
|
+
"@types/ws": "^8.5.10",
|
|
43
|
+
"semantic-release": "^25.0.5",
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"typescript": "^5.4.0",
|
|
46
|
+
"vitest": "^1.3.0"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
}
|
|
51
|
+
}
|