@camunda8/orchestration-cluster-api 10.0.0-alpha.44 → 10.0.0-alpha.45

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.
@@ -1,7 +1,7 @@
1
1
  import * as effect_Cause from 'effect/Cause';
2
2
  import * as effect_Types from 'effect/Types';
3
3
  import { Context, Effect, Stream, Duration, Layer, Schedule, Cause, Scope } from 'effect';
4
- import { S as SearchPaginateOptions, P as Paginator, a as PaginationMode, C as ConsistencyOptions, b as SearchResponse, c as CamundaClient, d as CamundaOptions, A as ActivatedJobResult } from '../CamundaClient-Cm0dssvE.cjs';
4
+ import { S as SearchPaginateOptions, P as Paginator, a as PaginationMode, C as ConsistencyOptions, b as SearchResponse, c as CamundaClient, d as CamundaOptions, A as ActivatedJobResult } from '../CamundaClient-BiHf6wkM.cjs';
5
5
  import 'zod';
6
6
  import '../logger-D-p21VHo.cjs';
7
7
 
@@ -232,6 +232,19 @@ interface ActivateJobsStreamOptions<R = never> {
232
232
  readonly requestTimeout?: Duration.Input | number;
233
233
  /** Restrict activation to these variable names. */
234
234
  readonly fetchVariables?: readonly string[];
235
+ /**
236
+ * Activate jobs with a lease — default `false`.
237
+ *
238
+ * When `true`, each activated job is assigned a distinct, opaque lease token
239
+ * (`ActivatedJobResult.leaseToken`) that is threaded back into the fenced
240
+ * `completeJob` / `failJob` / `throwJobError` commands, fencing them against a
241
+ * superseded activation of the same job.
242
+ *
243
+ * Note: the Effect `handler` intentionally keeps the base job shape
244
+ * (`leaseToken` remains optional/nullable); the token is threaded back into
245
+ * fenced commands automatically, so handlers do not read it directly.
246
+ */
247
+ readonly withLease?: boolean;
235
248
  /**
236
249
  * `Schedule` used to back off and retry a **failed activation request** (transport
237
250
  * outage, broker restart, transient server error). Runs on the Effect `Clock`.
@@ -1,7 +1,7 @@
1
1
  import * as effect_Cause from 'effect/Cause';
2
2
  import * as effect_Types from 'effect/Types';
3
3
  import { Context, Effect, Stream, Duration, Layer, Schedule, Cause, Scope } from 'effect';
4
- import { S as SearchPaginateOptions, P as Paginator, a as PaginationMode, C as ConsistencyOptions, b as SearchResponse, c as CamundaClient, d as CamundaOptions, A as ActivatedJobResult } from '../CamundaClient-pcUQlGFl.js';
4
+ import { S as SearchPaginateOptions, P as Paginator, a as PaginationMode, C as ConsistencyOptions, b as SearchResponse, c as CamundaClient, d as CamundaOptions, A as ActivatedJobResult } from '../CamundaClient-3psgxgtn.js';
5
5
  import 'zod';
6
6
  import '../logger-D-p21VHo.js';
7
7
 
@@ -232,6 +232,19 @@ interface ActivateJobsStreamOptions<R = never> {
232
232
  readonly requestTimeout?: Duration.Input | number;
233
233
  /** Restrict activation to these variable names. */
234
234
  readonly fetchVariables?: readonly string[];
235
+ /**
236
+ * Activate jobs with a lease — default `false`.
237
+ *
238
+ * When `true`, each activated job is assigned a distinct, opaque lease token
239
+ * (`ActivatedJobResult.leaseToken`) that is threaded back into the fenced
240
+ * `completeJob` / `failJob` / `throwJobError` commands, fencing them against a
241
+ * superseded activation of the same job.
242
+ *
243
+ * Note: the Effect `handler` intentionally keeps the base job shape
244
+ * (`leaseToken` remains optional/nullable); the token is threaded back into
245
+ * fenced commands automatically, so handlers do not read it directly.
246
+ */
247
+ readonly withLease?: boolean;
235
248
  /**
236
249
  * `Schedule` used to back off and retry a **failed activation request** (transport
237
250
  * outage, broker restart, transient server error). Runs on the Effect `Clock`.
@@ -2,8 +2,9 @@ import {
2
2
  CamundaValidationError,
3
3
  EventualConsistencyTimeoutError,
4
4
  createCamundaClient,
5
+ isPresentWhenUnsupportedError,
5
6
  nextPageRequest
6
- } from "../chunk-AJLLDG7R.js";
7
+ } from "../chunk-B5HJCAHJ.js";
7
8
  import "../chunk-LXWPMQOS.js";
8
9
  import "../chunk-DGUM43GV.js";
9
10
 
@@ -188,6 +189,9 @@ import {
188
189
  Layer as Layer2,
189
190
  Stream as Stream2
190
191
  } from "effect";
192
+ function isTerminalContractError(e) {
193
+ return isPresentWhenUnsupportedError(e) || isPresentWhenUnsupportedError(e.cause);
194
+ }
191
195
  var RetryableJobError = class extends Data2.TaggedError("RetryableJobError") {
192
196
  };
193
197
  var TerminalJobError = class extends Data2.TaggedError("TerminalJobError") {
@@ -212,7 +216,8 @@ function activateJobsStream(type, options = {}) {
212
216
  maxJobsToActivate: batch,
213
217
  requestTimeout,
214
218
  timeout: toMillis(jobTimeout),
215
- ...options.fetchVariables && options.fetchVariables.length > 0 ? { fetchVariable: [...options.fetchVariables] } : {}
219
+ ...options.fetchVariables && options.fetchVariables.length > 0 ? { fetchVariable: [...options.fetchVariables] } : {},
220
+ ...options.withLease ? { withLease: true } : {}
216
221
  };
217
222
  const pollOnce = Effect2.gen(function* () {
218
223
  const camunda = yield* CamundaEffect;
@@ -221,7 +226,18 @@ function activateJobsStream(type, options = {}) {
221
226
  if (jobs.length === 0) yield* Effect2.sleep(pollInterval);
222
227
  return jobs;
223
228
  });
224
- const activation = options.activationRetrySchedule ? Effect2.retry(pollOnce, { schedule: options.activationRetrySchedule }) : pollOnce;
229
+ const activation = options.activationRetrySchedule ? Effect2.retry(pollOnce, {
230
+ schedule: options.activationRetrySchedule,
231
+ // A dependent-presence contract mismatch (`PresentWhenUnsupportedError`,
232
+ // e.g. `withLease` requested against a server that predates lease tokens)
233
+ // is TERMINAL: retrying the same request against the same server can
234
+ // never satisfy the derived presence contract. Stop instead of looping on
235
+ // the user's retry schedule, matching the terminal handling in the plain
236
+ // and threaded worker poll loops. The Effect client maps the thrown guard
237
+ // error into the `DomainError` channel while preserving the original as
238
+ // `cause`, so we discriminate it there.
239
+ while: (e) => !isTerminalContractError(e)
240
+ }) : pollOnce;
225
241
  return Stream2.fromIterableEffectRepeat(activation);
226
242
  }
227
243
  function completeJob(job, vars) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/effect.ts","../../src/effect-worker.ts"],"sourcesContent":["// First-class Effect adapter for the Camunda client.\n//\n// Mirrors the Promise-based `CamundaClient` surface, mapping every method to an\n// `Effect.Effect<Awaited<R>, DomainError, never>` via the same `Proxy` +\n// method-memoization trick the former functional adapter used (it already handles\n// `CancelablePromise`/sync returns via `isPromiseLike`).\n//\n// `effect` is an OPTIONAL peer dependency: importing this module (the `./effect`\n// subpath) is the only way to pull `effect` into the runtime graph. The main `.`\n// entry never imports it, so Promise-first users are never forced to adopt Effect.\n\nimport { Context, Data, Duration, Effect, Layer, Schedule, Stream } from 'effect';\nimport { type CamundaClient, type CamundaOptions, createCamundaClient } from './gen/CamundaClient';\nimport {\n EventualConsistencyTimeoutError as RuntimeEventualConsistencyTimeoutError,\n CamundaValidationError as RuntimeValidationError,\n} from './runtime/errors';\nimport type { ConsistencyOptions } from './runtime/eventual';\nimport {\n nextPageRequest,\n type PaginationMode,\n type Paginator,\n type SearchBody,\n type SearchResponse,\n} from './runtime/pagination';\nimport type { SearchPaginateOptions } from './runtime/searchPagination';\n\n// --- Tagged domain errors -------------------------------------------------------\n//\n// Modeled as `Data.TaggedError` classes so callers discriminate with\n// `Effect.catchTag` / `Effect.catchTags` instead of a hand-rolled classification\n// switch. Each carries a stable `_tag` literal.\n\n/** A request/response validation failure surfaced by the SDK. */\nexport class CamundaValidationError extends Data.TaggedError('CamundaValidationError')<{\n readonly side: 'request' | 'response';\n readonly operationId?: string;\n readonly summary: string;\n readonly issues: string[];\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** An eventual-consistency poll that did not converge within its budget. */\nexport class EventualConsistencyTimeout extends Data.TaggedError('EventualConsistencyTimeout')<{\n readonly attempts?: number;\n readonly elapsedMs?: number;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** An HTTP-level failure (non-2xx / transport carrying a status). */\nexport class HttpError extends Data.TaggedError('HttpError')<{\n readonly status?: number;\n readonly body?: unknown;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** Any other thrown value that does not map to a more specific tag. */\nexport class CamundaGenericError extends Data.TaggedError('CamundaGenericError')<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** The typed error channel for every Effect the client produces. */\nexport type DomainError =\n | CamundaValidationError\n | EventualConsistencyTimeout\n | HttpError\n | CamundaGenericError;\n\n/** The tag literals of the {@link DomainError} union. */\nexport type DomainErrorTag = DomainError['_tag'];\n\n// Narrow an arbitrary thrown value into the tagged `DomainError` union.\nfunction toDomainError(err: unknown): DomainError {\n if (err instanceof RuntimeValidationError) {\n return new CamundaValidationError({\n side: err.side,\n operationId: err.operationId,\n summary: err.summary,\n issues: err.issues,\n message: err.message,\n cause: err,\n });\n }\n if (err instanceof RuntimeEventualConsistencyTimeoutError) {\n return new EventualConsistencyTimeout({\n attempts: err.attempts,\n elapsedMs: err.elapsedMs,\n message: err.message,\n cause: err,\n });\n }\n const anyErr = err as { status?: unknown; body?: unknown; message?: unknown } | null | undefined;\n if (anyErr && typeof anyErr === 'object' && typeof anyErr.status === 'number') {\n return new HttpError({\n status: anyErr.status,\n body: anyErr.body,\n message: typeof anyErr.message === 'string' ? anyErr.message : `HTTP ${anyErr.status}`,\n cause: err,\n });\n }\n if (err instanceof Error) {\n return new CamundaGenericError({ message: err.message, cause: err });\n }\n return new CamundaGenericError({ message: String(err), cause: err });\n}\n\n// --- Paginated search -----------------------------------------------------------\n//\n// The Promise client attaches a `.paginate` helper to every `search*` operation\n// (`installSearchPagination`), returning a lazy `AsyncIterable` `Paginator`. The\n// Effect client keeps that helper but re-expresses it in Effect terms: pages and\n// items become `Stream`s, and the eager drain becomes an `Effect`.\n//\n// The page-advance *algebra* is shared with the Promise engine — `nextPageRequest`\n// (pure) decides cursor-vs-offset and end-of-results identically for both. Only the\n// driver differs, and it has to: the Promise engine is an async generator advanced\n// by `AbortSignal`, and bridging that into a `Stream` (`Stream.fromAsyncIterable`)\n// pulls through an uninterruptible `Effect.tryPromise`, so a fiber blocked on an\n// in-flight page could not be interrupted. Driving the loop with the Effect search\n// call — which already cancels its `CancelablePromise` on interrupt — keeps the\n// whole stream interruptible.\n\n/** Options for the Effect client's `.paginate`. */\nexport interface EffectPaginateOptions<TData = unknown> {\n /**\n * Safety cap on pages fetched (default: unbounded). A non-positive value fetches\n * no pages at all — the cap is enforced *before* the first request.\n */\n readonly maxPages?: number;\n /** How to advance. `auto` prefers a cursor, falls back to offset. */\n readonly mode?: PaginationMode;\n /**\n * Eventual-consistency controls forwarded to the underlying search call. Defaults\n * to `{ waitUpToMs: 0 }`. Only the **first** page honours this window: once paging\n * is under way an empty page is end-of-results, not a not-yet-consistent read.\n */\n readonly consistency?: ConsistencyOptions<TData>;\n}\n\n/**\n * The Effect-flavoured counterpart of a {@link Paginator}: the same three views\n * (`pages` / `items` / `toArray`) over a multi-page search, as `Stream`s and an\n * `Effect` rather than async iterables and a `Promise`.\n *\n * Every view is lazy — a page is fetched only when pulled — and interruptible:\n * interrupting the fiber cancels the in-flight page request rather than leaving it\n * to settle unobserved.\n */\nexport interface EffectPaginator<TItem> {\n /** A `Stream` of whole pages, each fetched lazily as it is pulled. */\n pages(): Stream.Stream<SearchResponse<TItem>, DomainError>;\n /** A `Stream` of individual items, flattened across all pages. */\n items(): Stream.Stream<TItem, DomainError>;\n /** Eagerly drains every item into an array. Bounded result sets only. */\n toArray(): Effect.Effect<TItem[], DomainError>;\n}\n\n/** The Effect-returning search call a paginator drives. */\ntype EffectSearch = (\n body: SearchBody,\n consistencyArg: { consistency: ConsistencyOptions<unknown> }\n) => Effect.Effect<SearchResponse<unknown>, DomainError>;\n\n/** Build the Effect `.paginate` helper for one Effect-returning search operation. */\nfunction effectifyPaginate(search: EffectSearch) {\n return (body: SearchBody, opts: EffectPaginateOptions = {}): EffectPaginator<unknown> => {\n const { consistency, maxPages = Number.POSITIVE_INFINITY, mode = 'auto' } = opts;\n\n const pagesFrom = (\n pageBody: SearchBody,\n remaining: number,\n isFirstPage: boolean\n ): Stream.Stream<SearchResponse<unknown>, DomainError> => {\n if (remaining <= 0) return Stream.empty;\n // Waiting on eventual consistency only makes sense for the initial query: once\n // paging forward, an empty page means end-of-results, not an inconsistent read,\n // so waiting (and its terminal timeout) must not apply to later fetches.\n const consistencyArg = {\n consistency: isFirstPage ? (consistency ?? { waitUpToMs: 0 }) : { waitUpToMs: 0 },\n } as { consistency: ConsistencyOptions<unknown> };\n\n return Stream.unwrap(\n search(pageBody, consistencyArg).pipe(\n Effect.map((response) => {\n const next = nextPageRequest(pageBody, response, mode);\n const head = Stream.make(response);\n return next === null\n ? head\n : Stream.concat(head, pagesFrom(next, remaining - 1, false));\n })\n )\n );\n };\n\n const pages = () => pagesFrom(body, maxPages, true);\n const items = () =>\n pages().pipe(\n Stream.map((response) => response.items),\n Stream.flattenIterable\n );\n\n return {\n pages,\n items,\n toArray: () => Stream.runCollect(items()).pipe(Effect.map((chunk) => [...chunk])),\n };\n };\n}\n\n// --- Client shape ---------------------------------------------------------------\n\n/** Keys of `C` whose values are callable. */\nexport type FnKeys<C> = { [K in keyof C]: C[K] extends (...a: any) => any ? K : never }[keyof C];\n\n/**\n * Helpers attached to a client *method* (not to the client), re-expressed in Effect\n * terms. Resolves to `unknown` — the identity of `&` — for a method that carries none.\n */\ntype EffectifyMethodHelpers<F> = F extends {\n paginate(body: infer B, opts?: SearchPaginateOptions<infer D>): Paginator<infer I>;\n}\n ? { paginate(body: B, opts?: EffectPaginateOptions<D>): EffectPaginator<I> }\n : unknown;\n\n/** Maps a single method to its Effect-returning form, keeping its attached helpers. */\ntype EffectifyMethod<F> = F extends (...a: infer A) => infer R\n ? ((...a: A) => Effect.Effect<Awaited<R>, DomainError, never>) & EffectifyMethodHelpers<F>\n : never;\n\n/** Maps every method of `C` to an Effect-returning method, preserving non-fn members. */\nexport type Effectify<C> = {\n [K in FnKeys<C>]: EffectifyMethod<C[K]>;\n} & { inner: C } & { [K in Exclude<keyof C, FnKeys<C>>]: C[K] };\n\n/** The Effect-flavoured Camunda client. Every operation returns an `Effect`. */\nexport type CamundaEffectClient = Effectify<CamundaClient>;\n\n// Runtime detection of promise-like (includes CancelablePromise).\nfunction isPromiseLike<T>(v: unknown): v is Promise<T> {\n return !!v && typeof (v as { then?: unknown }).then === 'function';\n}\n\n/**\n * Create an Effect-flavoured Camunda client.\n *\n * Every `CamundaClient` method becomes `(...args) => Effect.Effect<Awaited<R>,\n * DomainError, never>`. Failures are narrowed into the tagged {@link DomainError}\n * union so callers use `Effect.catchTag`/`catchTags`. The underlying throwing\n * client is reachable via the `.inner` escape hatch.\n *\n * @description Camunda Effect Client. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect.test.ts) for example usage.\n */\nexport function createCamundaEffectClient(options?: CamundaOptions): CamundaEffectClient {\n const base = createCamundaClient(options);\n const cache = new Map<string | symbol, unknown>();\n\n function wrap(fn: (...a: any[]) => any) {\n const wrapped = (...args: any[]) =>\n Effect.suspend(() => {\n // The generated client can throw *synchronously* (e.g. an eventual\n // endpoint invoked without `consistencyManagement`). Map those to the\n // tagged error channel too, rather than letting them escape as a defect.\n let r: unknown;\n try {\n r = fn.apply(base, args);\n } catch (e) {\n return Effect.fail(toDomainError(e));\n }\n if (!isPromiseLike(r)) return Effect.succeed(r);\n const promise = r;\n return Effect.callback<any, DomainError>((resume) => {\n promise.then(\n (a: unknown) => resume(Effect.succeed(a)),\n (e: unknown) => resume(Effect.fail(toDomainError(e)))\n );\n // Honour Effect interruption: if the fiber is interrupted (e.g. by\n // `withTimeout` or a parent), cancel the underlying `CancelablePromise`\n // so the in-flight HTTP request is aborted instead of leaking.\n const cancel = (r as { cancel?: () => void }).cancel;\n return typeof cancel === 'function' ? Effect.sync(() => cancel.call(r)) : undefined;\n });\n });\n\n // A fresh arrow carries none of the own properties the runtime attaches to the\n // original method. Re-attach the ones the client installs, adapted to Effect —\n // otherwise the Effect client silently loses part of the Promise client's API\n // (`.paginate` on all ~48 `search*` operations). Guarded by\n // `tests/effect-client-surface.test.ts`, which fails if a new method-attached\n // helper appears here without being adapted.\n //\n // The Effect paginator drives `wrapped` (this very Effect search call), not the\n // Promise `.paginate`; the presence of `.paginate` on the original is what marks\n // this method as a paginated search operation.\n if (typeof (fn as { paginate?: unknown }).paginate === 'function') {\n (wrapped as { paginate?: unknown }).paginate = effectifyPaginate(\n wrapped as unknown as EffectSearch\n );\n }\n\n return wrapped;\n }\n\n const handler: ProxyHandler<any> = {\n get(_t, prop: string | symbol) {\n if (prop === 'inner') return base;\n if (cache.has(prop)) return cache.get(prop);\n const value = (base as any)[prop];\n if (typeof value === 'function') {\n const w = wrap(value);\n cache.set(prop, w);\n return w;\n }\n cache.set(prop, value);\n return value;\n },\n };\n\n return new Proxy({}, handler) as CamundaEffectClient;\n}\n\n// --- Combinators (Effect-native) ------------------------------------------------\n\n/**\n * Human-readable rendering of a `Duration.Input` for error messages. `String()`\n * degrades to `[object Object]` for non-string inputs (millis-numbers, `Duration`\n * values, `[seconds, nanos]` tuples); this normalises them via `Duration.format`.\n */\nfunction formatDuration(d: Duration.Input): string {\n // Only used to build error messages. `fromInputUnsafe` throws on a malformed\n // (but statically typed) Duration.Input, so guard it — a formatting failure\n // must never turn a typed timeout failure into an unexpected defect.\n try {\n return Duration.format(Duration.fromInputUnsafe(d));\n } catch {\n return String(d);\n }\n}\n\n/**\n * Retry an effect with exponential backoff (+ jitter), capped attempts, and an\n * optional predicate over the error.\n */\nexport function retryWithBackoff<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n opts: {\n max: number;\n baseDelay?: Duration.Input;\n while?: (e: E) => boolean;\n }\n): Effect.Effect<A, E, R> {\n const backoff = Schedule.exponential(opts.baseDelay ?? '100 millis').pipe(Schedule.jittered);\n // `times: n` caps at n retries after the initial attempt → n+1 total attempts.\n // (v4 dropped `Schedule.intersect`; the retry options object composes the\n // exponential-backoff schedule with the attempt cap and the error predicate.)\n return Effect.retry(effect, {\n schedule: backoff,\n times: Math.max(0, opts.max - 1),\n while: opts.while,\n });\n}\n\n/**\n * Fail an effect with a real interruption if it does not settle within `duration`\n * (true interruption, not a best-effort `Promise.race`).\n */\nexport function withTimeout<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n duration: Duration.Input,\n onTimeout?: () => E | EventualConsistencyTimeout\n): Effect.Effect<A, E | EventualConsistencyTimeout, R> {\n return Effect.timeoutOrElse(effect, {\n duration,\n orElse: () =>\n Effect.fail(\n onTimeout\n ? onTimeout()\n : new EventualConsistencyTimeout({\n message: `Timed out after ${formatDuration(duration)}`,\n })\n ),\n });\n}\n\n/**\n * Poll `effect` on the Effect `Clock` until `predicate` holds, timing out to\n * {@link EventualConsistencyTimeout} once `waitUpTo` elapses. Because it uses the\n * Effect `Clock` (not `Date.now`/`setTimeout`), `TestClock.adjust` advances it\n * deterministically in tests — no real-clock burn.\n */\nexport function eventually<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n predicate: (a: A) => boolean,\n opts: {\n waitUpTo: Duration.Input;\n interval?: Duration.Input;\n }\n): Effect.Effect<A, E | EventualConsistencyTimeout, R> {\n const interval = opts.interval ?? '500 millis';\n // Poll on the Effect `Clock` (Effect.sleep), returning the value A once the\n // predicate holds. Kept as an Effect-valued loop (not `Effect.repeat` with a\n // schedule, whose success channel is the schedule output, not A).\n const poll: Effect.Effect<A, E, R> = Effect.suspend(() =>\n effect.pipe(\n Effect.flatMap((a) =>\n predicate(a) ? Effect.succeed(a) : Effect.sleep(interval).pipe(Effect.andThen(poll))\n )\n )\n );\n return withTimeout(\n poll,\n opts.waitUpTo,\n () =>\n new EventualConsistencyTimeout({\n message: `Eventual consistency timeout after ${formatDuration(opts.waitUpTo)}`,\n })\n );\n}\n\n// --- Dependency injection -------------------------------------------------------\n\n/**\n * `Context` service key for the Effect Camunda client. Compose worker/orchestration\n * code against this tag and provide {@link layer} (or a test double) via `Layer`.\n */\nexport class CamundaEffect extends Context.Service<CamundaEffect, CamundaEffectClient>()(\n 'CamundaEffect'\n) {}\n\n/**\n * A `Layer` that constructs a {@link CamundaEffectClient} and provides it as the\n * {@link CamundaEffect} service. Swap in a test double by providing a different\n * `Layer` for the same tag.\n */\nexport function layer(options?: CamundaOptions): Layer.Layer<CamundaEffect> {\n return Layer.sync(CamundaEffect, () => createCamundaEffectClient(options));\n}\n","// Effect-native job-worker surface for the Camunda client.\n//\n// Where `./effect` (see `effect.ts`) gives an Effect surface over the *single-call*\n// client (deploy/create/search/…), this adds the Effect surface for **job workers**:\n// the long-running `activateJobs` → handle → `completeJob`/`failJob` loop.\n//\n// It composes over the existing client machinery — activation goes through the same\n// `activateJobs` call (and therefore the same backpressure/retry runtime) as the\n// Promise worker — rather than reimplementing activation. What it adds on top is\n// Effect-native:\n// - a job handler `(job) => Effect<CompleteVars, JobError, R>` with a **typed\n// failure channel** (retryable → `failJob` with decremented retries; terminal →\n// `throwJobError`/incident);\n// - **`Schedule`** for activation backoff and in-handler retry, both running on the\n// Effect `Clock` (so `TestClock` makes their timing virtual in tests);\n// - **`Stream`** with bounded `concurrency` for backpressure over the activation loop;\n// - **`Layer`**/**`Scope`** DI + lifecycle: the worker forks into a scope and is\n// interrupted (with a best-effort lease release) when the scope closes.\n//\n// `effect` remains an OPTIONAL peer dependency: importing this module (via the\n// `./effect` subpath) is the only way to pull `effect` into the runtime graph. The\n// main `.` (Promise) worker API is untouched.\n\nimport {\n Cause,\n Data,\n Duration,\n Effect,\n Fiber,\n Layer,\n type Schedule,\n type Scope,\n Stream,\n} from 'effect';\nimport { CamundaEffect, type DomainError } from './effect';\nimport type { ActivatedJobResult } from './gen/types.gen';\n\n// --- Job shape ------------------------------------------------------------------\n\n/**\n * A single activated job handed to an Effect handler. This is the raw activation\n * payload (variables + custom headers + lifecycle keys); acknowledgement is driven\n * by the value/error the handler's `Effect` produces, not by imperative methods.\n */\nexport type Job = ActivatedJobResult;\n\n/** Variables to complete a job with. `void`/`undefined` completes with no variables. */\n// biome-ignore lint/suspicious/noConfusingVoidType: a handler may complete a job with no variables, i.e. return Effect<void>\nexport type CompleteVars = { readonly [key: string]: unknown } | void | undefined;\n\n// --- Typed job-failure channel --------------------------------------------------\n//\n// A handler discriminates its two failure modes with tagged errors instead of a\n// hand-rolled classification: a **retryable** failure decrements retries and calls\n// `failJob` (optionally after in-handler `Schedule` retries); a **terminal** failure\n// raises a BPMN error / incident via `throwJobError`.\n\n/**\n * A retryable job failure: the handler could not complete the job now, but a later\n * activation might succeed. Mapped to `failJob` with `retries - 1` and an optional\n * server-side re-activation backoff.\n */\nexport class RetryableJobError extends Data.TaggedError('RetryableJobError')<{\n readonly message: string;\n /** Server-side delay before the job becomes re-activatable (`failJob` `retryBackOff`). */\n readonly retryBackoff?: Duration.Input;\n /** Optional variables to attach to the job on failure. */\n readonly variables?: { readonly [key: string]: unknown };\n readonly cause?: unknown;\n}> {}\n\n/**\n * A terminal job failure: retrying will not help. Mapped to `throwJobError`, which\n * is caught by a matching BPMN error boundary event or — if uncaught — raises an\n * incident.\n */\nexport class TerminalJobError extends Data.TaggedError('TerminalJobError')<{\n /** The BPMN error code matched against an error catch event. */\n readonly code: string;\n readonly message: string;\n /** Optional variables to instantiate at the error catch event's scope. */\n readonly variables?: { readonly [key: string]: unknown };\n readonly cause?: unknown;\n}> {}\n\n/** The typed error channel a job handler may fail with. */\nexport type JobError = RetryableJobError | TerminalJobError;\n\n/** A job handler: consumes a {@link Job}, produces completion variables or a {@link JobError}. */\nexport type JobHandler<A extends CompleteVars, R> = (job: Job) => Effect.Effect<A, JobError, R>;\n\n// --- Configuration --------------------------------------------------------------\n\nexport interface ActivateJobsStreamOptions<R = never> {\n /** Worker name recorded on the activation request. Defaults to `effect-worker-<type>-<n>`, where `<n>` is an incrementing per-process counter. */\n readonly workerName?: string;\n /** Max jobs to activate per poll (the activation batch size). Default `10`. */\n readonly maxJobsToActivate?: number;\n /**\n * Delay between polls that returned **no** jobs, on the Effect `Clock` (so it is\n * virtual under `TestClock`). A poll that returns jobs schedules the next poll\n * immediately. Default `1 second`.\n */\n readonly pollInterval?: Duration.Input;\n /** Per-job activation lock timeout (server-side). Default `60 seconds`. */\n readonly jobTimeout?: Duration.Input;\n /**\n * Long-poll request timeout. `0` (the default) lets the broker hold the request\n * for its configured default; a negative value returns immediately when idle.\n */\n readonly requestTimeout?: Duration.Input | number;\n /** Restrict activation to these variable names. */\n readonly fetchVariables?: readonly string[];\n /**\n * `Schedule` used to back off and retry a **failed activation request** (transport\n * outage, broker restart, transient server error). Runs on the Effect `Clock`.\n * When omitted, an activation failure fails the stream.\n */\n readonly activationRetrySchedule?: Schedule.Schedule<unknown, DomainError, never, R>;\n}\n\nexport interface EffectWorkerConfig<A extends CompleteVars, R = never>\n extends ActivateJobsStreamOptions<R> {\n /** The job type to activate. */\n readonly type: string;\n /** The Effect job handler. */\n readonly handler: JobHandler<A, R>;\n /**\n * Max jobs processed concurrently (handler parallelism / backpressure). The\n * activation loop will not pull faster than handlers drain, and the activation\n * batch is capped to this value (when it is a finite number) so the worker never\n * leases more jobs than it can process at once. Default: the value of\n * {@link ActivateJobsStreamOptions.maxJobsToActivate} (or `10`).\n */\n readonly concurrency?: number | 'unbounded';\n /**\n * `Schedule` used to retry the **handler** in-process on a {@link RetryableJobError}\n * before the job is failed back to the broker. Runs on the Effect `Clock`\n * (virtual under `TestClock`). A {@link TerminalJobError} is never retried.\n */\n readonly handlerRetrySchedule?: Schedule.Schedule<unknown, JobError, never, R>;\n}\n\n/** A handle to a running Effect worker. */\nexport interface CamundaEffectWorkerHandle {\n /** The job type this worker activates. */\n readonly type: string;\n /** Completes when the worker loop ends (only on a fatal, non-retryable activation error). */\n readonly join: Effect.Effect<void, DomainError>;\n /** Interrupt the worker (also happens automatically when the owning scope closes). */\n readonly interrupt: Effect.Effect<void>;\n}\n\nconst DEFAULT_MAX_JOBS = 10;\nconst DEFAULT_POLL_INTERVAL: Duration.Input = '1 second';\nconst DEFAULT_JOB_TIMEOUT: Duration.Input = '60 seconds';\nconst DEFAULT_LONGPOLL_TIMEOUT = 0;\n\nlet _workerCounter = 0;\n\nfunction toMillis(d: Duration.Input): number {\n return Duration.toMillis(Duration.fromInputUnsafe(d));\n}\n\n// --- Activation stream ----------------------------------------------------------\n\n/**\n * A `Stream` of activated jobs of `type`. Repeatedly calls `activateJobs` (through the\n * `/effect` client `Layer`, i.e. the same backpressure-aware runtime the Promise worker\n * uses) and emits each activated job. The between-empty-polls delay and the optional\n * activation-retry `Schedule` both run on the Effect `Clock`, so `TestClock.adjust`\n * advances them deterministically in tests.\n */\nexport function activateJobsStream<R = never>(\n type: string,\n options: ActivateJobsStreamOptions<R> = {}\n): Stream.Stream<Job, DomainError, CamundaEffect | R> {\n const worker = options.workerName ?? `effect-worker-${type}-${++_workerCounter}`;\n const batch = options.maxJobsToActivate ?? DEFAULT_MAX_JOBS;\n const pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n const jobTimeout = options.jobTimeout ?? DEFAULT_JOB_TIMEOUT;\n const requestTimeout =\n typeof options.requestTimeout === 'number'\n ? options.requestTimeout\n : options.requestTimeout !== undefined\n ? toMillis(options.requestTimeout)\n : DEFAULT_LONGPOLL_TIMEOUT;\n\n const body = {\n type,\n worker,\n maxJobsToActivate: batch,\n requestTimeout,\n timeout: toMillis(jobTimeout),\n ...(options.fetchVariables && options.fetchVariables.length > 0\n ? { fetchVariable: [...options.fetchVariables] }\n : {}),\n };\n\n const pollOnce: Effect.Effect<Job[], DomainError, CamundaEffect> = Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n const result = yield* camunda.activateJobs(body);\n const jobs = (result?.jobs ?? []) as Job[];\n // Empty poll → wait on the Effect Clock before the next activation (virtual under\n // TestClock). A non-empty poll proceeds immediately; downstream `concurrency`\n // provides the backpressure.\n if (jobs.length === 0) yield* Effect.sleep(pollInterval);\n return jobs;\n });\n\n const activation: Effect.Effect<Job[], DomainError, CamundaEffect | R> =\n options.activationRetrySchedule\n ? Effect.retry(pollOnce, { schedule: options.activationRetrySchedule })\n : pollOnce;\n\n return Stream.fromIterableEffectRepeat(activation);\n}\n\n// --- Worker loop ----------------------------------------------------------------\n\nfunction completeJob<A extends CompleteVars>(\n job: Job,\n vars: A\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.completeJob({\n jobKey: job.jobKey,\n variables: (vars ?? {}) as { [key: string]: unknown },\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\nfunction failJobRetryable(\n job: Job,\n error: RetryableJobError\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.failJob({\n jobKey: job.jobKey,\n errorMessage: error.message,\n retries: Math.max(0, (job.retries ?? 1) - 1),\n ...(error.retryBackoff !== undefined ? { retryBackOff: toMillis(error.retryBackoff) } : {}),\n ...(error.variables ? { variables: { ...error.variables } } : {}),\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\nfunction raiseIncident(\n job: Job,\n error: TerminalJobError\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.throwJobError({\n jobKey: job.jobKey,\n errorCode: error.code,\n errorMessage: error.message,\n ...(error.variables ? { variables: { ...error.variables } } : {}),\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\n// On interruption mid-handling, release the lease by failing the job back with its\n// retries **unchanged** so it re-activates promptly instead of waiting for the lock\n// to expire. Best-effort: a failure here (e.g. shutdown racing the HTTP call) is\n// swallowed so it cannot turn interruption into a defect.\nfunction releaseLease(job: Job): Effect.Effect<void, never, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.failJob({\n jobKey: job.jobKey,\n errorMessage: 'worker interrupted; releasing lease for re-activation',\n retries: job.retries ?? 1,\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n }).pipe(Effect.ignore);\n}\n\n// An unexpected handler *defect* (an untyped throw / `Effect.die`, not a typed\n// `JobError`) is a bug, not a modelled outcome. Rather than let it be swallowed by the\n// worker-loop cause handler — which would leave the job unacknowledged until its\n// server-side lock expires (risking duplicate delivery) and hide the bug — translate it\n// into a best-effort `failJob` with `retries - 1`, matching the Promise worker\n// (`src/runtime/jobWorker.ts`). The defect is logged first so the handler bug stays\n// visible. This raises a `DomainError` on the ack channel if `failJob` itself fails,\n// which the worker loop then logs-and-continues like any other ack failure.\nfunction failJobOnDefect(\n job: Job,\n defect: unknown\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* Effect.logError(\n `effect-worker: handler for job ${job.jobKey} threw an unexpected defect; failing job with retries - 1`,\n defect\n );\n yield* camunda.failJob({\n jobKey: job.jobKey,\n errorMessage: defect instanceof Error ? defect.message : 'Handler error',\n retries: Math.max(0, (job.retries ?? 1) - 1),\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\nfunction processJob<A extends CompleteVars, R>(\n config: EffectWorkerConfig<A, R>,\n job: Job\n): Effect.Effect<void, DomainError, CamundaEffect | R> {\n const handled: Effect.Effect<A, JobError, R> = config.handlerRetrySchedule\n ? Effect.retry(config.handler(job), {\n schedule: config.handlerRetrySchedule,\n while: (e: JobError) => e._tag === 'RetryableJobError',\n })\n : config.handler(job);\n\n return handled.pipe(\n Effect.flatMap((vars) => completeJob(job, vars)),\n Effect.catchTag('RetryableJobError', (e) => failJobRetryable(job, e)),\n Effect.catchTag('TerminalJobError', (e) => raiseIncident(job, e)),\n // Interruption is re-raised (not a defect) by `onInterrupt` below; only genuine\n // handler defects reach here and are converted to a best-effort failJob.\n Effect.catchDefect((defect) => failJobOnDefect(job, defect)),\n Effect.onInterrupt(() => releaseLease(job))\n );\n}\n\n/**\n * The worker loop: drains {@link activateJobsStream}, running the handler for each job\n * with bounded `concurrency` (the backpressure knob), and acknowledging via\n * `completeJob`/`failJob`/`throwJobError` per the handler's typed outcome.\n */\nexport function runWorkerLoop<A extends CompleteVars, R = never>(\n config: EffectWorkerConfig<A, R>\n): Effect.Effect<void, DomainError, CamundaEffect | R> {\n const concurrency = config.concurrency ?? config.maxJobsToActivate ?? DEFAULT_MAX_JOBS;\n // Never lease more jobs per poll than we can process concurrently. Jobs activated\n // beyond `concurrency` would sit buffered in the stream while their server-side lock\n // timeout counts down, risking lock expiry / duplicate delivery (and stale-lease\n // rejections). Cap the activation batch to `concurrency` when it is a finite number,\n // matching the Promise worker's headroom-bounded activation. When `concurrency` is\n // `'unbounded'`, leave the batch as configured.\n const maxJobsToActivate =\n typeof concurrency === 'number'\n ? Math.min(config.maxJobsToActivate ?? DEFAULT_MAX_JOBS, concurrency)\n : config.maxJobsToActivate;\n return activateJobsStream<R>(config.type, { ...config, maxJobsToActivate }).pipe(\n Stream.mapEffect(\n (job) =>\n // A per-job acknowledgement failure (completeJob/failJob/throwJobError raising a\n // `DomainError`) must not tear down the worker: a transient ack outage on one job\n // would otherwise fail `Stream.runDrain` and stop all processing. Log it and keep\n // going so `join` ends only on a fatal activation error (its documented contract),\n // matching the Promise worker which swallows per-job ack failures. Two causes are\n // re-raised instead of swallowed: a pure interruption (scope close / `interrupt`),\n // so shutdown and the best-effort lease release in `processJob` still propagate;\n // and a genuine defect (`die`), so an unexpected bug surfaces rather than hiding —\n // note handler defects are already converted to a best-effort failJob inside\n // `processJob`, so only truly unexpected defects reach here.\n // NOTE: `Effect.catchAll` is avoided deliberately — under the effect 4.0.0-rc it\n // suppresses `Stream.mapEffect` emission even on the success path; `catchCause`\n // does not.\n processJob(config, job).pipe(\n Effect.catchCause((cause) =>\n Cause.hasInterruptsOnly(cause) || Cause.hasDies(cause)\n ? Effect.failCause(cause)\n : Effect.logError(\n `effect-worker[${config.type}]: acknowledging job ${job.jobKey} failed; continuing`,\n cause\n )\n )\n ),\n { concurrency }\n ),\n Stream.runDrain\n );\n}\n\n/**\n * Create and start an Effect job worker, forked into the current `Scope`. The worker\n * runs until its scope closes (or {@link CamundaEffectWorkerHandle.interrupt} is run),\n * at which point it is interrupted and any in-flight job's lease is released.\n *\n * Depends on the {@link CamundaEffect} service — provide the `/effect` client `layer`.\n *\n * @description Camunda Effect Worker. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect-worker.test.ts) for example usage.\n */\nexport function createCamundaEffectWorker<A extends CompleteVars, R = never>(\n config: EffectWorkerConfig<A, R>\n): Effect.Effect<CamundaEffectWorkerHandle, never, Scope.Scope | CamundaEffect | R> {\n return Effect.gen(function* () {\n const fiber = yield* Effect.forkScoped(runWorkerLoop(config));\n return {\n type: config.type,\n join: Fiber.join(fiber),\n interrupt: Fiber.interrupt(fiber).pipe(Effect.asVoid),\n } satisfies CamundaEffectWorkerHandle;\n });\n}\n\n/**\n * A `Layer` that runs a Camunda Effect worker for the layer's lifetime. Compose it\n * with the `/effect` client `layer` (which provides its {@link CamundaEffect}\n * dependency) plus a `Layer` for the handler's own requirements `R`.\n */\nexport function workerLayer<A extends CompleteVars, R = never>(\n config: EffectWorkerConfig<A, R>\n): Layer.Layer<never, never, CamundaEffect | R> {\n return Layer.effectDiscard(createCamundaEffectWorker(config));\n}\n"],"mappings":"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","names":["CamundaValidationError","Data","Duration","Effect","Layer","Stream","Data","Duration","Effect","Stream","Layer"]}
1
+ {"version":3,"sources":["../../src/effect.ts","../../src/effect-worker.ts"],"sourcesContent":["// First-class Effect adapter for the Camunda client.\n//\n// Mirrors the Promise-based `CamundaClient` surface, mapping every method to an\n// `Effect.Effect<Awaited<R>, DomainError, never>` via the same `Proxy` +\n// method-memoization trick the former functional adapter used (it already handles\n// `CancelablePromise`/sync returns via `isPromiseLike`).\n//\n// `effect` is an OPTIONAL peer dependency: importing this module (the `./effect`\n// subpath) is the only way to pull `effect` into the runtime graph. The main `.`\n// entry never imports it, so Promise-first users are never forced to adopt Effect.\n\nimport { Context, Data, Duration, Effect, Layer, Schedule, Stream } from 'effect';\nimport { type CamundaClient, type CamundaOptions, createCamundaClient } from './gen/CamundaClient';\nimport {\n EventualConsistencyTimeoutError as RuntimeEventualConsistencyTimeoutError,\n CamundaValidationError as RuntimeValidationError,\n} from './runtime/errors';\nimport type { ConsistencyOptions } from './runtime/eventual';\nimport {\n nextPageRequest,\n type PaginationMode,\n type Paginator,\n type SearchBody,\n type SearchResponse,\n} from './runtime/pagination';\nimport type { SearchPaginateOptions } from './runtime/searchPagination';\n\n// --- Tagged domain errors -------------------------------------------------------\n//\n// Modeled as `Data.TaggedError` classes so callers discriminate with\n// `Effect.catchTag` / `Effect.catchTags` instead of a hand-rolled classification\n// switch. Each carries a stable `_tag` literal.\n\n/** A request/response validation failure surfaced by the SDK. */\nexport class CamundaValidationError extends Data.TaggedError('CamundaValidationError')<{\n readonly side: 'request' | 'response';\n readonly operationId?: string;\n readonly summary: string;\n readonly issues: string[];\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** An eventual-consistency poll that did not converge within its budget. */\nexport class EventualConsistencyTimeout extends Data.TaggedError('EventualConsistencyTimeout')<{\n readonly attempts?: number;\n readonly elapsedMs?: number;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** An HTTP-level failure (non-2xx / transport carrying a status). */\nexport class HttpError extends Data.TaggedError('HttpError')<{\n readonly status?: number;\n readonly body?: unknown;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** Any other thrown value that does not map to a more specific tag. */\nexport class CamundaGenericError extends Data.TaggedError('CamundaGenericError')<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** The typed error channel for every Effect the client produces. */\nexport type DomainError =\n | CamundaValidationError\n | EventualConsistencyTimeout\n | HttpError\n | CamundaGenericError;\n\n/** The tag literals of the {@link DomainError} union. */\nexport type DomainErrorTag = DomainError['_tag'];\n\n// Narrow an arbitrary thrown value into the tagged `DomainError` union.\nfunction toDomainError(err: unknown): DomainError {\n if (err instanceof RuntimeValidationError) {\n return new CamundaValidationError({\n side: err.side,\n operationId: err.operationId,\n summary: err.summary,\n issues: err.issues,\n message: err.message,\n cause: err,\n });\n }\n if (err instanceof RuntimeEventualConsistencyTimeoutError) {\n return new EventualConsistencyTimeout({\n attempts: err.attempts,\n elapsedMs: err.elapsedMs,\n message: err.message,\n cause: err,\n });\n }\n const anyErr = err as { status?: unknown; body?: unknown; message?: unknown } | null | undefined;\n if (anyErr && typeof anyErr === 'object' && typeof anyErr.status === 'number') {\n return new HttpError({\n status: anyErr.status,\n body: anyErr.body,\n message: typeof anyErr.message === 'string' ? anyErr.message : `HTTP ${anyErr.status}`,\n cause: err,\n });\n }\n if (err instanceof Error) {\n return new CamundaGenericError({ message: err.message, cause: err });\n }\n return new CamundaGenericError({ message: String(err), cause: err });\n}\n\n// --- Paginated search -----------------------------------------------------------\n//\n// The Promise client attaches a `.paginate` helper to every `search*` operation\n// (`installSearchPagination`), returning a lazy `AsyncIterable` `Paginator`. The\n// Effect client keeps that helper but re-expresses it in Effect terms: pages and\n// items become `Stream`s, and the eager drain becomes an `Effect`.\n//\n// The page-advance *algebra* is shared with the Promise engine — `nextPageRequest`\n// (pure) decides cursor-vs-offset and end-of-results identically for both. Only the\n// driver differs, and it has to: the Promise engine is an async generator advanced\n// by `AbortSignal`, and bridging that into a `Stream` (`Stream.fromAsyncIterable`)\n// pulls through an uninterruptible `Effect.tryPromise`, so a fiber blocked on an\n// in-flight page could not be interrupted. Driving the loop with the Effect search\n// call — which already cancels its `CancelablePromise` on interrupt — keeps the\n// whole stream interruptible.\n\n/** Options for the Effect client's `.paginate`. */\nexport interface EffectPaginateOptions<TData = unknown> {\n /**\n * Safety cap on pages fetched (default: unbounded). A non-positive value fetches\n * no pages at all — the cap is enforced *before* the first request.\n */\n readonly maxPages?: number;\n /** How to advance. `auto` prefers a cursor, falls back to offset. */\n readonly mode?: PaginationMode;\n /**\n * Eventual-consistency controls forwarded to the underlying search call. Defaults\n * to `{ waitUpToMs: 0 }`. Only the **first** page honours this window: once paging\n * is under way an empty page is end-of-results, not a not-yet-consistent read.\n */\n readonly consistency?: ConsistencyOptions<TData>;\n}\n\n/**\n * The Effect-flavoured counterpart of a {@link Paginator}: the same three views\n * (`pages` / `items` / `toArray`) over a multi-page search, as `Stream`s and an\n * `Effect` rather than async iterables and a `Promise`.\n *\n * Every view is lazy — a page is fetched only when pulled — and interruptible:\n * interrupting the fiber cancels the in-flight page request rather than leaving it\n * to settle unobserved.\n */\nexport interface EffectPaginator<TItem> {\n /** A `Stream` of whole pages, each fetched lazily as it is pulled. */\n pages(): Stream.Stream<SearchResponse<TItem>, DomainError>;\n /** A `Stream` of individual items, flattened across all pages. */\n items(): Stream.Stream<TItem, DomainError>;\n /** Eagerly drains every item into an array. Bounded result sets only. */\n toArray(): Effect.Effect<TItem[], DomainError>;\n}\n\n/** The Effect-returning search call a paginator drives. */\ntype EffectSearch = (\n body: SearchBody,\n consistencyArg: { consistency: ConsistencyOptions<unknown> }\n) => Effect.Effect<SearchResponse<unknown>, DomainError>;\n\n/** Build the Effect `.paginate` helper for one Effect-returning search operation. */\nfunction effectifyPaginate(search: EffectSearch) {\n return (body: SearchBody, opts: EffectPaginateOptions = {}): EffectPaginator<unknown> => {\n const { consistency, maxPages = Number.POSITIVE_INFINITY, mode = 'auto' } = opts;\n\n const pagesFrom = (\n pageBody: SearchBody,\n remaining: number,\n isFirstPage: boolean\n ): Stream.Stream<SearchResponse<unknown>, DomainError> => {\n if (remaining <= 0) return Stream.empty;\n // Waiting on eventual consistency only makes sense for the initial query: once\n // paging forward, an empty page means end-of-results, not an inconsistent read,\n // so waiting (and its terminal timeout) must not apply to later fetches.\n const consistencyArg = {\n consistency: isFirstPage ? (consistency ?? { waitUpToMs: 0 }) : { waitUpToMs: 0 },\n } as { consistency: ConsistencyOptions<unknown> };\n\n return Stream.unwrap(\n search(pageBody, consistencyArg).pipe(\n Effect.map((response) => {\n const next = nextPageRequest(pageBody, response, mode);\n const head = Stream.make(response);\n return next === null\n ? head\n : Stream.concat(head, pagesFrom(next, remaining - 1, false));\n })\n )\n );\n };\n\n const pages = () => pagesFrom(body, maxPages, true);\n const items = () =>\n pages().pipe(\n Stream.map((response) => response.items),\n Stream.flattenIterable\n );\n\n return {\n pages,\n items,\n toArray: () => Stream.runCollect(items()).pipe(Effect.map((chunk) => [...chunk])),\n };\n };\n}\n\n// --- Client shape ---------------------------------------------------------------\n\n/** Keys of `C` whose values are callable. */\nexport type FnKeys<C> = { [K in keyof C]: C[K] extends (...a: any) => any ? K : never }[keyof C];\n\n/**\n * Helpers attached to a client *method* (not to the client), re-expressed in Effect\n * terms. Resolves to `unknown` — the identity of `&` — for a method that carries none.\n */\ntype EffectifyMethodHelpers<F> = F extends {\n paginate(body: infer B, opts?: SearchPaginateOptions<infer D>): Paginator<infer I>;\n}\n ? { paginate(body: B, opts?: EffectPaginateOptions<D>): EffectPaginator<I> }\n : unknown;\n\n/** Maps a single method to its Effect-returning form, keeping its attached helpers. */\ntype EffectifyMethod<F> = F extends (...a: infer A) => infer R\n ? ((...a: A) => Effect.Effect<Awaited<R>, DomainError, never>) & EffectifyMethodHelpers<F>\n : never;\n\n/** Maps every method of `C` to an Effect-returning method, preserving non-fn members. */\nexport type Effectify<C> = {\n [K in FnKeys<C>]: EffectifyMethod<C[K]>;\n} & { inner: C } & { [K in Exclude<keyof C, FnKeys<C>>]: C[K] };\n\n/** The Effect-flavoured Camunda client. Every operation returns an `Effect`. */\nexport type CamundaEffectClient = Effectify<CamundaClient>;\n\n// Runtime detection of promise-like (includes CancelablePromise).\nfunction isPromiseLike<T>(v: unknown): v is Promise<T> {\n return !!v && typeof (v as { then?: unknown }).then === 'function';\n}\n\n/**\n * Create an Effect-flavoured Camunda client.\n *\n * Every `CamundaClient` method becomes `(...args) => Effect.Effect<Awaited<R>,\n * DomainError, never>`. Failures are narrowed into the tagged {@link DomainError}\n * union so callers use `Effect.catchTag`/`catchTags`. The underlying throwing\n * client is reachable via the `.inner` escape hatch.\n *\n * @description Camunda Effect Client. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect.test.ts) for example usage.\n */\nexport function createCamundaEffectClient(options?: CamundaOptions): CamundaEffectClient {\n const base = createCamundaClient(options);\n const cache = new Map<string | symbol, unknown>();\n\n function wrap(fn: (...a: any[]) => any) {\n const wrapped = (...args: any[]) =>\n Effect.suspend(() => {\n // The generated client can throw *synchronously* (e.g. an eventual\n // endpoint invoked without `consistencyManagement`). Map those to the\n // tagged error channel too, rather than letting them escape as a defect.\n let r: unknown;\n try {\n r = fn.apply(base, args);\n } catch (e) {\n return Effect.fail(toDomainError(e));\n }\n if (!isPromiseLike(r)) return Effect.succeed(r);\n const promise = r;\n return Effect.callback<any, DomainError>((resume) => {\n promise.then(\n (a: unknown) => resume(Effect.succeed(a)),\n (e: unknown) => resume(Effect.fail(toDomainError(e)))\n );\n // Honour Effect interruption: if the fiber is interrupted (e.g. by\n // `withTimeout` or a parent), cancel the underlying `CancelablePromise`\n // so the in-flight HTTP request is aborted instead of leaking.\n const cancel = (r as { cancel?: () => void }).cancel;\n return typeof cancel === 'function' ? Effect.sync(() => cancel.call(r)) : undefined;\n });\n });\n\n // A fresh arrow carries none of the own properties the runtime attaches to the\n // original method. Re-attach the ones the client installs, adapted to Effect —\n // otherwise the Effect client silently loses part of the Promise client's API\n // (`.paginate` on all ~48 `search*` operations). Guarded by\n // `tests/effect-client-surface.test.ts`, which fails if a new method-attached\n // helper appears here without being adapted.\n //\n // The Effect paginator drives `wrapped` (this very Effect search call), not the\n // Promise `.paginate`; the presence of `.paginate` on the original is what marks\n // this method as a paginated search operation.\n if (typeof (fn as { paginate?: unknown }).paginate === 'function') {\n (wrapped as { paginate?: unknown }).paginate = effectifyPaginate(\n wrapped as unknown as EffectSearch\n );\n }\n\n return wrapped;\n }\n\n const handler: ProxyHandler<any> = {\n get(_t, prop: string | symbol) {\n if (prop === 'inner') return base;\n if (cache.has(prop)) return cache.get(prop);\n const value = (base as any)[prop];\n if (typeof value === 'function') {\n const w = wrap(value);\n cache.set(prop, w);\n return w;\n }\n cache.set(prop, value);\n return value;\n },\n };\n\n return new Proxy({}, handler) as CamundaEffectClient;\n}\n\n// --- Combinators (Effect-native) ------------------------------------------------\n\n/**\n * Human-readable rendering of a `Duration.Input` for error messages. `String()`\n * degrades to `[object Object]` for non-string inputs (millis-numbers, `Duration`\n * values, `[seconds, nanos]` tuples); this normalises them via `Duration.format`.\n */\nfunction formatDuration(d: Duration.Input): string {\n // Only used to build error messages. `fromInputUnsafe` throws on a malformed\n // (but statically typed) Duration.Input, so guard it — a formatting failure\n // must never turn a typed timeout failure into an unexpected defect.\n try {\n return Duration.format(Duration.fromInputUnsafe(d));\n } catch {\n return String(d);\n }\n}\n\n/**\n * Retry an effect with exponential backoff (+ jitter), capped attempts, and an\n * optional predicate over the error.\n */\nexport function retryWithBackoff<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n opts: {\n max: number;\n baseDelay?: Duration.Input;\n while?: (e: E) => boolean;\n }\n): Effect.Effect<A, E, R> {\n const backoff = Schedule.exponential(opts.baseDelay ?? '100 millis').pipe(Schedule.jittered);\n // `times: n` caps at n retries after the initial attempt → n+1 total attempts.\n // (v4 dropped `Schedule.intersect`; the retry options object composes the\n // exponential-backoff schedule with the attempt cap and the error predicate.)\n return Effect.retry(effect, {\n schedule: backoff,\n times: Math.max(0, opts.max - 1),\n while: opts.while,\n });\n}\n\n/**\n * Fail an effect with a real interruption if it does not settle within `duration`\n * (true interruption, not a best-effort `Promise.race`).\n */\nexport function withTimeout<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n duration: Duration.Input,\n onTimeout?: () => E | EventualConsistencyTimeout\n): Effect.Effect<A, E | EventualConsistencyTimeout, R> {\n return Effect.timeoutOrElse(effect, {\n duration,\n orElse: () =>\n Effect.fail(\n onTimeout\n ? onTimeout()\n : new EventualConsistencyTimeout({\n message: `Timed out after ${formatDuration(duration)}`,\n })\n ),\n });\n}\n\n/**\n * Poll `effect` on the Effect `Clock` until `predicate` holds, timing out to\n * {@link EventualConsistencyTimeout} once `waitUpTo` elapses. Because it uses the\n * Effect `Clock` (not `Date.now`/`setTimeout`), `TestClock.adjust` advances it\n * deterministically in tests — no real-clock burn.\n */\nexport function eventually<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n predicate: (a: A) => boolean,\n opts: {\n waitUpTo: Duration.Input;\n interval?: Duration.Input;\n }\n): Effect.Effect<A, E | EventualConsistencyTimeout, R> {\n const interval = opts.interval ?? '500 millis';\n // Poll on the Effect `Clock` (Effect.sleep), returning the value A once the\n // predicate holds. Kept as an Effect-valued loop (not `Effect.repeat` with a\n // schedule, whose success channel is the schedule output, not A).\n const poll: Effect.Effect<A, E, R> = Effect.suspend(() =>\n effect.pipe(\n Effect.flatMap((a) =>\n predicate(a) ? Effect.succeed(a) : Effect.sleep(interval).pipe(Effect.andThen(poll))\n )\n )\n );\n return withTimeout(\n poll,\n opts.waitUpTo,\n () =>\n new EventualConsistencyTimeout({\n message: `Eventual consistency timeout after ${formatDuration(opts.waitUpTo)}`,\n })\n );\n}\n\n// --- Dependency injection -------------------------------------------------------\n\n/**\n * `Context` service key for the Effect Camunda client. Compose worker/orchestration\n * code against this tag and provide {@link layer} (or a test double) via `Layer`.\n */\nexport class CamundaEffect extends Context.Service<CamundaEffect, CamundaEffectClient>()(\n 'CamundaEffect'\n) {}\n\n/**\n * A `Layer` that constructs a {@link CamundaEffectClient} and provides it as the\n * {@link CamundaEffect} service. Swap in a test double by providing a different\n * `Layer` for the same tag.\n */\nexport function layer(options?: CamundaOptions): Layer.Layer<CamundaEffect> {\n return Layer.sync(CamundaEffect, () => createCamundaEffectClient(options));\n}\n","// Effect-native job-worker surface for the Camunda client.\n//\n// Where `./effect` (see `effect.ts`) gives an Effect surface over the *single-call*\n// client (deploy/create/search/…), this adds the Effect surface for **job workers**:\n// the long-running `activateJobs` → handle → `completeJob`/`failJob` loop.\n//\n// It composes over the existing client machinery — activation goes through the same\n// `activateJobs` call (and therefore the same backpressure/retry runtime) as the\n// Promise worker — rather than reimplementing activation. What it adds on top is\n// Effect-native:\n// - a job handler `(job) => Effect<CompleteVars, JobError, R>` with a **typed\n// failure channel** (retryable → `failJob` with decremented retries; terminal →\n// `throwJobError`/incident);\n// - **`Schedule`** for activation backoff and in-handler retry, both running on the\n// Effect `Clock` (so `TestClock` makes their timing virtual in tests);\n// - **`Stream`** with bounded `concurrency` for backpressure over the activation loop;\n// - **`Layer`**/**`Scope`** DI + lifecycle: the worker forks into a scope and is\n// interrupted (with a best-effort lease release) when the scope closes.\n//\n// `effect` remains an OPTIONAL peer dependency: importing this module (via the\n// `./effect` subpath) is the only way to pull `effect` into the runtime graph. The\n// main `.` (Promise) worker API is untouched.\n\nimport {\n Cause,\n Data,\n Duration,\n Effect,\n Fiber,\n Layer,\n type Schedule,\n type Scope,\n Stream,\n} from 'effect';\nimport { CamundaEffect, type DomainError } from './effect';\nimport type { ActivatedJobResult } from './gen/types.gen';\nimport { isPresentWhenUnsupportedError } from './runtime/errors';\n\n/**\n * Is this Effect `DomainError` a terminal dependent-presence contract mismatch?\n *\n * The Effect client narrows the plain `PresentWhenUnsupportedError` thrown by a\n * generated `x-present-when` guard into the tagged `DomainError` channel, keeping\n * the original error as `cause`. Retrying it is futile (an older server can never\n * satisfy the derived presence contract), so activation retry must exclude it.\n */\nfunction isTerminalContractError(e: DomainError): boolean {\n return (\n isPresentWhenUnsupportedError(e) ||\n isPresentWhenUnsupportedError((e as { cause?: unknown }).cause)\n );\n}\n\n// --- Job shape ------------------------------------------------------------------\n\n/**\n * A single activated job handed to an Effect handler. This is the raw activation\n * payload (variables + custom headers + lifecycle keys); acknowledgement is driven\n * by the value/error the handler's `Effect` produces, not by imperative methods.\n */\nexport type Job = ActivatedJobResult;\n\n/** Variables to complete a job with. `void`/`undefined` completes with no variables. */\n// biome-ignore lint/suspicious/noConfusingVoidType: a handler may complete a job with no variables, i.e. return Effect<void>\nexport type CompleteVars = { readonly [key: string]: unknown } | void | undefined;\n\n// --- Typed job-failure channel --------------------------------------------------\n//\n// A handler discriminates its two failure modes with tagged errors instead of a\n// hand-rolled classification: a **retryable** failure decrements retries and calls\n// `failJob` (optionally after in-handler `Schedule` retries); a **terminal** failure\n// raises a BPMN error / incident via `throwJobError`.\n\n/**\n * A retryable job failure: the handler could not complete the job now, but a later\n * activation might succeed. Mapped to `failJob` with `retries - 1` and an optional\n * server-side re-activation backoff.\n */\nexport class RetryableJobError extends Data.TaggedError('RetryableJobError')<{\n readonly message: string;\n /** Server-side delay before the job becomes re-activatable (`failJob` `retryBackOff`). */\n readonly retryBackoff?: Duration.Input;\n /** Optional variables to attach to the job on failure. */\n readonly variables?: { readonly [key: string]: unknown };\n readonly cause?: unknown;\n}> {}\n\n/**\n * A terminal job failure: retrying will not help. Mapped to `throwJobError`, which\n * is caught by a matching BPMN error boundary event or — if uncaught — raises an\n * incident.\n */\nexport class TerminalJobError extends Data.TaggedError('TerminalJobError')<{\n /** The BPMN error code matched against an error catch event. */\n readonly code: string;\n readonly message: string;\n /** Optional variables to instantiate at the error catch event's scope. */\n readonly variables?: { readonly [key: string]: unknown };\n readonly cause?: unknown;\n}> {}\n\n/** The typed error channel a job handler may fail with. */\nexport type JobError = RetryableJobError | TerminalJobError;\n\n/** A job handler: consumes a {@link Job}, produces completion variables or a {@link JobError}. */\nexport type JobHandler<A extends CompleteVars, R> = (job: Job) => Effect.Effect<A, JobError, R>;\n\n// --- Configuration --------------------------------------------------------------\n\nexport interface ActivateJobsStreamOptions<R = never> {\n /** Worker name recorded on the activation request. Defaults to `effect-worker-<type>-<n>`, where `<n>` is an incrementing per-process counter. */\n readonly workerName?: string;\n /** Max jobs to activate per poll (the activation batch size). Default `10`. */\n readonly maxJobsToActivate?: number;\n /**\n * Delay between polls that returned **no** jobs, on the Effect `Clock` (so it is\n * virtual under `TestClock`). A poll that returns jobs schedules the next poll\n * immediately. Default `1 second`.\n */\n readonly pollInterval?: Duration.Input;\n /** Per-job activation lock timeout (server-side). Default `60 seconds`. */\n readonly jobTimeout?: Duration.Input;\n /**\n * Long-poll request timeout. `0` (the default) lets the broker hold the request\n * for its configured default; a negative value returns immediately when idle.\n */\n readonly requestTimeout?: Duration.Input | number;\n /** Restrict activation to these variable names. */\n readonly fetchVariables?: readonly string[];\n /**\n * Activate jobs with a lease — default `false`.\n *\n * When `true`, each activated job is assigned a distinct, opaque lease token\n * (`ActivatedJobResult.leaseToken`) that is threaded back into the fenced\n * `completeJob` / `failJob` / `throwJobError` commands, fencing them against a\n * superseded activation of the same job.\n *\n * Note: the Effect `handler` intentionally keeps the base job shape\n * (`leaseToken` remains optional/nullable); the token is threaded back into\n * fenced commands automatically, so handlers do not read it directly.\n */\n readonly withLease?: boolean;\n /**\n * `Schedule` used to back off and retry a **failed activation request** (transport\n * outage, broker restart, transient server error). Runs on the Effect `Clock`.\n * When omitted, an activation failure fails the stream.\n */\n readonly activationRetrySchedule?: Schedule.Schedule<unknown, DomainError, never, R>;\n}\n\nexport interface EffectWorkerConfig<A extends CompleteVars, R = never>\n extends ActivateJobsStreamOptions<R> {\n /** The job type to activate. */\n readonly type: string;\n /** The Effect job handler. */\n readonly handler: JobHandler<A, R>;\n /**\n * Max jobs processed concurrently (handler parallelism / backpressure). The\n * activation loop will not pull faster than handlers drain, and the activation\n * batch is capped to this value (when it is a finite number) so the worker never\n * leases more jobs than it can process at once. Default: the value of\n * {@link ActivateJobsStreamOptions.maxJobsToActivate} (or `10`).\n */\n readonly concurrency?: number | 'unbounded';\n /**\n * `Schedule` used to retry the **handler** in-process on a {@link RetryableJobError}\n * before the job is failed back to the broker. Runs on the Effect `Clock`\n * (virtual under `TestClock`). A {@link TerminalJobError} is never retried.\n */\n readonly handlerRetrySchedule?: Schedule.Schedule<unknown, JobError, never, R>;\n}\n\n/** A handle to a running Effect worker. */\nexport interface CamundaEffectWorkerHandle {\n /** The job type this worker activates. */\n readonly type: string;\n /** Completes when the worker loop ends (only on a fatal, non-retryable activation error). */\n readonly join: Effect.Effect<void, DomainError>;\n /** Interrupt the worker (also happens automatically when the owning scope closes). */\n readonly interrupt: Effect.Effect<void>;\n}\n\nconst DEFAULT_MAX_JOBS = 10;\nconst DEFAULT_POLL_INTERVAL: Duration.Input = '1 second';\nconst DEFAULT_JOB_TIMEOUT: Duration.Input = '60 seconds';\nconst DEFAULT_LONGPOLL_TIMEOUT = 0;\n\nlet _workerCounter = 0;\n\nfunction toMillis(d: Duration.Input): number {\n return Duration.toMillis(Duration.fromInputUnsafe(d));\n}\n\n// --- Activation stream ----------------------------------------------------------\n\n/**\n * A `Stream` of activated jobs of `type`. Repeatedly calls `activateJobs` (through the\n * `/effect` client `Layer`, i.e. the same backpressure-aware runtime the Promise worker\n * uses) and emits each activated job. The between-empty-polls delay and the optional\n * activation-retry `Schedule` both run on the Effect `Clock`, so `TestClock.adjust`\n * advances them deterministically in tests.\n */\nexport function activateJobsStream<R = never>(\n type: string,\n options: ActivateJobsStreamOptions<R> = {}\n): Stream.Stream<Job, DomainError, CamundaEffect | R> {\n const worker = options.workerName ?? `effect-worker-${type}-${++_workerCounter}`;\n const batch = options.maxJobsToActivate ?? DEFAULT_MAX_JOBS;\n const pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n const jobTimeout = options.jobTimeout ?? DEFAULT_JOB_TIMEOUT;\n const requestTimeout =\n typeof options.requestTimeout === 'number'\n ? options.requestTimeout\n : options.requestTimeout !== undefined\n ? toMillis(options.requestTimeout)\n : DEFAULT_LONGPOLL_TIMEOUT;\n\n const body = {\n type,\n worker,\n maxJobsToActivate: batch,\n requestTimeout,\n timeout: toMillis(jobTimeout),\n ...(options.fetchVariables && options.fetchVariables.length > 0\n ? { fetchVariable: [...options.fetchVariables] }\n : {}),\n ...(options.withLease ? { withLease: true } : {}),\n };\n\n const pollOnce: Effect.Effect<Job[], DomainError, CamundaEffect> = Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n const result = yield* camunda.activateJobs(body);\n const jobs = (result?.jobs ?? []) as Job[];\n // Empty poll → wait on the Effect Clock before the next activation (virtual under\n // TestClock). A non-empty poll proceeds immediately; downstream `concurrency`\n // provides the backpressure.\n if (jobs.length === 0) yield* Effect.sleep(pollInterval);\n return jobs;\n });\n\n const activation: Effect.Effect<Job[], DomainError, CamundaEffect | R> =\n options.activationRetrySchedule\n ? Effect.retry(pollOnce, {\n schedule: options.activationRetrySchedule,\n // A dependent-presence contract mismatch (`PresentWhenUnsupportedError`,\n // e.g. `withLease` requested against a server that predates lease tokens)\n // is TERMINAL: retrying the same request against the same server can\n // never satisfy the derived presence contract. Stop instead of looping on\n // the user's retry schedule, matching the terminal handling in the plain\n // and threaded worker poll loops. The Effect client maps the thrown guard\n // error into the `DomainError` channel while preserving the original as\n // `cause`, so we discriminate it there.\n while: (e) => !isTerminalContractError(e),\n })\n : pollOnce;\n\n return Stream.fromIterableEffectRepeat(activation);\n}\n\n// --- Worker loop ----------------------------------------------------------------\n\nfunction completeJob<A extends CompleteVars>(\n job: Job,\n vars: A\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.completeJob({\n jobKey: job.jobKey,\n variables: (vars ?? {}) as { [key: string]: unknown },\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\nfunction failJobRetryable(\n job: Job,\n error: RetryableJobError\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.failJob({\n jobKey: job.jobKey,\n errorMessage: error.message,\n retries: Math.max(0, (job.retries ?? 1) - 1),\n ...(error.retryBackoff !== undefined ? { retryBackOff: toMillis(error.retryBackoff) } : {}),\n ...(error.variables ? { variables: { ...error.variables } } : {}),\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\nfunction raiseIncident(\n job: Job,\n error: TerminalJobError\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.throwJobError({\n jobKey: job.jobKey,\n errorCode: error.code,\n errorMessage: error.message,\n ...(error.variables ? { variables: { ...error.variables } } : {}),\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\n// On interruption mid-handling, release the lease by failing the job back with its\n// retries **unchanged** so it re-activates promptly instead of waiting for the lock\n// to expire. Best-effort: a failure here (e.g. shutdown racing the HTTP call) is\n// swallowed so it cannot turn interruption into a defect.\nfunction releaseLease(job: Job): Effect.Effect<void, never, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* camunda.failJob({\n jobKey: job.jobKey,\n errorMessage: 'worker interrupted; releasing lease for re-activation',\n retries: job.retries ?? 1,\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n }).pipe(Effect.ignore);\n}\n\n// An unexpected handler *defect* (an untyped throw / `Effect.die`, not a typed\n// `JobError`) is a bug, not a modelled outcome. Rather than let it be swallowed by the\n// worker-loop cause handler — which would leave the job unacknowledged until its\n// server-side lock expires (risking duplicate delivery) and hide the bug — translate it\n// into a best-effort `failJob` with `retries - 1`, matching the Promise worker\n// (`src/runtime/jobWorker.ts`). The defect is logged first so the handler bug stays\n// visible. This raises a `DomainError` on the ack channel if `failJob` itself fails,\n// which the worker loop then logs-and-continues like any other ack failure.\nfunction failJobOnDefect(\n job: Job,\n defect: unknown\n): Effect.Effect<void, DomainError, CamundaEffect> {\n return Effect.gen(function* () {\n const camunda = yield* CamundaEffect;\n yield* Effect.logError(\n `effect-worker: handler for job ${job.jobKey} threw an unexpected defect; failing job with retries - 1`,\n defect\n );\n yield* camunda.failJob({\n jobKey: job.jobKey,\n errorMessage: defect instanceof Error ? defect.message : 'Handler error',\n retries: Math.max(0, (job.retries ?? 1) - 1),\n ...(job.leaseToken != null ? { leaseToken: job.leaseToken } : {}),\n });\n });\n}\n\nfunction processJob<A extends CompleteVars, R>(\n config: EffectWorkerConfig<A, R>,\n job: Job\n): Effect.Effect<void, DomainError, CamundaEffect | R> {\n const handled: Effect.Effect<A, JobError, R> = config.handlerRetrySchedule\n ? Effect.retry(config.handler(job), {\n schedule: config.handlerRetrySchedule,\n while: (e: JobError) => e._tag === 'RetryableJobError',\n })\n : config.handler(job);\n\n return handled.pipe(\n Effect.flatMap((vars) => completeJob(job, vars)),\n Effect.catchTag('RetryableJobError', (e) => failJobRetryable(job, e)),\n Effect.catchTag('TerminalJobError', (e) => raiseIncident(job, e)),\n // Interruption is re-raised (not a defect) by `onInterrupt` below; only genuine\n // handler defects reach here and are converted to a best-effort failJob.\n Effect.catchDefect((defect) => failJobOnDefect(job, defect)),\n Effect.onInterrupt(() => releaseLease(job))\n );\n}\n\n/**\n * The worker loop: drains {@link activateJobsStream}, running the handler for each job\n * with bounded `concurrency` (the backpressure knob), and acknowledging via\n * `completeJob`/`failJob`/`throwJobError` per the handler's typed outcome.\n */\nexport function runWorkerLoop<A extends CompleteVars, R = never>(\n config: EffectWorkerConfig<A, R>\n): Effect.Effect<void, DomainError, CamundaEffect | R> {\n const concurrency = config.concurrency ?? config.maxJobsToActivate ?? DEFAULT_MAX_JOBS;\n // Never lease more jobs per poll than we can process concurrently. Jobs activated\n // beyond `concurrency` would sit buffered in the stream while their server-side lock\n // timeout counts down, risking lock expiry / duplicate delivery (and stale-lease\n // rejections). Cap the activation batch to `concurrency` when it is a finite number,\n // matching the Promise worker's headroom-bounded activation. When `concurrency` is\n // `'unbounded'`, leave the batch as configured.\n const maxJobsToActivate =\n typeof concurrency === 'number'\n ? Math.min(config.maxJobsToActivate ?? DEFAULT_MAX_JOBS, concurrency)\n : config.maxJobsToActivate;\n return activateJobsStream<R>(config.type, { ...config, maxJobsToActivate }).pipe(\n Stream.mapEffect(\n (job) =>\n // A per-job acknowledgement failure (completeJob/failJob/throwJobError raising a\n // `DomainError`) must not tear down the worker: a transient ack outage on one job\n // would otherwise fail `Stream.runDrain` and stop all processing. Log it and keep\n // going so `join` ends only on a fatal activation error (its documented contract),\n // matching the Promise worker which swallows per-job ack failures. Two causes are\n // re-raised instead of swallowed: a pure interruption (scope close / `interrupt`),\n // so shutdown and the best-effort lease release in `processJob` still propagate;\n // and a genuine defect (`die`), so an unexpected bug surfaces rather than hiding —\n // note handler defects are already converted to a best-effort failJob inside\n // `processJob`, so only truly unexpected defects reach here.\n // NOTE: `Effect.catchAll` is avoided deliberately — under the effect 4.0.0-rc it\n // suppresses `Stream.mapEffect` emission even on the success path; `catchCause`\n // does not.\n processJob(config, job).pipe(\n Effect.catchCause((cause) =>\n Cause.hasInterruptsOnly(cause) || Cause.hasDies(cause)\n ? Effect.failCause(cause)\n : Effect.logError(\n `effect-worker[${config.type}]: acknowledging job ${job.jobKey} failed; continuing`,\n cause\n )\n )\n ),\n { concurrency }\n ),\n Stream.runDrain\n );\n}\n\n/**\n * Create and start an Effect job worker, forked into the current `Scope`. The worker\n * runs until its scope closes (or {@link CamundaEffectWorkerHandle.interrupt} is run),\n * at which point it is interrupted and any in-flight job's lease is released.\n *\n * Depends on the {@link CamundaEffect} service — provide the `/effect` client `layer`.\n *\n * @description Camunda Effect Worker. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect-worker.test.ts) for example usage.\n */\nexport function createCamundaEffectWorker<A extends CompleteVars, R = never>(\n config: EffectWorkerConfig<A, R>\n): Effect.Effect<CamundaEffectWorkerHandle, never, Scope.Scope | CamundaEffect | R> {\n return Effect.gen(function* () {\n const fiber = yield* Effect.forkScoped(runWorkerLoop(config));\n return {\n type: config.type,\n join: Fiber.join(fiber),\n interrupt: Fiber.interrupt(fiber).pipe(Effect.asVoid),\n } satisfies CamundaEffectWorkerHandle;\n });\n}\n\n/**\n * A `Layer` that runs a Camunda Effect worker for the layer's lifetime. Compose it\n * with the `/effect` client `layer` (which provides its {@link CamundaEffect}\n * dependency) plus a `Layer` for the handler's own requirements `R`.\n */\nexport function workerLayer<A extends CompleteVars, R = never>(\n config: EffectWorkerConfig<A, R>\n): Layer.Layer<never, never, CamundaEffect | R> {\n return Layer.effectDiscard(createCamundaEffectWorker(config));\n}\n"],"mappings":"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","names":["CamundaValidationError","Data","Duration","Effect","Layer","Stream","Data","Duration","Effect","Stream","Layer"]}