@nanobpm/nano-sdk 1.0.0 → 1.2.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/dist/index.js CHANGED
@@ -19,11 +19,11 @@ async function detectNano(restAddress, fetchImpl = fetch) {
19
19
  if (res.ok) {
20
20
  const body = await res.json().catch(() => ({}));
21
21
  const nano = body?.nano;
22
- if (nano && typeof nano.commandStreamPath === "string") {
22
+ if (nano && typeof nano.falconPath === "string") {
23
23
  info = {
24
24
  engine: String(nano.engine ?? "nanobpmn"),
25
25
  version: nano.version ? String(nano.version) : void 0,
26
- commandStreamPath: nano.commandStreamPath
26
+ falconPath: nano.falconPath
27
27
  };
28
28
  }
29
29
  }
@@ -33,7 +33,7 @@ async function detectNano(restAddress, fetchImpl = fetch) {
33
33
  cache.set(base, info);
34
34
  return info;
35
35
  }
36
- function commandStreamUrl(restAddress, path) {
36
+ function falconUrl(restAddress, path) {
37
37
  let base = normalizeBase(restAddress);
38
38
  if (base.startsWith("https://")) base = "wss://" + base.slice("https://".length);
39
39
  else if (base.startsWith("http://")) base = "ws://" + base.slice("http://".length);
@@ -55,7 +55,17 @@ async function getWebSocket() {
55
55
  }
56
56
 
57
57
  // src/transport.ts
58
- var CommandStreamTransport = class {
58
+ var SubmissionTimeoutError = class extends Error {
59
+ constructor(timeoutMs) {
60
+ super(
61
+ `create submission stalled: no gateway ack within ${timeoutMs}ms (server is applying admission backpressure)`
62
+ );
63
+ this.timeoutMs = timeoutMs;
64
+ this.name = "SubmissionTimeoutError";
65
+ }
66
+ timeoutMs;
67
+ };
68
+ var FalconTransport = class {
59
69
  url;
60
70
  ws = null;
61
71
  open = false;
@@ -66,8 +76,17 @@ var CommandStreamTransport = class {
66
76
  heartbeat = null;
67
77
  connectPromise = null;
68
78
  closed = false;
69
- constructor(restAddress, path) {
70
- this.url = commandStreamUrl(restAddress, path);
79
+ defaultSubmitTimeoutMs;
80
+ /**
81
+ * Server-granted submission-credit window (seeded by `welcome`, topped up by
82
+ * `submissionCredits`). Mirrors the engine's intake metering so creates queue
83
+ * client-side under admission backpressure instead of flooding the gateway.
84
+ */
85
+ credits = 0;
86
+ creditWaiters = [];
87
+ constructor(restAddress, path, defaultSubmitTimeoutMs) {
88
+ this.url = falconUrl(restAddress, path);
89
+ this.defaultSubmitTimeoutMs = defaultSubmitTimeoutMs !== void 0 && defaultSubmitTimeoutMs > 0 ? defaultSubmitTimeoutMs : void 0;
71
90
  }
72
91
  nextCorr() {
73
92
  this.corr += 1;
@@ -95,12 +114,16 @@ var CommandStreamTransport = class {
95
114
  this.handle(f, resolve);
96
115
  };
97
116
  ws.onerror = () => {
98
- if (!this.open) reject(new Error(`command-stream connect failed: ${this.url}`));
117
+ if (!this.open) reject(new Error(`falcon connect failed: ${this.url}`));
99
118
  };
100
119
  ws.onclose = () => {
101
120
  this.open = false;
121
+ this.credits = 0;
122
+ const waiters = this.creditWaiters;
123
+ this.creditWaiters = [];
124
+ for (const w of waiters) w.fail(new Error("falcon closed"));
102
125
  if (this.heartbeat) clearInterval(this.heartbeat);
103
- for (const p of this.pending.values()) p.reject(new Error("command-stream closed"));
126
+ for (const p of this.pending.values()) p.reject(new Error("falcon closed"));
104
127
  this.pending.clear();
105
128
  if (!this.closed) setTimeout(() => void this.reconnect(), 1e3);
106
129
  };
@@ -118,12 +141,14 @@ var CommandStreamTransport = class {
118
141
  switch (f.type) {
119
142
  case "welcome": {
120
143
  this.open = true;
144
+ this.credits = Number(f.submissionCredits ?? 0);
121
145
  const hb = Number(f.heartbeatMs ?? 0);
122
146
  if (hb > 0) {
123
147
  if (this.heartbeat) clearInterval(this.heartbeat);
124
148
  this.heartbeat = setInterval(() => this.send({ type: "heartbeat" }), hb);
125
149
  }
126
150
  for (const sub of this.subs.values()) this.sendSubscribe(sub);
151
+ this.releaseCreditWaiters();
127
152
  resolveConnect();
128
153
  break;
129
154
  }
@@ -155,6 +180,11 @@ var CommandStreamTransport = class {
155
180
  }
156
181
  break;
157
182
  }
183
+ case "submissionCredits": {
184
+ this.credits += Number(f.n ?? 0);
185
+ this.releaseCreditWaiters();
186
+ break;
187
+ }
158
188
  }
159
189
  }
160
190
  sendSubscribe(sub) {
@@ -167,9 +197,51 @@ var CommandStreamTransport = class {
167
197
  fetchVariable: sub.fetchVariables
168
198
  });
169
199
  }
200
+ // ----- submission-credit gating ------------------------------------------
201
+ /**
202
+ * Take one submission credit, waiting for the gateway to replenish when the
203
+ * window is exhausted (admission backpressure — no 503, no retry). When
204
+ * `timeoutMs` elapses first, rejects with {@link SubmissionTimeoutError} and
205
+ * removes the queued waiter so no credit slot leaks.
206
+ */
207
+ acquireCredit(timeoutMs) {
208
+ if (this.credits > 0) {
209
+ this.credits -= 1;
210
+ return Promise.resolve();
211
+ }
212
+ return new Promise((resolve, reject) => {
213
+ let timer;
214
+ const waiter = {
215
+ grant: () => {
216
+ if (timer !== void 0) clearTimeout(timer);
217
+ this.credits -= 1;
218
+ resolve();
219
+ },
220
+ fail: (e) => {
221
+ if (timer !== void 0) clearTimeout(timer);
222
+ reject(e);
223
+ }
224
+ };
225
+ this.creditWaiters.push(waiter);
226
+ if (timeoutMs !== void 0 && timeoutMs > 0) {
227
+ timer = setTimeout(() => {
228
+ const i = this.creditWaiters.indexOf(waiter);
229
+ if (i >= 0) this.creditWaiters.splice(i, 1);
230
+ reject(new SubmissionTimeoutError(timeoutMs));
231
+ }, timeoutMs);
232
+ timer.unref?.();
233
+ }
234
+ });
235
+ }
236
+ releaseCreditWaiters() {
237
+ while (this.credits > 0 && this.creditWaiters.length > 0) {
238
+ this.creditWaiters.shift().grant();
239
+ }
240
+ }
170
241
  /** Create a process instance over the stream. */
171
242
  async createInstance(input) {
172
243
  await this.connect();
244
+ await this.acquireCredit(input.submitTimeoutMs ?? this.defaultSubmitTimeoutMs);
173
245
  const corr = this.nextCorr();
174
246
  let completionResolve = null;
175
247
  const completion = input.awaitCompletion ? new Promise((r) => completionResolve = r) : null;
@@ -221,6 +293,68 @@ var CommandStreamTransport = class {
221
293
  }
222
294
  };
223
295
 
296
+ // src/embedded.ts
297
+ var EmbeddedTransport = class {
298
+ constructor(host, pollMs = 25) {
299
+ this.host = host;
300
+ this.pollMs = pollMs;
301
+ }
302
+ host;
303
+ pollMs;
304
+ subs = /* @__PURE__ */ new Map();
305
+ timer = null;
306
+ closed = false;
307
+ async createInstance(input) {
308
+ const { processInstanceKey } = await this.host.createInstance(input);
309
+ this.host.tick();
310
+ if (!input.awaitCompletion) return { status: 200, body: { processInstanceKey } };
311
+ while (!this.host.instanceCompleted(processInstanceKey)) {
312
+ this.host.tick();
313
+ await new Promise((r) => setTimeout(r, this.pollMs));
314
+ }
315
+ const variables = this.host.instanceVariables(processInstanceKey);
316
+ return { status: 200, body: { processInstanceKey, variables }, completion: { processCompleted: true, variables, processInstanceKey } };
317
+ }
318
+ async subscribe(sub) {
319
+ this.subs.set(sub.jobType, { worker: sub.worker, credits: sub.credits, timeoutMs: sub.timeoutMs, onJob: sub.onJob });
320
+ if (!this.timer) this.timer = setInterval(() => this.pump(), this.pollMs);
321
+ }
322
+ unsubscribe(jobType) {
323
+ this.subs.delete(jobType);
324
+ }
325
+ completeJob(jobKey, variables) {
326
+ void this.host.completeJob(jobKey, variables);
327
+ }
328
+ failJob(jobKey, retries, errorMessage) {
329
+ void this.host.failJob(jobKey, retries ?? 0, errorMessage);
330
+ }
331
+ throwError(jobKey, _errorCode, errorMessage) {
332
+ void this.host.failJob(jobKey, 0, errorMessage);
333
+ }
334
+ grantCredits(jobType, n) {
335
+ const s = this.subs.get(jobType);
336
+ if (s) s.credits += n;
337
+ }
338
+ pump() {
339
+ if (this.closed) return;
340
+ this.host.tick();
341
+ for (const [type, s] of this.subs) {
342
+ if (s.credits <= 0) continue;
343
+ void this.host.activateJobs(type, s.credits, s.timeoutMs, s.worker).then((jobs) => {
344
+ for (const j of jobs) {
345
+ if (s.credits <= 0) break;
346
+ s.credits--;
347
+ s.onJob(j);
348
+ }
349
+ });
350
+ }
351
+ }
352
+ close() {
353
+ this.closed = true;
354
+ if (this.timer) clearInterval(this.timer);
355
+ }
356
+ };
357
+
224
358
  // src/nanoWorker.ts
225
359
  import { JobActionReceiptSymbol as JobActionReceipt } from "@camunda8/orchestration-cluster-api";
226
360
  var NanoJobWorker = class {
@@ -308,11 +442,27 @@ var NanoJobWorker = class {
308
442
 
309
443
  // src/index.ts
310
444
  export * from "@camunda8/orchestration-cluster-api";
445
+ function isTruthyEnv(v) {
446
+ if (v === void 0 || v === null) return false;
447
+ const s = String(v).trim().toLowerCase();
448
+ return s !== "" && s !== "0" && s !== "off" && s !== "false" && s !== "no";
449
+ }
450
+ function forceRest(opts) {
451
+ const fromOpts = opts?.config?.CAMUNDA_FORCE_REST;
452
+ const fromEnv = typeof process !== "undefined" ? process.env?.CAMUNDA_FORCE_REST : void 0;
453
+ return isTruthyEnv(fromOpts) || isTruthyEnv(fromEnv);
454
+ }
311
455
  function resolveMode(opts) {
456
+ if (forceRest(opts)) return "rest";
312
457
  const fromOpts = opts?.config?.CAMUNDA_TRANSPORT;
313
458
  const fromEnv = typeof process !== "undefined" ? process.env?.CAMUNDA_TRANSPORT : void 0;
314
459
  return fromOpts ?? fromEnv ?? "auto";
315
460
  }
461
+ function resolveSubmitTimeoutMs(opts) {
462
+ const raw = opts?.config?.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS ?? (typeof process !== "undefined" ? process.env?.CAMUNDA_NANO_SUBMIT_TIMEOUT_MS : void 0);
463
+ const n = typeof raw === "string" ? Number(raw) : raw;
464
+ return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
465
+ }
316
466
  function baseFrom(restAddress) {
317
467
  return normalizeBase(restAddress).replace(/\/v2$/, "");
318
468
  }
@@ -320,53 +470,106 @@ function createCamundaClient(opts) {
320
470
  const client = createCamundaClientBase(opts);
321
471
  const mode = resolveMode(opts);
322
472
  if (mode === "rest") return client;
473
+ if (mode === "embedded") {
474
+ if (!opts?.embeddedHost) throw new Error("transport 'embedded' requires opts.embeddedHost");
475
+ const transport2 = new EmbeddedTransport(opts.embeddedHost);
476
+ return wrapClient(client, async () => transport2, () => transport2.close());
477
+ }
323
478
  const restAddress = client.getConfig().restAddress;
324
479
  const base = baseFrom(restAddress);
480
+ const submitTimeoutMs = resolveSubmitTimeoutMs(opts);
325
481
  let transport = null;
326
482
  let nano;
483
+ let falconDead = false;
484
+ let warnedDead = false;
485
+ const markDead = (err) => {
486
+ falconDead = true;
487
+ transport = null;
488
+ if (!warnedDead) {
489
+ warnedDead = true;
490
+ console.warn(
491
+ `[@nanobpm/sdk] Falcon transport unavailable (${err?.message ?? err}); falling back to REST. Set CAMUNDA_FORCE_REST=1 to skip Falcon detection.`
492
+ );
493
+ }
494
+ };
327
495
  const ensure = async () => {
496
+ if (falconDead) return null;
328
497
  if (nano === void 0) {
329
- nano = mode === "command-stream" ? { engine: "nanobpmn", commandStreamPath: "/command-stream" } : await detectNano(base);
498
+ nano = mode === "falcon" ? { engine: "nanobpmn", falconPath: "/falcon" } : await detectNano(base);
330
499
  }
331
500
  if (!nano) return null;
332
- if (!transport) transport = new CommandStreamTransport(base, nano.commandStreamPath);
501
+ if (!transport) {
502
+ const t = new FalconTransport(base, nano.falconPath, submitTimeoutMs);
503
+ try {
504
+ await t.connect();
505
+ transport = t;
506
+ } catch (e) {
507
+ try {
508
+ t.close();
509
+ } catch {
510
+ }
511
+ markDead(e);
512
+ return null;
513
+ }
514
+ }
333
515
  return transport;
334
516
  };
517
+ return wrapClient(client, ensure, () => transport?.close());
518
+ }
519
+ function wrapClient(client, ensure, onStop) {
335
520
  return new Proxy(client, {
336
521
  get(target, prop, receiver) {
337
522
  if (prop === "createProcessInstance") {
338
523
  return (input, options) => {
339
- const useStream = ensure();
340
- return useStream.then(async (t) => {
524
+ return ensure().then(async (t) => {
341
525
  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;
526
+ try {
527
+ const r = await t.createInstance({
528
+ processDefinitionId: input?.processDefinitionId,
529
+ processDefinitionKey: input?.processDefinitionKey,
530
+ variables: input?.variables,
531
+ awaitCompletion: input?.awaitCompletion ?? false,
532
+ fetchVariables: input?.fetchVariables,
533
+ submitTimeoutMs: input?.submitTimeoutMs
534
+ });
535
+ if (r.status >= 400) throw new Error(`createInstance failed: ${r.status} ${JSON.stringify(r.body)}`);
536
+ const body = r.body ?? {};
537
+ return r.completion ? { ...body, variables: r.completion.variables, processInstanceKey: r.completion.processInstanceKey } : body;
538
+ } catch (e) {
539
+ const msg = String(e?.message ?? e);
540
+ if (msg.includes("falcon closed") || msg.includes("falcon connect failed")) {
541
+ console.warn(`[@nanobpm/sdk] Falcon call failed (${msg}); retrying via REST.`);
542
+ return target.createProcessInstance(input, options);
543
+ }
544
+ throw e;
545
+ }
352
546
  });
353
547
  };
354
548
  }
355
549
  if (prop === "createJobWorker") {
356
550
  return (cfg) => {
357
551
  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);
552
+ void ensure().then(async (t) => {
553
+ if (!t) {
554
+ target.createJobWorker(cfg);
555
+ return;
556
+ }
557
+ w.bindTransport(t);
558
+ try {
559
+ await w.start();
560
+ } catch (e) {
561
+ console.warn(
562
+ `[@nanobpm/sdk] Falcon subscribe failed (${e?.message ?? e}); falling back to REST job worker.`
563
+ );
564
+ target.createJobWorker(cfg);
565
+ }
363
566
  });
364
567
  return w;
365
568
  };
366
569
  }
367
570
  if (prop === "stopAllWorkers") {
368
571
  return () => {
369
- transport?.close();
572
+ onStop?.();
370
573
  return target.stopAllWorkers();
371
574
  };
372
575
  }
@@ -376,8 +579,10 @@ function createCamundaClient(opts) {
376
579
  }
377
580
  var index_default = createCamundaClient;
378
581
  export {
379
- CommandStreamTransport,
582
+ EmbeddedTransport,
583
+ FalconTransport,
380
584
  NanoJobWorker,
585
+ SubmissionTimeoutError,
381
586
  createCamundaClient,
382
587
  index_default as default,
383
588
  detectNano
package/dist/index.js.map CHANGED
@@ -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":[]}
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> & {\n CAMUNDA_TRANSPORT?: NanoTransport;\n CAMUNDA_REST_ADDRESS?: string;\n CAMUNDA_NANO_SUBMIT_TIMEOUT_MS?: string | number;\n /**\n * Force plain REST even when the gateway advertises Falcon. Useful for\n * environments where WebSockets are blocked (corporate proxies etc.).\n * Accepts any truthy string (`1`, `true`, `yes`, `on`).\n */\n CAMUNDA_FORCE_REST?: string | boolean | number;\n };\n /** Embedded (ADR 0005) in-process engine host; required when transport is \"embedded\". */\n embeddedHost?: EmbeddedHost;\n};\n\nfunction isTruthyEnv(v: unknown): boolean {\n if (v === undefined || v === null) return false;\n const s = String(v).trim().toLowerCase();\n return s !== \"\" && s !== \"0\" && s !== \"off\" && s !== \"false\" && s !== \"no\";\n}\n\nfunction forceRest(opts?: AnyOpts): boolean {\n const fromOpts = (opts?.config as any)?.CAMUNDA_FORCE_REST;\n const fromEnv =\n typeof process !== \"undefined\" ? process.env?.CAMUNDA_FORCE_REST : undefined;\n return isTruthyEnv(fromOpts) || isTruthyEnv(fromEnv);\n}\n\nfunction resolveMode(opts?: AnyOpts): NanoTransport {\n // Explicit escape hatch for environments where WebSockets are blocked\n // (e.g. corporate proxies). Wins over CAMUNDA_TRANSPORT.\n if (forceRest(opts)) return \"rest\";\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 // Sticky \"falcon is not reachable\" flag. Once we've seen a WebSocket handshake\n // fail (e.g. a proxy blocks WS), stop retrying for this client's lifetime and\n // fall back to REST — matching the escape-hatch semantics of CAMUNDA_FORCE_REST.\n let falconDead = false;\n let warnedDead = false;\n const markDead = (err: unknown) => {\n falconDead = true;\n transport = null;\n if (!warnedDead) {\n warnedDead = true;\n // eslint-disable-next-line no-console\n console.warn(\n `[@nanobpm/sdk] Falcon transport unavailable (${(err as Error)?.message ?? err}); ` +\n `falling back to REST. Set CAMUNDA_FORCE_REST=1 to skip Falcon detection.`,\n );\n }\n };\n\n const ensure = async (): Promise<FalconTransport | null> => {\n if (falconDead) return 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) {\n const t = new FalconTransport(base, nano.falconPath, submitTimeoutMs);\n try {\n // Eagerly open the socket so a proxy-blocked WebSocket surfaces here\n // (single failure) rather than on every request.\n await t.connect();\n transport = t;\n } catch (e) {\n try { t.close(); } catch { /* ignore */ }\n markDead(e);\n return null;\n }\n }\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 try {\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 } catch (e) {\n // A late WS failure (proxy severed the socket, reconnect denied) —\n // fall back to REST for this call.\n const msg = String((e as Error)?.message ?? e);\n if (msg.includes(\"falcon closed\") || msg.includes(\"falcon connect failed\")) {\n // eslint-disable-next-line no-console\n console.warn(`[@nanobpm/sdk] Falcon call failed (${msg}); retrying via REST.`);\n return (target as any).createProcessInstance(input, options);\n }\n throw e;\n }\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(async (t) => {\n if (!t) { (target as any).createJobWorker(cfg); return; }\n w.bindTransport(t as any);\n try { await w.start(); }\n catch (e) {\n // eslint-disable-next-line no-console\n console.warn(\n `[@nanobpm/sdk] Falcon subscribe failed (${(e as Error)?.message ?? e}); ` +\n `falling back to REST job worker.`,\n );\n (target as any).createJobWorker(cfg);\n }\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":["transport"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
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.",
3
+ "version": "1.2.0",
4
+ "description": "Drop-in replacement for @camunda8/orchestration-cluster-api that upgrades to the Nano Falcon protocol when connected to a Nano server.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "publishConfig": {