@nanobpm/nano-sdk 1.0.0 → 1.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @nanobpm/nano-sdk
2
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.
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 **falcon** protocol when connected to a [Nano](../nanobpmn) server. Against stock Camunda 8 it behaves exactly like the upstream SDK.
4
4
 
5
5
  ```diff
6
6
  - import { createCamundaClient } from "@camunda8/orchestration-cluster-api";
@@ -11,21 +11,21 @@ Everything else is re-exported unchanged — same types, same client, same API.
11
11
 
12
12
  ## What gets upgraded
13
13
 
14
- When the client detects a Nano server, two hot paths move from REST onto a single persistent WebSocket (`/command-stream`):
14
+ When the client detects a Nano server, two hot paths move from REST onto a single persistent WebSocket (`/falcon`):
15
15
 
16
- - **`createProcessInstance`** → `createInstance` frame (with `awaitCompletion` resolved via `instanceCompleted`).
16
+ - **`createProcessInstance`** → `createInstance` frame, gated on the server's submission-credit window (with `awaitCompletion` resolved via `instanceCompleted`).
17
17
  - **`createJobWorker`** → `subscribe` + pushed `job` frames, acked with `completeJob`/`failJob`/`throwError` and credit-replenished.
18
18
 
19
19
  This avoids the ~125 creates/s/conn ceiling of the REST long-poll path.
20
20
 
21
21
  ## Detection & override
22
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):
23
+ Detection is automatic: the SDK probes `GET /v2/topology` once and looks for the Nano `falconPath` advertisement. Control it via the `CAMUNDA_TRANSPORT` config (or env var):
24
24
 
25
25
  | value | behaviour |
26
26
  | ---------------- | ------------------------------------------- |
27
- | `auto` (default) | command-stream on Nano, REST elsewhere |
28
- | `command-stream` | force the stream (assume Nano) |
27
+ | `auto` (default) | falcon on Nano, REST elsewhere |
28
+ | `falcon` | force the stream (assume Nano) |
29
29
  | `rest` | never upgrade — pure upstream behaviour |
30
30
 
31
31
  ```ts
@@ -34,6 +34,37 @@ const client = createCamundaClient({
34
34
  });
35
35
  ```
36
36
 
37
+ ## Failing fast under backpressure (`submitTimeoutMs`)
38
+
39
+ On the Falcon protocol, admission backpressure is expressed by the server
40
+ withholding submission credits (no `503`, no retry), so a `createProcessInstance`
41
+ otherwise waits indefinitely for the gateway to ack. To fail fast instead, set a
42
+ submit timeout — per call, or client-wide via config/env:
43
+
44
+ ```ts
45
+ // Client-wide default (ms); per-call submitTimeoutMs wins when both are set.
46
+ const client = createCamundaClient({
47
+ config: { CAMUNDA_REST_ADDRESS: "http://localhost:8080", CAMUNDA_NANO_SUBMIT_TIMEOUT_MS: 2000 },
48
+ });
49
+
50
+ import { SubmissionTimeoutError } from "@nanobpm/nano-sdk";
51
+
52
+ try {
53
+ await client.createProcessInstance({ processDefinitionId: "order", submitTimeoutMs: 500 });
54
+ } catch (err) {
55
+ if (err instanceof SubmissionTimeoutError) {
56
+ // Server is applying admission backpressure — back off, do NOT tight-loop retry.
57
+ }
58
+ }
59
+ ```
60
+
61
+ `submitTimeoutMs` is a **client-side** guard: the SDK gates each create on the
62
+ server's submission-credit window (queuing client-side under backpressure rather
63
+ than flooding the gateway), and this bounds how long it waits for a credit before
64
+ rejecting with `SubmissionTimeoutError`. It is never sent on the wire. On timeout
65
+ the queued create is dropped so no credit slot leaks. Omit it (the default) to
66
+ wait indefinitely.
67
+
37
68
  ## Build & verify
38
69
 
39
70
  ```sh
package/dist/index.cjs CHANGED
@@ -31,8 +31,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
- CommandStreamTransport: () => CommandStreamTransport,
34
+ EmbeddedTransport: () => EmbeddedTransport,
35
+ FalconTransport: () => FalconTransport,
35
36
  NanoJobWorker: () => NanoJobWorker,
