import { JobActionReceiptSymbol, createCamundaClient as createCamundaClient$1 } from '@camunda8/orchestration-cluster-api'; export * from '@camunda8/orchestration-cluster-api'; /** A job handed to a worker handler. */ interface EmbeddedJob { jobKey: string; type: string; processInstanceKey: string; elementId: string; retries: number; variables: Record; } /** The engine bridge an embedded app supplies (implemented by the app template's * Deno host around μ-nano.wasm). Engine-core stays clock-free; the host injects now(). */ interface EmbeddedHost { deploy(xml: string): Promise<{ processIds: string[]; }>; createInstance(input: { processDefinitionId?: string; variables?: Record; }): Promise<{ processInstanceKey: string; }>; activateJobs(type: string, max: number, timeoutMs: number, worker: string): Promise; completeJob(jobKey: string, variables?: Record): Promise; failJob(jobKey: string, retries: number, errorMessage?: string): Promise; instanceCompleted(key: string): boolean; instanceVariables(key: string): Record; /** Drive timers + dispatch at wall-clock now. Returns true if anything changed. */ tick(): void; } /** Pull-based dispatch over an in-process host. Structurally compatible with the * FalconTransport surface used by NanoJobWorker + the client proxy. */ declare class EmbeddedTransport { private host; private pollMs; private subs; private timer; private closed; constructor(host: EmbeddedHost, pollMs?: number); createInstance(input: { processDefinitionId?: string; variables?: Record; awaitCompletion?: boolean; }): Promise<{ status: number; body: unknown; completion?: { processCompleted: boolean; variables: unknown; processInstanceKey: string; }; }>; subscribe(sub: { jobType: string; worker: string; credits: number; timeoutMs: number; onJob: (j: any) => void; }): Promise; unsubscribe(jobType: string): void; completeJob(jobKey: string, variables?: Record): void; failJob(jobKey: string, retries?: number, errorMessage?: string): void; throwError(jobKey: string, _errorCode: string, errorMessage?: string): void; grantCredits(jobType: string, n: number): void; private pump; close(): void; } interface NanoInfo { engine: string; version?: string; falconPath: string; } /** * Probes a REST address once (cached) and returns the Nano engine info if the * server is a nanobpmn gateway, or null for stock Camunda 8. */ declare function detectNano(restAddress: string, fetchImpl?: typeof fetch): Promise; /** * Thrown when the gateway sends a result frame that omits a field the protocol * requires (a `commandResult` with no `status`, an `instanceCompleted` with no * `processInstanceKey`). The absence of such an identity/status field always * signals an engine or protocol fault, so we surface it loudly here rather than * laundering it into a benign-looking default ("" / 0) that would pass * downstream checks and corrupt state (e.g. completing a job against key ""). */ declare class MalformedFrameError extends Error { readonly frameType: string; readonly detail: string; readonly frame: unknown; constructor(frameType: string, detail: string, frame: unknown); } interface JobFrame { jobKey: string; type: string; processInstanceKey: string; variables?: Record; customHeaders?: Record; retries?: number; [k: string]: unknown; } interface Subscription { jobType: string; worker: string; credits: number; timeoutMs: number; fetchVariables: string[] | null; onJob: (job: JobFrame) => void; } /** * Thrown by {@link FalconTransport.createInstance} when the gateway does * not acknowledge a create within `submitTimeoutMs`. On the Falcon protocol, * admission backpressure is expressed by the server withholding submission * credits (no `503`, no retry), so a create otherwise waits indefinitely for * intake capacity. This turns that stall into a typed rejection — treat it as * "the server is backpressured" and back off; do not tight-loop retry. */ declare class SubmissionTimeoutError extends Error { readonly timeoutMs: number; constructor(timeoutMs: number); } /** * Thrown by {@link FalconTransport.connect} when a WebSocket is opened but the * gateway does not complete the Falcon handshake (no `welcome` frame) within * `connectTimeoutMs`. * * Most WebSocket-hostile infrastructure (corporate proxies, HTTP-only ingress, * some L7 load balancers / WAFs) *rejects* the upgrade, which surfaces promptly * as a socket error and falls back to REST. But a proxy that *blackholes* the * upgrade — accepting the TCP connection and forwarding bytes while the * handshake never completes and no `welcome` ever arrives — would otherwise * leave {@link FalconTransport.connect} pending forever, hanging the first * request instead of degrading to REST. A bounded connect deadline turns that * silent stall into this typed rejection so the caller can fall back to REST. */ declare class ConnectTimeoutError extends Error { readonly timeoutMs: number; constructor(timeoutMs: number); } declare class FalconTransport { private url; private ws; private open; private corr; private pending; private awaits; private subs; private heartbeat; private connectPromise; private closed; /** True while the background reconnect loop is running (prevents overlap). */ private reconnecting; private defaultSubmitTimeoutMs?; /** * Client-side bound (ms) on the Falcon handshake: how long a freshly-opened * WebSocket may take to deliver its `welcome` before {@link connect} rejects * with {@link ConnectTimeoutError}. Guards against WebSocket-hostile infra * that blackholes the upgrade (accepts the socket but never completes the * handshake). `undefined`/`0` waits indefinitely (legacy behaviour). */ private connectTimeoutMs?; /** * Server-granted submission-credit window (seeded by `welcome`, topped up by * `submissionCredits`). Mirrors the engine's intake metering so creates queue * client-side under admission backpressure instead of flooding the gateway. */ private credits; private creditWaiters; constructor(restAddress: string, path: string, defaultSubmitTimeoutMs?: number, connectTimeoutMs?: number); private nextCorr; /** * Parse the correlation id off an inbound result frame. A missing or invalid * `corr` cannot be routed to its waiting caller, so we drop the frame with a * warning rather than coercing it to 0 (which would silently target — or * mis-target — whatever call happens to hold corr 0). */ private frameCorr; private send; connect(): Promise; /** * Open exactly one WebSocket and settle the returned promise once: resolve on * the server's `welcome`, reject if the socket errors or closes before it * arrives. Also wires the persistent handlers, including the unexpected-close * hook that drives the background reconnect loop. * * The returned promise's rejection is *owned by the caller*: the initial * {@link connect} surfaces it (fast first failure), while {@link reconnectLoop} * catches and retries it. The close handler itself never lets a failure escape * as an unhandled rejection — that was the crash bug this fixes. */ private openSocket; /** * Start the background reconnect loop after an unexpected close — unless one is * already running or the client was explicitly closed. Publishes a shared * `connectPromise` so any {@link connect} awaiter during the outage latches onto * the in-flight reconnect instead of racing a second socket. */ private beginReconnect; /** * Reconnect with bounded exponential backoff + jitter until the engine returns * (or {@link close} is called). Each attempt's failure is caught here, so a * transient failure while the engine is down never escapes as an unhandled * rejection. On success the server's `welcome` re-subscribes every active * worker (see {@link handle}). */ private reconnectLoop; /** * Full-jitter (AWS-style) exponential backoff: a uniform random delay in * `[0, ceiling)`, where the ceiling grows exponentially from `base` (100ms) * and is clamped to `cap` (2s). Full jitter spreads retries out to avoid a * thundering herd while still keeping a long outage retrying at a steady * ceiling rather than backing off forever. */ private backoffDelayMs; private handle; private sendSubscribe; /** * Take one submission credit, waiting for the gateway to replenish when the * window is exhausted (admission backpressure — no 503, no retry). When * `timeoutMs` elapses first, rejects with {@link SubmissionTimeoutError} and * removes the queued waiter so no credit slot leaks. */ private acquireCredit; private releaseCreditWaiters; /** Create a process instance over the stream. */ createInstance(input: { processDefinitionId?: string; processDefinitionKey?: string; variables?: Record; awaitCompletion?: boolean; fetchVariables?: string[]; requestTimeoutMs?: number; /** * Client-side bound (ms) on how long to wait for a submission credit before * rejecting with {@link SubmissionTimeoutError}. Overrides the transport-wide * default. Omit to wait indefinitely under backpressure. Never sent on the * wire — the server is unaware of it. */ submitTimeoutMs?: number; }): Promise<{ status: number; body: unknown; completion?: { processCompleted: boolean; variables: unknown; processInstanceKey: string; }; }>; /** Subscribe a worker; jobs arrive via sub.onJob, credits replenished by ack helpers. */ subscribe(sub: Subscription): Promise; unsubscribe(jobType: string): void; completeJob(jobKey: string, variables?: Record): void; failJob(jobKey: string, retries?: number, errorMessage?: string): void; throwError(jobKey: string, errorCode: string, errorMessage?: string): void; grantCredits(jobType: string, n: number): void; close(): void; } interface NanoJobWorkerConfig { jobType: string; workerName?: string; maxParallelJobs?: number; jobTimeoutMs?: number; fetchVariables?: string[]; jobHandler: (job: any) => Promise | typeof JobActionReceiptSymbol; autoStart?: boolean; } declare class NanoJobWorker { private transport; private cfg; private credits; private stopped; /** A start() was requested while transport was still null; replay it on bind. */ private startRequested; /** * Shared in-flight (then settled) subscription attempt. Racing start() / * bindTransport() calls await this same promise so they subscribe at most * once, yet all observe a rejection if transport.subscribe() fails. Reset to * null on failure (so a later start() can retry) and on stop(). */ private subscribePromise; readonly jobType: string; readonly name: string; constructor(transport: FalconTransport | null, cfg: NanoJobWorkerConfig); /** * Bind the transport after async Nano detection. If a start() was requested * before the transport was available, honour it now (subscribing exactly once). */ bindTransport(transport: FalconTransport): void; /** * Begin draining jobs. Null-safe and idempotent: * - If the transport is not yet bound, the request is deferred and replayed by * bindTransport() once it is — no null dereference. * - Duplicate calls (e.g. proxy self-start plus an eager caller start) result * in a single subscription. */ start(): Promise; private subscribe; private enrich; private dispatch; stop(): void; close(): void; } /** auto: upgrade only on Nano. falcon: force. rest: never upgrade. embedded: in-process μ-nano. */ type NanoTransport = "auto" | "falcon" | "rest" | "embedded"; type AnyOpts = Parameters[0] & { config?: Record & { CAMUNDA_TRANSPORT?: NanoTransport; CAMUNDA_REST_ADDRESS?: string; CAMUNDA_NANO_SUBMIT_TIMEOUT_MS?: string | number; /** * Client-side bound (ms) on the Falcon WebSocket handshake. If the gateway * advertises Falcon but the socket opens without ever completing the * handshake (a proxy that blackholes the upgrade), the SDK gives up after * this deadline and falls back to REST instead of hanging. Defaults to * {@link DEFAULT_CONNECT_TIMEOUT_MS}; set `0` to wait indefinitely. */ CAMUNDA_NANO_CONNECT_TIMEOUT_MS?: string | number; /** * Force plain REST even when the gateway advertises Falcon. Useful for * environments where WebSockets are blocked (corporate proxies etc.). * Accepts any truthy string (`1`, `true`, `yes`, `on`). */ CAMUNDA_FORCE_REST?: string | boolean | number; }; /** Embedded (ADR 0005) in-process engine host; required when transport is "embedded". */ embeddedHost?: EmbeddedHost; }; /** * Default Falcon handshake deadline (ms). Unlike the submit timeout (which * defaults to indefinite backpressure waiting), the connect timeout defaults to * a finite value: a stalled handshake must degrade to REST out of the box, not * hang. Generous enough not to trip a slow-but-legitimate handshake. */ declare const DEFAULT_CONNECT_TIMEOUT_MS = 5000; /** * Drop-in replacement for the upstream createCamundaClient. Returns the upstream * client wrapped in a Proxy that upgrades createProcessInstance + createJobWorker * to the Falcon protocol when connected to a Nano server (overridable via the * CAMUNDA_TRANSPORT config: "auto" | "falcon" | "rest"). */ declare function createCamundaClient(opts?: AnyOpts): ReturnType; export { ConnectTimeoutError, DEFAULT_CONNECT_TIMEOUT_MS, type EmbeddedHost, type EmbeddedJob, EmbeddedTransport, FalconTransport, MalformedFrameError, type NanoInfo, NanoJobWorker, type NanoTransport, SubmissionTimeoutError, createCamundaClient, createCamundaClient as default, detectNano };