37
+ SubmissionTimeoutError: () => SubmissionTimeoutError,
36
38
  createCamundaClient: () => createCamundaClient,
37
39
  default: () => index_default,
38
40
  detectNano: () => detectNano
@@ -56,11 +58,11 @@ async function detectNano(restAddress, fetchImpl = fetch) {
56
58
  if (res.ok) {
57
59
  const body = await res.json().catch(() => ({}));
58
60
  const nano = body?.nano;
59
- if (nano && typeof nano.commandStreamPath === "string") {
61
+ if (nano && typeof nano.falconPath === "string") {
60
62
  info = {
61
63
  engine: String(nano.engine ?? "nanobpmn"),
62
64
  version: nano.version ? String(nano.version) : void 0,
63
- commandStreamPath: nano.commandStreamPath
65
+ falconPath: nano.falconPath
64
66
  };
65
67
  }
66
68
  }
@@ -70,7 +72,7 @@ async function detectNano(restAddress, fetchImpl = fetch) {
70
72
  cache.set(base, info);
71
73
  return info;
72
74
  }
73
- function commandStreamUrl(restAddress, path) {
75
+ function falconUrl(restAddress, path) {
74
76
  let base = normalizeBase(restAddress);
75
77
  if (base.startsWith("https://")) base = "wss://" + base.slice("https://".length);
76
78
  else if (base.startsWith("http://")) base = "ws://" + base.slice("http://".length);
@@ -92,7 +94,17 @@ async function getWebSocket() {
92
94
  }
93
95
 
94
96
  // src/transport.ts
95
- var CommandStreamTransport = class {
97
+ var SubmissionTimeoutError = class extends Error {
98
+ constructor(timeoutMs) {
99
+ super(
100
+ `create submission stalled: no gateway ack within ${timeoutMs}ms (server is applying admission backpressure)`
101
+ );
102
+ this.timeoutMs = timeoutMs;
103
+ this.name = "SubmissionTimeoutError";
104
+ }
105
+ timeoutMs;
106
+ };
107
+ var FalconTransport = class {
96
108
  url;
97
109
  ws = null;
98
110
  open = false;
@@ -103,8 +115,17 @@ var CommandStreamTransport = class {
103
115
  heartbeat = null;
104
116
  connectPromise = null;
105
117
  closed = false;
106
- constructor(restAddress, path) {
107
- this.url = commandStreamUrl(restAddress, path);
118
+ defaultSubmitTimeoutMs;
119
+ /**
120
+ * Server-granted submission-credit window (seeded by `welcome`, topped up by
121
+ * `submissionCredits`). Mirrors the engine's intake metering so creates queue
122
+ * client-side under admission backpressure instead of flooding the gateway.
123
+ */
124
+ credits = 0;
125
+ creditWaiters = [];
126
+ constructor(restAddress, path, defaultSubmitTimeoutMs) {
127
+ this.url = falconUrl(restAddress, path);
128
+ this.defaultSubmitTimeoutMs = defaultSubmitTimeoutMs !== void 0 && defaultSubmitTimeoutMs > 0 ? defaultSubmitTimeoutMs : void 0;
108
129
  }
109
130
  nextCorr() {
110
131
  this.corr += 1;
@@ -132,12 +153,16 @@ var CommandStreamTransport = class {
132
153
  this.handle(f, resolve);
133
154
  };
134
155
  ws.onerror = () => {
135
- if (!this.open) reject(new Error(`command-stream connect failed: ${this.url}`));
156
+ if (!this.open) reject(new Error(`falcon connect failed: ${this.url}`));
136
157
  };
137
158
  ws.onclose = () => {
138
159
  this.open = false;
160
+ this.credits = 0;
161
+ const waiters = this.creditWaiters;
162
+ this.creditWaiters = [];
163
+ for (const w of waiters) w.fail(new Error("falcon closed"));
139
164
  if (this.heartbeat) clearInterval(this.heartbeat);
140
- for (const p of this.pending.values()) p.reject(new Error("command-stream closed"));
165
+ for (const p of this.pending.values()) p.reject(new Error("falcon closed"));
141
166
  this.pending.clear();
142
167
  if (!this.closed) setTimeout(() => void this.reconnect(), 1e3);
143
168
  };
@@ -155,12 +180,14 @@ var CommandStreamTransport = class {
155
180
  switch (f.type) {
156
181
  case "welcome": {
157
182
  this.open = true;
183
+ this.credits = Number(f.submissionCredits ?? 0);
158
184
  const hb = Number(f.heartbeatMs ?? 0);
159
185
  if (hb > 0) {
160
186
  if (this.heartbeat) clearInterval(this.heartbeat);
161
187
  this.heartbeat = setInterval(() => this.send({ type: "heartbeat" }), hb);
162
188
  }
163
189
  for (const sub of this.subs.values()) this.sendSubscribe(sub);
190
+ this.releaseCreditWaiters();
164
191
  resolveConnect();
165
192
  break;
166
193
  }
@@ -192,6 +219,11 @@ var CommandStreamTransport = class {
192
219
  }
193
220
  break;
194
221
  }
222
+ case "submissionCredits": {
223
+ this.credits += Number(f.n ?? 0);
224
+ this.releaseCreditWaiters();
225
+ break;
226
+ }
195
227
  }
196
228
  }
197
229
  sendSubscribe(sub) {
@@ -204,9 +236,51 @@ var CommandStreamTransport = class {
204
236
  fetchVariable: sub.fetchVariables
205
237
  });
206
238
  }
239
+ // ----- submission-credit gating ------------------------------------------
240
+ /**
241
+ * Take one submission credit, waiting for the gateway to replenish when the
242
+ * window is exhausted (admission backpressure — no 503, no retry). When
243
+ * `timeoutMs` elapses first, rejects with {@link SubmissionTimeoutError} and
244
+ * removes the queued waiter so no credit slot leaks.
245
+ */
246
+ acquireCredit(timeoutMs) {
247
+ if (this.credits > 0) {
248
+ this.credits -= 1;
249
+ return Promise.resolve();
250
+ }
251
+ return new Promise((resolve, reject) => {
252
+ let timer;
253
+ const waiter = {
254
+ grant: () => {
255
+ if (timer !== void 0) clearTimeout(timer);
256
+ this.credits -= 1;
257
+ resolve();
258
+ },
259
+ fail: (e) => {
260
+ if (timer !== void 0) clearTimeout(timer);
261
+ reject(e);
262
+ }
263
+ };
264
+ this.creditWaiters.push(waiter);
265
+ if (timeoutMs !== void 0 && timeoutMs > 0) {
266
+ timer = setTimeout(() => {
267
+ const i = this.creditWaiters.indexOf(waiter);
268
+ if (i >= 0) this.creditWaiters.splice(i, 1);
269
+ reject(new SubmissionTimeoutError(timeoutMs));
270
+ }, timeoutMs);
271
+ timer.unref?.();
272
+ }
273
+ });
274
+ }
275
+ releaseCreditWaiters() {
276
+ while (this.credits > 0 && this.creditWaiters.length > 0) {
277
+ this.creditWaiters.shift().grant();
278
+ }
279
+ }
207
280
  /** Create a process instance over the stream. */
208
281
  async createInstance(input) {
209
282
  await this.connect();
283
+ await this.acquireCredit(input.submitTimeoutMs ?? this.defaultSubmitTimeoutMs);
210
284
  const corr = this.nextCorr();
211
285
  let completionResolve = null;
212
286
  const completion = input.awaitCompletion ? new Promise((r) => completionResolve = r) : null;
@@ -258,6 +332,68 @@ var CommandStreamTransport = class {
258
332
  }
259
333
  };
260
334
 
335
+ // src/embedded.ts
336
+ var EmbeddedTransport = class {
337
+ constructor(host, pollMs = 25) {
338
+ this.host = host;
339
+ this.pollMs = pollMs;
340
+ }
341
+ host;
342
+ pollMs;
343
+ subs = /* @__PURE__ */ new Map();
344
+ timer = null;
345
+ closed = false;
346
+ async createInstance(input) {
347
+ const { processInstanceKey } = await this.host.createInstance(input);
348
+ this.host.tick();
349
+ if (!input.awaitCompletion) return { status: 200, body: { processInstanceKey } };
350
+ while (!this.host.instanceCompleted(processInstanceKey)) {
351
+ this.host.tick();
352
+ await new Promise((r) => setTimeout(r, this.pollMs));
353
+ }
354
+ const variables = this.host.instanceVariables(processInstanceKey);
355
+ return { status: 200, body: { processInstanceKey, variables }, completion: { processCompleted: true, variables, processInstanceKey } };
356
+ }
357
+ async subscribe(sub) {
358
+ this.subs.set(sub.jobType, { worker: sub.worker, credits: sub.credits, timeoutMs: sub.timeoutMs, onJob: sub.onJob });
359
+ if (!this.timer) this.timer = setInterval(() => this.pump(), this.pollMs);
360
+ }
361
+ unsubscribe(jobType) {
362
+ this.subs.delete(jobType);
363
+ }
364
+ completeJob(jobKey, variables) {
365
+ void this.host.completeJob(jobKey, variables);
366
+ }
367
+ failJob(jobKey, retries, errorMessage) {
368
+ void this.host.failJob(jobKey, retries ?? 0, errorMessage);
369
+ }
370
+ throwError(jobKey, _errorCode, errorMessage) {
371
+ void this.host.failJob(jobKey, 0, errorMessage);
372
+ }
373
+ grantCredits(jobType, n) {
374
+ const s = this.subs.get(jobType);
375
+ if (s) s.credits += n;
376
+ }
377
+ pump() {
378
+ if (this.closed) return;
379
+ this.host.tick();
380
+ for (const [type, s] of this.subs) {
381
+ if (s.credits <= 0) continue;
382
+ void this.host.activateJobs(type, s.credits, s.timeoutMs, s.worker).then((jobs) => {
383
+ for (const j of jobs) {
384
+ if (s.credits <= 0) break;
385
+ s.credits--;
386
+ s.onJob(j);
387
+ }
388
+ });
389
+ }
390
+ }
391
+ close() {
392
+ this.closed = true;
393
+ if (this.timer) clearInterval(this.timer);
394
+ }
395
+ };
396
+
261
397
  // src/nanoWorker.ts
262
398
  var import_orchestration_cluster_api = require("@camunda8/orchestration-cluster-api");
263
399
  var NanoJobWorker = class {
@@ -350,6 +486,11 @@ function resolveMode(opts) {
350
486
  const fromEnv = typeof process !== "undefined" ? process.env?.CAMUNDA_TRANSPORT : void 0;
351
487
  return fromOpts ?? fromEnv ?? "auto";
352
488
  }
489
+ function resolveSubmitTimeoutMs(opts) {
490
+ const raw = opts?.config?.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS ?? (typeof process !== "undefined" ? process.env?.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS : void 0);
491
+ const n = typeof raw === "string" ? Number(raw) : raw;
492
+ return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
493
+ }
353
494
  function baseFrom(restAddress) {
354
495
  return normalizeBase(restAddress).replace(/\/v2$/, "");
355
496
  }
@@ -357,31 +498,40 @@ function createCamundaClient(opts) {
357
498
  const client = (0, import_orchestration_cluster_api2.createCamundaClient)(opts);
358
499
  const mode = resolveMode(opts);
359
500
  if (mode === "rest") return client;
501
+ if (mode === "embedded") {
502
+ if (!opts?.embeddedHost) throw new Error("transport 'embedded' requires opts.embeddedHost");
503
+ const transport2 = new EmbeddedTransport(opts.embeddedHost);
504
+ return wrapClient(client, async () => transport2, () => transport2.close());
505
+ }
360
506
  const restAddress = client.getConfig().restAddress;
361
507
  const base = baseFrom(restAddress);
508
+ const submitTimeoutMs = resolveSubmitTimeoutMs(opts);
362
509
  let transport = null;
363
510
  let nano;
364
511
  const ensure = async () => {
365
512
  if (nano === void 0) {
366
- nano = mode === "command-stream" ? { engine: "nanobpmn", commandStreamPath: "/command-stream" } : await detectNano(base);
513
+ nano = mode === "falcon" ? { engine: "nanobpmn", falconPath: "/falcon" } : await detectNano(base);
367
514
  }
368
515
  if (!nano) return null;
369
- if (!transport) transport = new CommandStreamTransport(base, nano.commandStreamPath);
516
+ if (!transport) transport = new FalconTransport(base, nano.falconPath, submitTimeoutMs);
370
517
  return transport;
371
518
  };
519
+ return wrapClient(client, ensure, () => transport?.close());
520
+ }
521
+ function wrapClient(client, ensure, onStop) {
372
522
  return new Proxy(client, {
373
523
  get(target, prop, receiver) {
374
524
  if (prop === "createProcessInstance") {
375
525
  return (input, options) => {
376
- const useStream = ensure();
377
- return useStream.then(async (t) => {
526
+ return ensure().then(async (t) => {
378
527
  if (!t) return target.createProcessInstance(input, options);
379
528
  const r = await t.createInstance({
380
529
  processDefinitionId: input?.processDefinitionId,
381
530
  processDefinitionKey: input?.processDefinitionKey,
382
531
  variables: input?.variables,
383
532
  awaitCompletion: input?.awaitCompletion ?? false,
384
- fetchVariables: input?.fetchVariables
533
+ fetchVariables: input?.fetchVariables,
534
+ submitTimeoutMs: input?.submitTimeoutMs
385
535
  });
386
536
  if (r.status >= 400) throw new Error(`createInstance failed: ${r.status} ${JSON.stringify(r.body)}`);
387
537
  const body = r.body ?? {};
@@ -403,7 +553,7 @@ function createCamundaClient(opts) {
403
553
  }
404
554
  if (prop === "stopAllWorkers") {
405
555
  return () => {
406
- transport?.close();
556
+ onStop?.();
407
557
  return target.stopAllWorkers();
408
558
  };
409
559
  }
@@ -414,8 +564,10 @@ function createCamundaClient(opts) {
414
564
  var index_default = createCamundaClient;
415
565
  // Annotate the CommonJS export names for ESM import in node:
416
566
  0 && (module.exports = {
417
- CommandStreamTransport,
567
+ EmbeddedTransport,
568
+ FalconTransport,
418
569
  NanoJobWorker,
570
+ SubmissionTimeoutError,
419
571
  createCamundaClient,
420
572
  detectNano,
421
573
  ...require("@camunda8/orchestration-cluster-api")
@@ -1 +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":"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","names":["import_orchestration_cluster_api","JobActionReceipt","createCamundaClientBase"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/ws.ts","../src/transport.ts","../src/embedded.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 Falcon 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 { FalconTransport } from \"./transport.js\";\nimport { EmbeddedTransport, type EmbeddedHost } from \"./embedded.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 { FalconTransport, SubmissionTimeoutError } from \"./transport.js\";\nexport { EmbeddedTransport, type EmbeddedHost, type EmbeddedJob } from \"./embedded.js\";\nexport { NanoJobWorker } from \"./nanoWorker.js\";\n\n/** auto: upgrade only on Nano. falcon: force. rest: never upgrade. embedded: in-process μ-nano. */\nexport type NanoTransport = \"auto\" | \"falcon\" | \"rest\" | \"embedded\";\n\ntype AnyOpts = Parameters<typeof createCamundaClientBase>[0] & {\n config?: Record<string, unknown> & { CAMUNDA_TRANSPORT?: NanoTransport; CAMUNDA_REST_ADDRESS?: string; CAMUNDA_NANO_SUBMIT_TIMEOUT_MS?: string | number };\n /** Embedded (ADR 0005) in-process engine host; required when transport is \"embedded\". */\n embeddedHost?: EmbeddedHost;\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/**\n * Default client-side submission-timeout (ms) for falcon creates, from\n * `opts.config.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS` or the env var. `undefined`/`0`\n * means wait indefinitely under backpressure. A per-call `submitTimeoutMs` on\n * `createProcessInstance` input overrides this.\n */\nfunction resolveSubmitTimeoutMs(opts?: AnyOpts): number | undefined {\n const raw =\n (opts?.config?.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS as string | number | undefined) ??\n (typeof process !== \"undefined\" ? process.env?.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS : undefined);\n const n = typeof raw === \"string\" ? Number(raw) : raw;\n return typeof n === \"number\" && Number.isFinite(n) && n > 0 ? n : undefined;\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 Falcon protocol when connected to a Nano server (overridable via the\n * CAMUNDA_TRANSPORT config: \"auto\" | \"falcon\" | \"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 // Embedded (ADR 0005): bind the in-process μ-nano host directly — no detection,\n // no socket. The host is the loopback \"Nano gateway in the same process\".\n if (mode === \"embedded\") {\n if (!opts?.embeddedHost) throw new Error(\"transport 'embedded' requires opts.embeddedHost\");\n const transport = new EmbeddedTransport(opts.embeddedHost);\n return wrapClient(client, async () => transport as any, () => transport.close());\n }\n\n const restAddress = client.getConfig().restAddress;\n const base = baseFrom(restAddress);\n const submitTimeoutMs = resolveSubmitTimeoutMs(opts);\n\n let transport: FalconTransport | null = null;\n let nano: NanoInfo | null | undefined; // undefined = not yet probed\n\n const ensure = async (): Promise<FalconTransport | null> => {\n if (nano === undefined) {\n nano = mode === \"falcon\"\n ? { engine: \"nanobpmn\", falconPath: \"/falcon\" }\n : await detectNano(base);\n }\n if (!nano) return null;\n if (!transport) transport = new FalconTransport(base, nano.falconPath, submitTimeoutMs);\n return transport;\n };\n return wrapClient(client, ensure, () => transport?.close());\n}\n\n/** Wrap an upstream client, upgrading createProcessInstance + createJobWorker to\n * a Nano transport (falcon or embedded) when `ensure` yields one. */\nfunction wrapClient(\n client: ReturnType<typeof createCamundaClientBase>,\n ensure: () => Promise<{ createInstance: Function; subscribe: Function; close: Function } | null>,\n onStop?: () => void,\n): ReturnType<typeof createCamundaClientBase> {\n return new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === \"createProcessInstance\") {\n return (input: any, options?: any) => {\n return ensure().then(async (t) => {\n if (!t) return (target as any).createProcessInstance(input, options);\n const r = await (t as any).createInstance({\n processDefinitionId: input?.processDefinitionId,\n processDefinitionKey: input?.processDefinitionKey,\n variables: input?.variables,\n awaitCompletion: input?.awaitCompletion ?? false,\n fetchVariables: input?.fetchVariables,\n submitTimeoutMs: input?.submitTimeoutMs,\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 as any); void w.start(); }\n else (target as any).createJobWorker(cfg);\n });\n return w as any;\n };\n }\n if (prop === \"stopAllWorkers\") {\n return () => { onStop?.(); return (target as any).stopAllWorkers(); };\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 `falconPath`. Detecting it lets the SDK\n// upgrade to the Falcon protocol transparently.\n\nexport interface NanoInfo {\n engine: string;\n version?: string;\n falconPath: 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.falconPath === \"string\") {\n info = {\n engine: String(nano.engine ?? \"nanobpmn\"),\n version: nano.version ? String(nano.version) : undefined,\n falconPath: nano.falconPath,\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:// falcon URL from a REST base + path. */\nexport function falconUrl(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","// Falcon 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/falcon.rs)\n// and the embedded worker SDK (server/src/console/worker_sdk.ts).\nimport { falconUrl } 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\n/** A create queued behind an exhausted submission-credit window. */\ninterface CreditWaiter {\n /** Grant a credit (decrement + resolve the acquire). */\n grant: () => void;\n /** Abandon the wait (reject the acquire), e.g. on disconnect. */\n fail: (e: unknown) => void;\n}\n\n/**\n * Thrown by {@link FalconTransport.createInstance} when the gateway does\n * not acknowledge a create within `submitTimeoutMs`. On the Falcon protocol,\n * admission backpressure is expressed by the server withholding submission\n * credits (no `503`, no retry), so a create otherwise waits indefinitely for\n * intake capacity. This turns that stall into a typed rejection — treat it as\n * \"the server is backpressured\" and back off; do not tight-loop retry.\n */\nexport class SubmissionTimeoutError extends Error {\n constructor(readonly timeoutMs: number) {\n super(\n `create submission stalled: no gateway ack within ${timeoutMs}ms (server is applying admission backpressure)`,\n );\n this.name = \"SubmissionTimeoutError\";\n }\n}\n\nexport class FalconTransport {\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 private defaultSubmitTimeoutMs?: number;\n /**\n * Server-granted submission-credit window (seeded by `welcome`, topped up by\n * `submissionCredits`). Mirrors the engine's intake metering so creates queue\n * client-side under admission backpressure instead of flooding the gateway.\n */\n private credits = 0;\n private creditWaiters: CreditWaiter[] = [];\n\n constructor(restAddress: string, path: string, defaultSubmitTimeoutMs?: number) {\n this.url = falconUrl(restAddress, path);\n this.defaultSubmitTimeoutMs =\n defaultSubmitTimeoutMs !== undefined && defaultSubmitTimeoutMs > 0\n ? defaultSubmitTimeoutMs\n : undefined;\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(`falcon connect failed: ${this.url}`));\n };\n ws.onclose = () => {\n this.open = false;\n // The credit window is per-connection; a reconnect's welcome re-grants\n // a fresh one. Reset it and fail any queued creates so callers can retry\n // on the new connection rather than hang against a dead window.\n this.credits = 0;\n const waiters = this.creditWaiters;\n this.creditWaiters = [];\n for (const w of waiters) w.fail(new Error(\"falcon closed\"));\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(\"falcon 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 this.credits = Number(f.submissionCredits ?? 0);\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 this.releaseCreditWaiters();\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 case \"submissionCredits\": {\n // Server topped up the create-side window (intake headroom returned).\n this.credits += Number(f.n ?? 0);\n this.releaseCreditWaiters();\n break;\n }\n // 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 // ----- submission-credit gating ------------------------------------------\n\n /**\n * Take one submission credit, waiting for the gateway to replenish when the\n * window is exhausted (admission backpressure — no 503, no retry). When\n * `timeoutMs` elapses first, rejects with {@link SubmissionTimeoutError} and\n * removes the queued waiter so no credit slot leaks.\n */\n private acquireCredit(timeoutMs?: number): Promise<void> {\n if (this.credits > 0) {\n this.credits -= 1;\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const waiter: CreditWaiter = {\n grant: () => {\n if (timer !== undefined) clearTimeout(timer);\n this.credits -= 1;\n resolve();\n },\n fail: (e) => {\n if (timer !== undefined) clearTimeout(timer);\n reject(e);\n },\n };\n this.creditWaiters.push(waiter);\n if (timeoutMs !== undefined && timeoutMs > 0) {\n timer = setTimeout(() => {\n const i = this.creditWaiters.indexOf(waiter);\n if (i >= 0) this.creditWaiters.splice(i, 1);\n reject(new SubmissionTimeoutError(timeoutMs));\n }, timeoutMs);\n timer.unref?.();\n }\n });\n }\n\n private releaseCreditWaiters(): void {\n while (this.credits > 0 && this.creditWaiters.length > 0) {\n this.creditWaiters.shift()!.grant();\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 /**\n * Client-side bound (ms) on how long to wait for a submission credit before\n * rejecting with {@link SubmissionTimeoutError}. Overrides the transport-wide\n * default. Omit to wait indefinitely under backpressure. Never sent on the\n * wire — the server is unaware of it.\n */\n submitTimeoutMs?: number;\n }): Promise<{ status: number; body: unknown; completion?: { processCompleted: boolean; variables: unknown; processInstanceKey: string } }> {\n await this.connect();\n // Gate intake on the server's submission-credit window: block here (bounded\n // by submitTimeoutMs) rather than flooding the gateway with creates it has\n // not granted capacity for. Job completions stay unmetered.\n await this.acquireCredit(input.submitTimeoutMs ?? this.defaultSubmitTimeoutMs);\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","// Embedded transport (ADR 0005, realization (a) \"in-process direct\"). Satisfies\n// the same operation seam the falcon transport does — createInstance,\n// subscribe/job-push, complete/fail/throw — but calls an in-process EmbeddedHost\n// directly: no sockets, no frames. The host wraps μ-nano.wasm and owns the wall\n// clock, durable journal, and timer/dispatch tick. Bind it via\n// createCamundaClient({ config: { CAMUNDA_TRANSPORT: \"embedded\" }, embeddedHost }).\n\n/** A job handed to a worker handler. */\nexport interface EmbeddedJob {\n jobKey: string;\n type: string;\n processInstanceKey: string;\n elementId: string;\n retries: number;\n variables: Record<string, unknown>;\n}\n\n/** The engine bridge an embedded app supplies (implemented by the app template's\n * Deno host around μ-nano.wasm). Engine-core stays clock-free; the host injects now(). */\nexport interface EmbeddedHost {\n deploy(xml: string): Promise<{ processIds: string[] }>;\n createInstance(input: { processDefinitionId?: string; variables?: Record<string, unknown> }): Promise<{ processInstanceKey: string }>;\n activateJobs(type: string, max: number, timeoutMs: number, worker: string): Promise<EmbeddedJob[]>;\n completeJob(jobKey: string, variables?: Record<string, unknown>): Promise<void>;\n failJob(jobKey: string, retries: number, errorMessage?: string): Promise<void>;\n instanceCompleted(key: string): boolean;\n instanceVariables(key: string): Record<string, unknown>;\n /** Drive timers + dispatch at wall-clock now. Returns true if anything changed. */\n tick(): void;\n}\n\ninterface Sub {\n worker: string;\n credits: number;\n timeoutMs: number;\n onJob: (j: EmbeddedJob) => void;\n}\n\n/** Pull-based dispatch over an in-process host. Structurally compatible with the\n * FalconTransport surface used by NanoJobWorker + the client proxy. */\nexport class EmbeddedTransport {\n private subs = new Map<string, Sub>();\n private timer: ReturnType<typeof setInterval> | null = null;\n private closed = false;\n constructor(private host: EmbeddedHost, private pollMs = 25) {}\n\n async createInstance(input: {\n processDefinitionId?: string;\n variables?: Record<string, unknown>;\n awaitCompletion?: boolean;\n }): Promise<{ status: number; body: unknown; completion?: { processCompleted: boolean; variables: unknown; processInstanceKey: string } }> {\n const { processInstanceKey } = await this.host.createInstance(input);\n this.host.tick();\n if (!input.awaitCompletion) return { status: 200, body: { processInstanceKey } };\n while (!this.host.instanceCompleted(processInstanceKey)) {\n this.host.tick();\n await new Promise((r) => setTimeout(r, this.pollMs));\n }\n const variables = this.host.instanceVariables(processInstanceKey);\n return { status: 200, body: { processInstanceKey, variables }, completion: { processCompleted: true, variables, processInstanceKey } };\n }\n\n async subscribe(sub: { jobType: string; worker: string; credits: number; timeoutMs: number; onJob: (j: any) => void }): Promise<void> {\n this.subs.set(sub.jobType, { worker: sub.worker, credits: sub.credits, timeoutMs: sub.timeoutMs, onJob: sub.onJob });\n if (!this.timer) this.timer = setInterval(() => this.pump(), this.pollMs);\n }\n unsubscribe(jobType: string): void { this.subs.delete(jobType); }\n\n completeJob(jobKey: string, variables?: Record<string, unknown>): void { void this.host.completeJob(jobKey, variables); }\n failJob(jobKey: string, retries?: number, errorMessage?: string): void { void this.host.failJob(jobKey, retries ?? 0, errorMessage); }\n throwError(jobKey: string, _errorCode: string, errorMessage?: string): void { void this.host.failJob(jobKey, 0, errorMessage); }\n grantCredits(jobType: string, n: number): void { const s = this.subs.get(jobType); if (s) s.credits += n; }\n\n private pump(): void {\n if (this.closed) return;\n this.host.tick();\n for (const [type, s] of this.subs) {\n if (s.credits <= 0) continue;\n void this.host.activateJobs(type, s.credits, s.timeoutMs, s.worker).then((jobs) => {\n for (const j of jobs) { if (s.credits <= 0) break; s.credits--; s.onJob(j); }\n });\n }\n }\n close(): void { this.closed = true; if (this.timer) clearInterval(this.timer); }\n}\n","// A drop-in JobWorker that drains jobs over the Falcon protocol. 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 { FalconTransport, 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: FalconTransport;\n private cfg: NanoJobWorkerConfig;\n private credits: number;\n private stopped = false;\n readonly jobType: string;\n readonly name: string;\n\n constructor(transport: FalconTransport, 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: FalconTransport): 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":"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","names":["import_orchestration_cluster_api","JobActionReceipt","createCamundaClientBase","transport"]}