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

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.
@@ -61,7 +61,7 @@ function createJobProxy(jobData, client) {
61
61
  variables,
62
62
  jobKey: jobData.jobKey,
63
63
  ...result !== void 0 && { result },
64
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
64
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
65
65
  }
66
66
  ]
67
67
  };
@@ -75,7 +75,7 @@ function createJobProxy(jobData, client) {
75
75
  {
76
76
  ...reason,
77
77
  jobKey: jobData.jobKey,
78
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
78
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
79
79
  }
80
80
  ]
81
81
  };
@@ -89,7 +89,7 @@ function createJobProxy(jobData, client) {
89
89
  {
90
90
  ...error,
91
91
  jobKey: jobData.jobKey,
92
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
92
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
93
93
  }
94
94
  ]
95
95
  };
@@ -110,12 +110,12 @@ function createJobProxy(jobData, client) {
110
110
  job.modifyJobTimeout = ({ newTimeoutMs }) => client.updateJob({
111
111
  changeset: { timeout: newTimeoutMs },
112
112
  jobKey: jobData.jobKey,
113
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
113
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
114
114
  });
115
115
  job.modifyRetries = ({ retries }) => client.updateJob({
116
116
  changeset: { retries },
117
117
  jobKey: jobData.jobKey,
118
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
118
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
119
119
  });
120
120
  return job;
121
121
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.ts","../src/runtime/moduleSpecifier.ts","../src/runtime/threadJobProxy.ts"],"sourcesContent":["/**\n * Worker thread entry point for the shared ThreadPool.\n *\n * Each thread in the pool runs this script. It:\n * 1. Waits for job messages on parentPort\n * 2. Dynamically imports and caches the handler module per handlerModule path\n * 3. Creates a client proxy + job proxy per job\n * 4. Calls the handler, sends back the result\n *\n * Threads are generic — the handler module is specified per-job, not per-thread.\n * Handlers are cached by module path so each import happens only once per thread.\n */\nimport { parentPort } from 'node:worker_threads';\nimport { createClientProxy } from './clientProxy.ts';\nimport { toModuleSpecifier } from './moduleSpecifier.ts';\nimport { createJobProxy } from './threadJobProxy.ts';\n\nif (!parentPort) {\n throw new Error('threadWorkerEntry must run inside a worker_threads Worker');\n}\n\ninterface JobMessage {\n type: 'job';\n /** Unique ID for this job dispatch (correlates response) */\n taskId: string;\n /** Serialized job data (plain object, no methods) */\n jobData: Record<string, unknown>;\n /** Handler module path — resolved per job, cached per thread */\n handlerModule: string;\n}\n\ninterface JobResult {\n type: 'job-result';\n taskId: string;\n ok: boolean;\n error?: string;\n /** When the handler acknowledged the job via complete/fail/error,\n * the action is captured here for the main thread to execute. */\n completionAction?: {\n method: string;\n args: unknown[];\n };\n}\n\n/** Cache of loaded handlers keyed by module path. Each thread loads a handler once per unique path. */\nconst handlerCache = new Map<string, (job: any, client: any) => Promise<any>>();\n\nasync function loadHandler(\n handlerModule: string\n): Promise<(job: any, client: any) => Promise<any>> {\n const cached = handlerCache.get(handlerModule);\n if (cached) return cached;\n\n const mod = await import(toModuleSpecifier(handlerModule));\n const handler = mod.default ?? mod.handler;\n if (typeof handler !== 'function') {\n throw new Error(\n `Threaded handler module '${handlerModule}' must export a default function or named 'handler' export`\n );\n }\n handlerCache.set(handlerModule, handler);\n return handler;\n}\n\nparentPort.on('message', async (msg: JobMessage & { clientPort?: any }) => {\n if (msg.type !== 'job') return;\n\n const { taskId, jobData, handlerModule, clientPort } = msg as JobMessage & {\n clientPort: import('node:worker_threads').MessagePort;\n };\n\n try {\n const handlerFn = await loadHandler(handlerModule);\n\n // Create a client proxy for this job's MessagePort\n const clientProxy = createClientProxy(clientPort);\n\n // Build a job-like object with proxied action methods\n const job = createJobProxy(jobData, clientProxy);\n\n await handlerFn(job, clientProxy);\n\n // If the handler never acknowledged the job (no complete/fail/error/ignore),\n // treat it as an error so the broker can retry rather than silently dropping it.\n if (!job.acknowledged) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error:\n `Handler for '${handlerModule}' returned without acknowledging the job. ` +\n 'Call job.complete(), job.fail(), job.error(), or job.ignore().',\n };\n parentPort!.postMessage(result);\n return;\n }\n\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: true,\n completionAction: job._completionAction || undefined,\n };\n parentPort!.postMessage(result);\n } catch (err: any) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error: err?.message || String(err),\n };\n parentPort!.postMessage(result);\n } finally {\n // Close this job's client port\n if (clientPort && typeof clientPort.close === 'function') {\n clientPort.close();\n }\n }\n});\n\n// Signal ready\nparentPort.postMessage({ type: 'ready' });\n","/**\n * Client proxy for worker_threads: allows threaded job handlers to call\n * CamundaClient methods transparently via MessagePort.\n *\n * Main thread side: installClientCallHandler(port, client)\n * Worker thread side: createClientProxy(port) → CamundaClient-shaped proxy\n */\n\nimport type { MessagePort } from 'node:worker_threads';\nimport type { CamundaClient } from '../gen/CamundaClient';\n\n// Wire protocol\nexport interface ClientCallMessage {\n type: 'client-call';\n callId: string;\n method: string;\n args: unknown[];\n}\n\nexport interface ClientCallResult {\n type: 'client-call-result';\n callId: string;\n result?: unknown;\n error?: string;\n}\n\n/** Fallback UUID generator for runtimes where globalThis.crypto.randomUUID is unavailable. */\nlet _counter = 0;\nfunction fallbackUUID(): string {\n // biome-ignore lint/plugin: correlation-id entropy, not cadence\n return `${Date.now().toString(36)}-${(++_counter).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Create a Proxy that looks like CamundaClient but forwards every method call\n * over a MessagePort to the main thread. Runs inside a worker thread.\n */\nexport function createClientProxy(port: MessagePort): CamundaClient {\n return new Proxy({} as CamundaClient, {\n get(_, method: string) {\n // Ignore symbol properties and common non-method accesses\n if (typeof method === 'symbol') return undefined;\n if (method === 'then') return undefined; // Prevent Proxy from being treated as thenable\n if (method === 'toJSON') return undefined;\n\n return (...args: unknown[]) => {\n const callId = globalThis.crypto?.randomUUID\n ? globalThis.crypto.randomUUID()\n : fallbackUUID();\n const msg: ClientCallMessage = { type: 'client-call', callId, method, args };\n port.postMessage(msg);\n return new Promise<unknown>((resolve, reject) => {\n const handler = (reply: ClientCallResult) => {\n if (reply.type !== 'client-call-result' || reply.callId !== callId) return;\n port.off('message', handler);\n if (reply.error !== undefined) {\n reject(new Error(reply.error));\n } else {\n resolve(reply.result);\n }\n };\n port.on('message', handler);\n });\n };\n },\n });\n}\n\n/**\n * Install a message handler on a MessagePort (main thread side) that\n * receives client-call messages from a worker thread and executes them\n * on the real CamundaClient instance.\n */\nexport function installClientCallHandler(port: MessagePort, client: CamundaClient): void {\n port.on('message', async (msg: ClientCallMessage) => {\n if (msg.type !== 'client-call') return;\n try {\n const fn = (client as any)[msg.method];\n if (typeof fn !== 'function') {\n throw new Error(`CamundaClient has no method '${msg.method}'`);\n }\n const result = await fn.apply(client, msg.args);\n const reply: ClientCallResult = { type: 'client-call-result', callId: msg.callId, result };\n port.postMessage(reply);\n } catch (err: any) {\n const reply: ClientCallResult = {\n type: 'client-call-result',\n callId: msg.callId,\n error: err?.message || String(err),\n };\n port.postMessage(reply);\n }\n });\n}\n","import { resolve as resolvePath } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/**\n * Turn a handler module reference into a specifier `import()` accepts.\n *\n * Bare specifiers (`@scope/pkg`, `my-handlers`) are passed through so normal package\n * resolution applies. Anything path-shaped is resolved against the working directory and\n * converted to a `file:` URL.\n *\n * The conversion must go through `pathToFileURL`. Hand-building the URL breaks on Windows\n * in three separate ways: `file://C:/...` puts the drive letter in the *host* rather than\n * the path, backslashes are not URL-legal and need escaping, and a bare `C:\\...` handed to\n * `new URL()` parses `C:` as the scheme — which is what produces \"Only URLs with a scheme\n * in: file, data, and node are supported by the default ESM loader\".\n */\nexport function toModuleSpecifier(handlerModule: string): string {\n const isPath =\n handlerModule.startsWith('.') ||\n handlerModule.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(handlerModule);\n\n return isPath ? pathToFileURL(resolvePath(handlerModule)).href : handlerModule;\n}\n","/**\n * Build a job object with action methods that proxy through the client, used by\n * the threaded worker entry point (`threadWorkerEntry.ts`).\n *\n * Kept in its own module — separate from the worker-thread bootstrap, which\n * throws on import outside a `worker_threads` Worker — so the proxy's behaviour\n * (in particular that EVERY job action forwards the activation `leaseToken` back\n * to the main-thread client) is unit-testable directly, without spinning up a\n * real worker thread.\n *\n * The job data is plain serialized data from the main thread. Completion actions\n * (complete/fail/error/cancelWorkflow) are captured as intent on\n * `job._completionAction` and executed by the main thread; the non-completion\n * actions (modifyJobTimeout/modifyRetries) proxy straight through the client.\n */\nimport type { JobResult as ApiJobResult } from '../gen/types.gen';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency\n// chain (jobWorker.ts → ../gen/CamundaClient → entire SDK) into the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\n\nexport function createJobProxy(jobData: Record<string, unknown>, client: any): any {\n const acknowledged = { value: false };\n const ack = () => {\n acknowledged.value = true;\n job.acknowledged = true;\n };\n\n const job: any = { ...jobData };\n\n /**\n * Completion actions (complete/fail/error/cancelWorkflow) are stored as intent\n * rather than proxied through the MessagePort. The thread returns immediately,\n * and the main thread executes the API call asynchronously. This keeps threads\n * free for CPU work instead of blocking on I/O round-trips.\n */\n\n job.complete = async (variables: Record<string, unknown> = {}, result?: ApiJobResult) => {\n ack();\n job._completionAction = {\n method: 'completeJob',\n args: [\n {\n variables,\n jobKey: jobData.jobKey,\n ...(result !== undefined && { result }),\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [\n {\n ...reason,\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [\n {\n ...error,\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.cancelWorkflow = async () => {\n ack();\n job._completionAction = {\n method: 'cancelProcessInstance',\n args: [{ processInstanceKey: jobData.processInstanceKey }],\n };\n return JobActionReceipt;\n };\n\n job.ignore = async () => {\n ack();\n return JobActionReceipt;\n };\n\n // Non-completion actions still proxy through the client (rare, need response)\n job.modifyJobTimeout = ({ newTimeoutMs }: { newTimeoutMs: number }) =>\n client.updateJob({\n changeset: { timeout: newTimeoutMs },\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({\n changeset: { retries },\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n });\n\n return job;\n}\n"],"mappings":"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","names":["resolvePath","result"]}
1
+ {"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.ts","../src/runtime/moduleSpecifier.ts","../src/runtime/threadJobProxy.ts"],"sourcesContent":["/**\n * Worker thread entry point for the shared ThreadPool.\n *\n * Each thread in the pool runs this script. It:\n * 1. Waits for job messages on parentPort\n * 2. Dynamically imports and caches the handler module per handlerModule path\n * 3. Creates a client proxy + job proxy per job\n * 4. Calls the handler, sends back the result\n *\n * Threads are generic — the handler module is specified per-job, not per-thread.\n * Handlers are cached by module path so each import happens only once per thread.\n */\nimport { parentPort } from 'node:worker_threads';\nimport { createClientProxy } from './clientProxy.ts';\nimport { toModuleSpecifier } from './moduleSpecifier.ts';\nimport { createJobProxy } from './threadJobProxy.ts';\n\nif (!parentPort) {\n throw new Error('threadWorkerEntry must run inside a worker_threads Worker');\n}\n\ninterface JobMessage {\n type: 'job';\n /** Unique ID for this job dispatch (correlates response) */\n taskId: string;\n /** Serialized job data (plain object, no methods) */\n jobData: Record<string, unknown>;\n /** Handler module path — resolved per job, cached per thread */\n handlerModule: string;\n}\n\ninterface JobResult {\n type: 'job-result';\n taskId: string;\n ok: boolean;\n error?: string;\n /** When the handler acknowledged the job via complete/fail/error,\n * the action is captured here for the main thread to execute. */\n completionAction?: {\n method: string;\n args: unknown[];\n };\n}\n\n/** Cache of loaded handlers keyed by module path. Each thread loads a handler once per unique path. */\nconst handlerCache = new Map<string, (job: any, client: any) => Promise<any>>();\n\nasync function loadHandler(\n handlerModule: string\n): Promise<(job: any, client: any) => Promise<any>> {\n const cached = handlerCache.get(handlerModule);\n if (cached) return cached;\n\n const mod = await import(toModuleSpecifier(handlerModule));\n const handler = mod.default ?? mod.handler;\n if (typeof handler !== 'function') {\n throw new Error(\n `Threaded handler module '${handlerModule}' must export a default function or named 'handler' export`\n );\n }\n handlerCache.set(handlerModule, handler);\n return handler;\n}\n\nparentPort.on('message', async (msg: JobMessage & { clientPort?: any }) => {\n if (msg.type !== 'job') return;\n\n const { taskId, jobData, handlerModule, clientPort } = msg as JobMessage & {\n clientPort: import('node:worker_threads').MessagePort;\n };\n\n try {\n const handlerFn = await loadHandler(handlerModule);\n\n // Create a client proxy for this job's MessagePort\n const clientProxy = createClientProxy(clientPort);\n\n // Build a job-like object with proxied action methods\n const job = createJobProxy(jobData, clientProxy);\n\n await handlerFn(job, clientProxy);\n\n // If the handler never acknowledged the job (no complete/fail/error/ignore),\n // treat it as an error so the broker can retry rather than silently dropping it.\n if (!job.acknowledged) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error:\n `Handler for '${handlerModule}' returned without acknowledging the job. ` +\n 'Call job.complete(), job.fail(), job.error(), or job.ignore().',\n };\n parentPort!.postMessage(result);\n return;\n }\n\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: true,\n completionAction: job._completionAction || undefined,\n };\n parentPort!.postMessage(result);\n } catch (err: any) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error: err?.message || String(err),\n };\n parentPort!.postMessage(result);\n } finally {\n // Close this job's client port\n if (clientPort && typeof clientPort.close === 'function') {\n clientPort.close();\n }\n }\n});\n\n// Signal ready\nparentPort.postMessage({ type: 'ready' });\n","/**\n * Client proxy for worker_threads: allows threaded job handlers to call\n * CamundaClient methods transparently via MessagePort.\n *\n * Main thread side: installClientCallHandler(port, client)\n * Worker thread side: createClientProxy(port) → CamundaClient-shaped proxy\n */\n\nimport type { MessagePort } from 'node:worker_threads';\nimport type { CamundaClient } from '../gen/CamundaClient';\n\n// Wire protocol\nexport interface ClientCallMessage {\n type: 'client-call';\n callId: string;\n method: string;\n args: unknown[];\n}\n\nexport interface ClientCallResult {\n type: 'client-call-result';\n callId: string;\n result?: unknown;\n error?: string;\n}\n\n/** Fallback UUID generator for runtimes where globalThis.crypto.randomUUID is unavailable. */\nlet _counter = 0;\nfunction fallbackUUID(): string {\n // biome-ignore lint/plugin: correlation-id entropy, not cadence\n return `${Date.now().toString(36)}-${(++_counter).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Create a Proxy that looks like CamundaClient but forwards every method call\n * over a MessagePort to the main thread. Runs inside a worker thread.\n */\nexport function createClientProxy(port: MessagePort): CamundaClient {\n return new Proxy({} as CamundaClient, {\n get(_, method: string) {\n // Ignore symbol properties and common non-method accesses\n if (typeof method === 'symbol') return undefined;\n if (method === 'then') return undefined; // Prevent Proxy from being treated as thenable\n if (method === 'toJSON') return undefined;\n\n return (...args: unknown[]) => {\n const callId = globalThis.crypto?.randomUUID\n ? globalThis.crypto.randomUUID()\n : fallbackUUID();\n const msg: ClientCallMessage = { type: 'client-call', callId, method, args };\n port.postMessage(msg);\n return new Promise<unknown>((resolve, reject) => {\n const handler = (reply: ClientCallResult) => {\n if (reply.type !== 'client-call-result' || reply.callId !== callId) return;\n port.off('message', handler);\n if (reply.error !== undefined) {\n reject(new Error(reply.error));\n } else {\n resolve(reply.result);\n }\n };\n port.on('message', handler);\n });\n };\n },\n });\n}\n\n/**\n * Install a message handler on a MessagePort (main thread side) that\n * receives client-call messages from a worker thread and executes them\n * on the real CamundaClient instance.\n */\nexport function installClientCallHandler(port: MessagePort, client: CamundaClient): void {\n port.on('message', async (msg: ClientCallMessage) => {\n if (msg.type !== 'client-call') return;\n try {\n const fn = (client as any)[msg.method];\n if (typeof fn !== 'function') {\n throw new Error(`CamundaClient has no method '${msg.method}'`);\n }\n const result = await fn.apply(client, msg.args);\n const reply: ClientCallResult = { type: 'client-call-result', callId: msg.callId, result };\n port.postMessage(reply);\n } catch (err: any) {\n const reply: ClientCallResult = {\n type: 'client-call-result',\n callId: msg.callId,\n error: err?.message || String(err),\n };\n port.postMessage(reply);\n }\n });\n}\n","import { resolve as resolvePath } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/**\n * Turn a handler module reference into a specifier `import()` accepts.\n *\n * Bare specifiers (`@scope/pkg`, `my-handlers`) are passed through so normal package\n * resolution applies. Anything path-shaped is resolved against the working directory and\n * converted to a `file:` URL.\n *\n * The conversion must go through `pathToFileURL`. Hand-building the URL breaks on Windows\n * in three separate ways: `file://C:/...` puts the drive letter in the *host* rather than\n * the path, backslashes are not URL-legal and need escaping, and a bare `C:\\...` handed to\n * `new URL()` parses `C:` as the scheme — which is what produces \"Only URLs with a scheme\n * in: file, data, and node are supported by the default ESM loader\".\n */\nexport function toModuleSpecifier(handlerModule: string): string {\n const isPath =\n handlerModule.startsWith('.') ||\n handlerModule.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(handlerModule);\n\n return isPath ? pathToFileURL(resolvePath(handlerModule)).href : handlerModule;\n}\n","/**\n * Build a job object with action methods that proxy through the client, used by\n * the threaded worker entry point (`threadWorkerEntry.ts`).\n *\n * Kept in its own module — separate from the worker-thread bootstrap, which\n * throws on import outside a `worker_threads` Worker — so the proxy's behaviour\n * (in particular that EVERY job action forwards the activation `jobLeaseToken` back\n * to the main-thread client) is unit-testable directly, without spinning up a\n * real worker thread.\n *\n * The job data is plain serialized data from the main thread. Completion actions\n * (complete/fail/error/cancelWorkflow) are captured as intent on\n * `job._completionAction` and executed by the main thread; the non-completion\n * actions (modifyJobTimeout/modifyRetries) proxy straight through the client.\n */\nimport type { JobResult as ApiJobResult } from '../gen/types.gen';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency\n// chain (jobWorker.ts → ../gen/CamundaClient → entire SDK) into the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\n\nexport function createJobProxy(jobData: Record<string, unknown>, client: any): any {\n const acknowledged = { value: false };\n const ack = () => {\n acknowledged.value = true;\n job.acknowledged = true;\n };\n\n const job: any = { ...jobData };\n\n /**\n * Completion actions (complete/fail/error/cancelWorkflow) are stored as intent\n * rather than proxied through the MessagePort. The thread returns immediately,\n * and the main thread executes the API call asynchronously. This keeps threads\n * free for CPU work instead of blocking on I/O round-trips.\n */\n\n job.complete = async (variables: Record<string, unknown> = {}, result?: ApiJobResult) => {\n ack();\n job._completionAction = {\n method: 'completeJob',\n args: [\n {\n variables,\n jobKey: jobData.jobKey,\n ...(result !== undefined && { result }),\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [\n {\n ...reason,\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [\n {\n ...error,\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.cancelWorkflow = async () => {\n ack();\n job._completionAction = {\n method: 'cancelProcessInstance',\n args: [{ processInstanceKey: jobData.processInstanceKey }],\n };\n return JobActionReceipt;\n };\n\n job.ignore = async () => {\n ack();\n return JobActionReceipt;\n };\n\n // Non-completion actions still proxy through the client (rare, need response)\n job.modifyJobTimeout = ({ newTimeoutMs }: { newTimeoutMs: number }) =>\n client.updateJob({\n changeset: { timeout: newTimeoutMs },\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({\n changeset: { retries },\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n });\n\n return job;\n}\n"],"mappings":"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","names":["resolvePath","result"]}
@@ -59,7 +59,7 @@ function createJobProxy(jobData, client) {
59
59
  variables,
60
60
  jobKey: jobData.jobKey,
61
61
  ...result !== void 0 && { result },
62
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
62
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
63
63
  }
64
64
  ]
65
65
  };
@@ -73,7 +73,7 @@ function createJobProxy(jobData, client) {
73
73
  {
74
74
  ...reason,
75
75
  jobKey: jobData.jobKey,
76
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
76
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
77
77
  }
78
78
  ]
79
79
  };
@@ -87,7 +87,7 @@ function createJobProxy(jobData, client) {
87
87
  {
88
88
  ...error,
89
89
  jobKey: jobData.jobKey,
90
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
90
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
91
91
  }
92
92
  ]
93
93
  };
@@ -108,12 +108,12 @@ function createJobProxy(jobData, client) {
108
108
  job.modifyJobTimeout = ({ newTimeoutMs }) => client.updateJob({
109
109
  changeset: { timeout: newTimeoutMs },
110
110
  jobKey: jobData.jobKey,
111
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
111
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
112
112
  });
113
113
  job.modifyRetries = ({ retries }) => client.updateJob({
114
114
  changeset: { retries },
115
115
  jobKey: jobData.jobKey,
116
- ...jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}
116
+ ...jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}
117
117
  });
118
118
  return job;
119
119
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.ts","../src/runtime/moduleSpecifier.ts","../src/runtime/threadJobProxy.ts"],"sourcesContent":["/**\n * Worker thread entry point for the shared ThreadPool.\n *\n * Each thread in the pool runs this script. It:\n * 1. Waits for job messages on parentPort\n * 2. Dynamically imports and caches the handler module per handlerModule path\n * 3. Creates a client proxy + job proxy per job\n * 4. Calls the handler, sends back the result\n *\n * Threads are generic — the handler module is specified per-job, not per-thread.\n * Handlers are cached by module path so each import happens only once per thread.\n */\nimport { parentPort } from 'node:worker_threads';\nimport { createClientProxy } from './clientProxy.ts';\nimport { toModuleSpecifier } from './moduleSpecifier.ts';\nimport { createJobProxy } from './threadJobProxy.ts';\n\nif (!parentPort) {\n throw new Error('threadWorkerEntry must run inside a worker_threads Worker');\n}\n\ninterface JobMessage {\n type: 'job';\n /** Unique ID for this job dispatch (correlates response) */\n taskId: string;\n /** Serialized job data (plain object, no methods) */\n jobData: Record<string, unknown>;\n /** Handler module path — resolved per job, cached per thread */\n handlerModule: string;\n}\n\ninterface JobResult {\n type: 'job-result';\n taskId: string;\n ok: boolean;\n error?: string;\n /** When the handler acknowledged the job via complete/fail/error,\n * the action is captured here for the main thread to execute. */\n completionAction?: {\n method: string;\n args: unknown[];\n };\n}\n\n/** Cache of loaded handlers keyed by module path. Each thread loads a handler once per unique path. */\nconst handlerCache = new Map<string, (job: any, client: any) => Promise<any>>();\n\nasync function loadHandler(\n handlerModule: string\n): Promise<(job: any, client: any) => Promise<any>> {\n const cached = handlerCache.get(handlerModule);\n if (cached) return cached;\n\n const mod = await import(toModuleSpecifier(handlerModule));\n const handler = mod.default ?? mod.handler;\n if (typeof handler !== 'function') {\n throw new Error(\n `Threaded handler module '${handlerModule}' must export a default function or named 'handler' export`\n );\n }\n handlerCache.set(handlerModule, handler);\n return handler;\n}\n\nparentPort.on('message', async (msg: JobMessage & { clientPort?: any }) => {\n if (msg.type !== 'job') return;\n\n const { taskId, jobData, handlerModule, clientPort } = msg as JobMessage & {\n clientPort: import('node:worker_threads').MessagePort;\n };\n\n try {\n const handlerFn = await loadHandler(handlerModule);\n\n // Create a client proxy for this job's MessagePort\n const clientProxy = createClientProxy(clientPort);\n\n // Build a job-like object with proxied action methods\n const job = createJobProxy(jobData, clientProxy);\n\n await handlerFn(job, clientProxy);\n\n // If the handler never acknowledged the job (no complete/fail/error/ignore),\n // treat it as an error so the broker can retry rather than silently dropping it.\n if (!job.acknowledged) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error:\n `Handler for '${handlerModule}' returned without acknowledging the job. ` +\n 'Call job.complete(), job.fail(), job.error(), or job.ignore().',\n };\n parentPort!.postMessage(result);\n return;\n }\n\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: true,\n completionAction: job._completionAction || undefined,\n };\n parentPort!.postMessage(result);\n } catch (err: any) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error: err?.message || String(err),\n };\n parentPort!.postMessage(result);\n } finally {\n // Close this job's client port\n if (clientPort && typeof clientPort.close === 'function') {\n clientPort.close();\n }\n }\n});\n\n// Signal ready\nparentPort.postMessage({ type: 'ready' });\n","/**\n * Client proxy for worker_threads: allows threaded job handlers to call\n * CamundaClient methods transparently via MessagePort.\n *\n * Main thread side: installClientCallHandler(port, client)\n * Worker thread side: createClientProxy(port) → CamundaClient-shaped proxy\n */\n\nimport type { MessagePort } from 'node:worker_threads';\nimport type { CamundaClient } from '../gen/CamundaClient';\n\n// Wire protocol\nexport interface ClientCallMessage {\n type: 'client-call';\n callId: string;\n method: string;\n args: unknown[];\n}\n\nexport interface ClientCallResult {\n type: 'client-call-result';\n callId: string;\n result?: unknown;\n error?: string;\n}\n\n/** Fallback UUID generator for runtimes where globalThis.crypto.randomUUID is unavailable. */\nlet _counter = 0;\nfunction fallbackUUID(): string {\n // biome-ignore lint/plugin: correlation-id entropy, not cadence\n return `${Date.now().toString(36)}-${(++_counter).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Create a Proxy that looks like CamundaClient but forwards every method call\n * over a MessagePort to the main thread. Runs inside a worker thread.\n */\nexport function createClientProxy(port: MessagePort): CamundaClient {\n return new Proxy({} as CamundaClient, {\n get(_, method: string) {\n // Ignore symbol properties and common non-method accesses\n if (typeof method === 'symbol') return undefined;\n if (method === 'then') return undefined; // Prevent Proxy from being treated as thenable\n if (method === 'toJSON') return undefined;\n\n return (...args: unknown[]) => {\n const callId = globalThis.crypto?.randomUUID\n ? globalThis.crypto.randomUUID()\n : fallbackUUID();\n const msg: ClientCallMessage = { type: 'client-call', callId, method, args };\n port.postMessage(msg);\n return new Promise<unknown>((resolve, reject) => {\n const handler = (reply: ClientCallResult) => {\n if (reply.type !== 'client-call-result' || reply.callId !== callId) return;\n port.off('message', handler);\n if (reply.error !== undefined) {\n reject(new Error(reply.error));\n } else {\n resolve(reply.result);\n }\n };\n port.on('message', handler);\n });\n };\n },\n });\n}\n\n/**\n * Install a message handler on a MessagePort (main thread side) that\n * receives client-call messages from a worker thread and executes them\n * on the real CamundaClient instance.\n */\nexport function installClientCallHandler(port: MessagePort, client: CamundaClient): void {\n port.on('message', async (msg: ClientCallMessage) => {\n if (msg.type !== 'client-call') return;\n try {\n const fn = (client as any)[msg.method];\n if (typeof fn !== 'function') {\n throw new Error(`CamundaClient has no method '${msg.method}'`);\n }\n const result = await fn.apply(client, msg.args);\n const reply: ClientCallResult = { type: 'client-call-result', callId: msg.callId, result };\n port.postMessage(reply);\n } catch (err: any) {\n const reply: ClientCallResult = {\n type: 'client-call-result',\n callId: msg.callId,\n error: err?.message || String(err),\n };\n port.postMessage(reply);\n }\n });\n}\n","import { resolve as resolvePath } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/**\n * Turn a handler module reference into a specifier `import()` accepts.\n *\n * Bare specifiers (`@scope/pkg`, `my-handlers`) are passed through so normal package\n * resolution applies. Anything path-shaped is resolved against the working directory and\n * converted to a `file:` URL.\n *\n * The conversion must go through `pathToFileURL`. Hand-building the URL breaks on Windows\n * in three separate ways: `file://C:/...` puts the drive letter in the *host* rather than\n * the path, backslashes are not URL-legal and need escaping, and a bare `C:\\...` handed to\n * `new URL()` parses `C:` as the scheme — which is what produces \"Only URLs with a scheme\n * in: file, data, and node are supported by the default ESM loader\".\n */\nexport function toModuleSpecifier(handlerModule: string): string {\n const isPath =\n handlerModule.startsWith('.') ||\n handlerModule.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(handlerModule);\n\n return isPath ? pathToFileURL(resolvePath(handlerModule)).href : handlerModule;\n}\n","/**\n * Build a job object with action methods that proxy through the client, used by\n * the threaded worker entry point (`threadWorkerEntry.ts`).\n *\n * Kept in its own module — separate from the worker-thread bootstrap, which\n * throws on import outside a `worker_threads` Worker — so the proxy's behaviour\n * (in particular that EVERY job action forwards the activation `leaseToken` back\n * to the main-thread client) is unit-testable directly, without spinning up a\n * real worker thread.\n *\n * The job data is plain serialized data from the main thread. Completion actions\n * (complete/fail/error/cancelWorkflow) are captured as intent on\n * `job._completionAction` and executed by the main thread; the non-completion\n * actions (modifyJobTimeout/modifyRetries) proxy straight through the client.\n */\nimport type { JobResult as ApiJobResult } from '../gen/types.gen';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency\n// chain (jobWorker.ts → ../gen/CamundaClient → entire SDK) into the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\n\nexport function createJobProxy(jobData: Record<string, unknown>, client: any): any {\n const acknowledged = { value: false };\n const ack = () => {\n acknowledged.value = true;\n job.acknowledged = true;\n };\n\n const job: any = { ...jobData };\n\n /**\n * Completion actions (complete/fail/error/cancelWorkflow) are stored as intent\n * rather than proxied through the MessagePort. The thread returns immediately,\n * and the main thread executes the API call asynchronously. This keeps threads\n * free for CPU work instead of blocking on I/O round-trips.\n */\n\n job.complete = async (variables: Record<string, unknown> = {}, result?: ApiJobResult) => {\n ack();\n job._completionAction = {\n method: 'completeJob',\n args: [\n {\n variables,\n jobKey: jobData.jobKey,\n ...(result !== undefined && { result }),\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [\n {\n ...reason,\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [\n {\n ...error,\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.cancelWorkflow = async () => {\n ack();\n job._completionAction = {\n method: 'cancelProcessInstance',\n args: [{ processInstanceKey: jobData.processInstanceKey }],\n };\n return JobActionReceipt;\n };\n\n job.ignore = async () => {\n ack();\n return JobActionReceipt;\n };\n\n // Non-completion actions still proxy through the client (rare, need response)\n job.modifyJobTimeout = ({ newTimeoutMs }: { newTimeoutMs: number }) =>\n client.updateJob({\n changeset: { timeout: newTimeoutMs },\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({\n changeset: { retries },\n jobKey: jobData.jobKey,\n ...(jobData.leaseToken != null ? { leaseToken: jobData.leaseToken } : {}),\n });\n\n return job;\n}\n"],"mappings":"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","names":["result"]}
1
+ {"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.ts","../src/runtime/moduleSpecifier.ts","../src/runtime/threadJobProxy.ts"],"sourcesContent":["/**\n * Worker thread entry point for the shared ThreadPool.\n *\n * Each thread in the pool runs this script. It:\n * 1. Waits for job messages on parentPort\n * 2. Dynamically imports and caches the handler module per handlerModule path\n * 3. Creates a client proxy + job proxy per job\n * 4. Calls the handler, sends back the result\n *\n * Threads are generic — the handler module is specified per-job, not per-thread.\n * Handlers are cached by module path so each import happens only once per thread.\n */\nimport { parentPort } from 'node:worker_threads';\nimport { createClientProxy } from './clientProxy.ts';\nimport { toModuleSpecifier } from './moduleSpecifier.ts';\nimport { createJobProxy } from './threadJobProxy.ts';\n\nif (!parentPort) {\n throw new Error('threadWorkerEntry must run inside a worker_threads Worker');\n}\n\ninterface JobMessage {\n type: 'job';\n /** Unique ID for this job dispatch (correlates response) */\n taskId: string;\n /** Serialized job data (plain object, no methods) */\n jobData: Record<string, unknown>;\n /** Handler module path — resolved per job, cached per thread */\n handlerModule: string;\n}\n\ninterface JobResult {\n type: 'job-result';\n taskId: string;\n ok: boolean;\n error?: string;\n /** When the handler acknowledged the job via complete/fail/error,\n * the action is captured here for the main thread to execute. */\n completionAction?: {\n method: string;\n args: unknown[];\n };\n}\n\n/** Cache of loaded handlers keyed by module path. Each thread loads a handler once per unique path. */\nconst handlerCache = new Map<string, (job: any, client: any) => Promise<any>>();\n\nasync function loadHandler(\n handlerModule: string\n): Promise<(job: any, client: any) => Promise<any>> {\n const cached = handlerCache.get(handlerModule);\n if (cached) return cached;\n\n const mod = await import(toModuleSpecifier(handlerModule));\n const handler = mod.default ?? mod.handler;\n if (typeof handler !== 'function') {\n throw new Error(\n `Threaded handler module '${handlerModule}' must export a default function or named 'handler' export`\n );\n }\n handlerCache.set(handlerModule, handler);\n return handler;\n}\n\nparentPort.on('message', async (msg: JobMessage & { clientPort?: any }) => {\n if (msg.type !== 'job') return;\n\n const { taskId, jobData, handlerModule, clientPort } = msg as JobMessage & {\n clientPort: import('node:worker_threads').MessagePort;\n };\n\n try {\n const handlerFn = await loadHandler(handlerModule);\n\n // Create a client proxy for this job's MessagePort\n const clientProxy = createClientProxy(clientPort);\n\n // Build a job-like object with proxied action methods\n const job = createJobProxy(jobData, clientProxy);\n\n await handlerFn(job, clientProxy);\n\n // If the handler never acknowledged the job (no complete/fail/error/ignore),\n // treat it as an error so the broker can retry rather than silently dropping it.\n if (!job.acknowledged) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error:\n `Handler for '${handlerModule}' returned without acknowledging the job. ` +\n 'Call job.complete(), job.fail(), job.error(), or job.ignore().',\n };\n parentPort!.postMessage(result);\n return;\n }\n\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: true,\n completionAction: job._completionAction || undefined,\n };\n parentPort!.postMessage(result);\n } catch (err: any) {\n const result: JobResult = {\n type: 'job-result',\n taskId,\n ok: false,\n error: err?.message || String(err),\n };\n parentPort!.postMessage(result);\n } finally {\n // Close this job's client port\n if (clientPort && typeof clientPort.close === 'function') {\n clientPort.close();\n }\n }\n});\n\n// Signal ready\nparentPort.postMessage({ type: 'ready' });\n","/**\n * Client proxy for worker_threads: allows threaded job handlers to call\n * CamundaClient methods transparently via MessagePort.\n *\n * Main thread side: installClientCallHandler(port, client)\n * Worker thread side: createClientProxy(port) → CamundaClient-shaped proxy\n */\n\nimport type { MessagePort } from 'node:worker_threads';\nimport type { CamundaClient } from '../gen/CamundaClient';\n\n// Wire protocol\nexport interface ClientCallMessage {\n type: 'client-call';\n callId: string;\n method: string;\n args: unknown[];\n}\n\nexport interface ClientCallResult {\n type: 'client-call-result';\n callId: string;\n result?: unknown;\n error?: string;\n}\n\n/** Fallback UUID generator for runtimes where globalThis.crypto.randomUUID is unavailable. */\nlet _counter = 0;\nfunction fallbackUUID(): string {\n // biome-ignore lint/plugin: correlation-id entropy, not cadence\n return `${Date.now().toString(36)}-${(++_counter).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Create a Proxy that looks like CamundaClient but forwards every method call\n * over a MessagePort to the main thread. Runs inside a worker thread.\n */\nexport function createClientProxy(port: MessagePort): CamundaClient {\n return new Proxy({} as CamundaClient, {\n get(_, method: string) {\n // Ignore symbol properties and common non-method accesses\n if (typeof method === 'symbol') return undefined;\n if (method === 'then') return undefined; // Prevent Proxy from being treated as thenable\n if (method === 'toJSON') return undefined;\n\n return (...args: unknown[]) => {\n const callId = globalThis.crypto?.randomUUID\n ? globalThis.crypto.randomUUID()\n : fallbackUUID();\n const msg: ClientCallMessage = { type: 'client-call', callId, method, args };\n port.postMessage(msg);\n return new Promise<unknown>((resolve, reject) => {\n const handler = (reply: ClientCallResult) => {\n if (reply.type !== 'client-call-result' || reply.callId !== callId) return;\n port.off('message', handler);\n if (reply.error !== undefined) {\n reject(new Error(reply.error));\n } else {\n resolve(reply.result);\n }\n };\n port.on('message', handler);\n });\n };\n },\n });\n}\n\n/**\n * Install a message handler on a MessagePort (main thread side) that\n * receives client-call messages from a worker thread and executes them\n * on the real CamundaClient instance.\n */\nexport function installClientCallHandler(port: MessagePort, client: CamundaClient): void {\n port.on('message', async (msg: ClientCallMessage) => {\n if (msg.type !== 'client-call') return;\n try {\n const fn = (client as any)[msg.method];\n if (typeof fn !== 'function') {\n throw new Error(`CamundaClient has no method '${msg.method}'`);\n }\n const result = await fn.apply(client, msg.args);\n const reply: ClientCallResult = { type: 'client-call-result', callId: msg.callId, result };\n port.postMessage(reply);\n } catch (err: any) {\n const reply: ClientCallResult = {\n type: 'client-call-result',\n callId: msg.callId,\n error: err?.message || String(err),\n };\n port.postMessage(reply);\n }\n });\n}\n","import { resolve as resolvePath } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n/**\n * Turn a handler module reference into a specifier `import()` accepts.\n *\n * Bare specifiers (`@scope/pkg`, `my-handlers`) are passed through so normal package\n * resolution applies. Anything path-shaped is resolved against the working directory and\n * converted to a `file:` URL.\n *\n * The conversion must go through `pathToFileURL`. Hand-building the URL breaks on Windows\n * in three separate ways: `file://C:/...` puts the drive letter in the *host* rather than\n * the path, backslashes are not URL-legal and need escaping, and a bare `C:\\...` handed to\n * `new URL()` parses `C:` as the scheme — which is what produces \"Only URLs with a scheme\n * in: file, data, and node are supported by the default ESM loader\".\n */\nexport function toModuleSpecifier(handlerModule: string): string {\n const isPath =\n handlerModule.startsWith('.') ||\n handlerModule.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(handlerModule);\n\n return isPath ? pathToFileURL(resolvePath(handlerModule)).href : handlerModule;\n}\n","/**\n * Build a job object with action methods that proxy through the client, used by\n * the threaded worker entry point (`threadWorkerEntry.ts`).\n *\n * Kept in its own module — separate from the worker-thread bootstrap, which\n * throws on import outside a `worker_threads` Worker — so the proxy's behaviour\n * (in particular that EVERY job action forwards the activation `jobLeaseToken` back\n * to the main-thread client) is unit-testable directly, without spinning up a\n * real worker thread.\n *\n * The job data is plain serialized data from the main thread. Completion actions\n * (complete/fail/error/cancelWorkflow) are captured as intent on\n * `job._completionAction` and executed by the main thread; the non-completion\n * actions (modifyJobTimeout/modifyRetries) proxy straight through the client.\n */\nimport type { JobResult as ApiJobResult } from '../gen/types.gen';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency\n// chain (jobWorker.ts → ../gen/CamundaClient → entire SDK) into the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\n\nexport function createJobProxy(jobData: Record<string, unknown>, client: any): any {\n const acknowledged = { value: false };\n const ack = () => {\n acknowledged.value = true;\n job.acknowledged = true;\n };\n\n const job: any = { ...jobData };\n\n /**\n * Completion actions (complete/fail/error/cancelWorkflow) are stored as intent\n * rather than proxied through the MessagePort. The thread returns immediately,\n * and the main thread executes the API call asynchronously. This keeps threads\n * free for CPU work instead of blocking on I/O round-trips.\n */\n\n job.complete = async (variables: Record<string, unknown> = {}, result?: ApiJobResult) => {\n ack();\n job._completionAction = {\n method: 'completeJob',\n args: [\n {\n variables,\n jobKey: jobData.jobKey,\n ...(result !== undefined && { result }),\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [\n {\n ...reason,\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [\n {\n ...error,\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n },\n ],\n };\n return JobActionReceipt;\n };\n\n job.cancelWorkflow = async () => {\n ack();\n job._completionAction = {\n method: 'cancelProcessInstance',\n args: [{ processInstanceKey: jobData.processInstanceKey }],\n };\n return JobActionReceipt;\n };\n\n job.ignore = async () => {\n ack();\n return JobActionReceipt;\n };\n\n // Non-completion actions still proxy through the client (rare, need response)\n job.modifyJobTimeout = ({ newTimeoutMs }: { newTimeoutMs: number }) =>\n client.updateJob({\n changeset: { timeout: newTimeoutMs },\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({\n changeset: { retries },\n jobKey: jobData.jobKey,\n ...(jobData.jobLeaseToken != null ? { jobLeaseToken: jobData.jobLeaseToken } : {}),\n });\n\n return job;\n}\n"],"mappings":"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","names":["result"]}
@@ -584,6 +584,17 @@ var zClusterStatusResponse = z.object({
584
584
  }).register(z.globalRegistry, {
585
585
  description: "The aggregated status of the whole cluster."
586
586
  });
587
+ var zClusterUpgradeStatusResponse = z.object({
588
+ status: z.enum([
589
+ "MIGRATED",
590
+ "MIGRATION_IN_PROGRESS",
591
+ "UNKNOWN"
592
+ ]).register(z.globalRegistry, {
593
+ description: "`MIGRATED` once every known upgrade-readiness condition is met for every known physical tenant; `MIGRATION_IN_PROGRESS` when at least one is confirmed not yet migrated; `UNKNOWN` otherwise."
594
+ })
595
+ }).register(z.globalRegistry, {
596
+ description: "The upgrade-readiness status of the whole cluster."
597
+ });
587
598
  var zClusterBrokerInfo = z.object({
588
599
  brokerId: z.string().register(z.globalRegistry, {
589
600
  description: `The unique (within a cluster) broker identifier. When the cluster is not zoned, then it's a string that represents the nodeId (an integer). When the cluster is zoned, instead, it's of the form "$zoneName_$nodeId", providing uniqueness even across zones.
@@ -2011,7 +2022,7 @@ var zHistoryItemId = z.string().min(1).max(256).register(z.globalRegistry, {
2011
2022
  description: "The client-supplied identifier this item was created with."
2012
2023
  });
2013
2024
  var zJobLeaseToken = z.string().min(1).register(z.globalRegistry, {
2014
- description: "An opaque, engine-minted fencing token identifying a single activation of a job.\nReturned by Activate Jobs as `ActivatedJobResult.leaseToken` when the job is\nactivated with a lease, and passed back on fenced job commands \u2014 and on\nagent-instance creation/updates as `jobLease` \u2014 to prove the caller holds the\ncurrent lease. The token is opaque: clients may rely on its presence and equality\nonly, and must never construct, parse, or otherwise interpret it beyond equality\nchecks. It cannot be minted client-side; only the engine produces it, exactly once\nper leased activation, and clients must not depend on any particular internal format.\n"
2025
+ description: "An opaque, engine-minted fencing token identifying a single activation of a job.\nReturned by Activate Jobs as `ActivatedJobResult.jobLeaseToken` when the job is\nactivated with a lease, and passed back under the same name on fenced job\ncommands and on agent-instance creation/updates, to prove the caller holds the\ncurrent lease. The token is opaque: clients may rely on its presence and equality\nonly, and must never construct, parse, or otherwise interpret it beyond equality\nchecks. It cannot be minted client-side; only the engine produces it, exactly once\nper leased activation, and clients must not depend on any particular internal format.\n"
2015
2026
  });
2016
2027
  var zAdvancedElementIdFilter = z.object({
2017
2028
  $eq: zElementId.optional(),
@@ -2257,7 +2268,7 @@ var zJobFailRequest = z.object({
2257
2268
  variables: z.record(z.string(), z.unknown()).register(z.globalRegistry, {
2258
2269
  description: "JSON object that will instantiate the variables at the local scope of the job's associated task.\n"
2259
2270
  }).optional(),
2260
- leaseToken: zJobLeaseToken.nullish()
2271
+ jobLeaseToken: zJobLeaseToken.nullish()
2261
2272
  });
2262
2273
  var zJobErrorRequest = z.object({
2263
2274
  errorCode: z.string().register(z.globalRegistry, {
@@ -2265,7 +2276,7 @@ var zJobErrorRequest = z.object({
2265
2276
  }),
2266
2277
  errorMessage: z.string().nullish(),
2267
2278
  variables: z.record(z.string(), z.unknown()).nullish(),
2268
- leaseToken: zJobLeaseToken.nullish()
2279
+ jobLeaseToken: zJobLeaseToken.nullish()
2269
2280
  });
2270
2281
  var zJobResultCorrections = z.object({
2271
2282
  assignee: z.string().nullish(),
@@ -2314,7 +2325,7 @@ var zJobResult = z.union([
2314
2325
  var zJobCompletionRequest = z.object({
2315
2326
  variables: z.record(z.string(), z.unknown()).nullish(),
2316
2327
  result: zJobResult.optional(),
2317
- leaseToken: zJobLeaseToken.nullish(),
2328
+ jobLeaseToken: zJobLeaseToken.nullish(),
2318
2329
  businessId: zBusinessId.nullish()
2319
2330
  });
2320
2331
  var zJobChangeset = z.object({
@@ -2748,7 +2759,7 @@ var zJobKey = zLongKey;
2748
2759
  var zAgentInstanceCreationRequest = z.object({
2749
2760
  elementInstanceKey: zElementInstanceKey,
2750
2761
  jobKey: zJobKey,
2751
- jobLease: zJobLeaseToken,
2762
+ jobLeaseToken: zJobLeaseToken,
2752
2763
  history: z.array(zAgentInstanceHistoryItem).min(1).register(z.globalRegistry, {
2753
2764
  description: "A batch of history items to append to the agent instance's conversation\nhistory, in request order. Each created item is echoed back in the\nresponse's createdHistory, positionally correlated. Must include a\nCONFIGURATION item establishing model, provider, and systemPrompt (and,\nif needed, limits). Every item's role must be CONFIGURATION or USER, and\nno item may carry non-zero usage-token metrics (inputTokens, outputTokens,\nreasoningTokenCount, cacheCreationTokenCount, cacheReadTokenCount);\ndurationMs is exempt and may be non-zero.\n"
2754
2765
  })
@@ -2759,7 +2770,7 @@ var zAgentInstanceUpdateRequest = z.object({
2759
2770
  elementInstanceKey: zElementInstanceKey,
2760
2771
  status: zAgentInstanceUpdateStatusEnum.optional(),
2761
2772
  jobKey: zJobKey,
2762
- jobLease: zJobLeaseToken,
2773
+ jobLeaseToken: zJobLeaseToken,
2763
2774
  history: z.array(zAgentInstanceHistoryItem).nullish()
2764
2775
  }).register(z.globalRegistry, {
2765
2776
  description: "Request to update the mutable state of an agent instance.\n"
@@ -2870,7 +2881,7 @@ var zActivatedJobResult = z.object({
2870
2881
  priority: z.int().min(-2147483648, { error: "Invalid value: Expected int32 to be >= -2147483648" }).max(2147483647, { error: "Invalid value: Expected int32 to be <= 2147483647" }).register(z.globalRegistry, {
2871
2882
  description: "The priority of the job. Higher values indicate higher priority. Jobs created before 8.10 have no stored priority; the API returns 0 for such jobs.\n"
2872
2883
  }),
2873
- leaseToken: zJobLeaseToken.nullish()
2884
+ jobLeaseToken: zJobLeaseToken.nullish()
2874
2885
  });
2875
2886
  var zJobActivationResult = z.object({
2876
2887
  jobs: z.array(zActivatedJobResult).register(z.globalRegistry, {
@@ -3216,7 +3227,7 @@ var zBatchOperationCreatedResult = z.object({
3216
3227
  var zBatchOperationResponse = z.object({
3217
3228
  batchOperationKey: zBatchOperationKey,
3218
3229
  state: zBatchOperationStateEnum,
3219
- batchOperationType: zBatchOperationTypeEnum,
3230
+ batchOperationType: zBatchOperationTypeEnum.nullable(),
3220
3231
  startDate: z.iso.datetime().nullable(),
3221
3232
  endDate: z.iso.datetime().nullable(),
3222
3233
  actorType: zAuditLogActorTypeEnum.nullable(),
@@ -3276,7 +3287,7 @@ var zIncidentResolutionRequest = z.object({
3276
3287
  var zJobUpdateRequest = z.object({
3277
3288
  changeset: zJobChangeset,
3278
3289
  operationReference: zOperationReference.optional(),
3279
- leaseToken: zJobLeaseToken.nullish()
3290
+ jobLeaseToken: zJobLeaseToken.nullish()
3280
3291
  });
3281
3292
  var zAgentDefinitionKey = zLongKey;
3282
3293
  var zAgentDefinitionResult = z.object({
@@ -3359,7 +3370,7 @@ var zAgentInstanceHistoryItemResult = z.object({
3359
3370
  agentInstanceKey: zAgentInstanceKey,
3360
3371
  elementInstanceKey: zElementInstanceKey,
3361
3372
  jobKey: zJobKey,
3362
- jobLease: zJobLeaseToken,
3373
+ jobLeaseToken: zJobLeaseToken,
3363
3374
  loopIteration: zLoopIterationId,
3364
3375
  role: zAgentInstanceHistoryRoleEnum,
3365
3376
  content: z.array(zAgentInstanceMessageContent).register(z.globalRegistry, {
@@ -5342,7 +5353,7 @@ var zSecretListResult = z.object({
5342
5353
  description: "The secret references, each of the form `camunda.secrets.<name>`."
5343
5354
  })
5344
5355
  }).register(z.globalRegistry, {
5345
- description: "The secret references the caller is authorized to see.\n\nUnbounded for now: the response carries the configured stores' full enumeration for the\nphysical tenant. Pagination is expected to land here before GA. This is an alpha endpoint,\nso that is not yet a breaking-contract concern.\n"
5356
+ description: "The secret references the caller is authorized to see.\n\nUnbounded: the response carries the configured stores' full enumeration for the physical\ntenant.\n"
5346
5357
  });
5347
5358
  var zSignalBroadcastRequest = z.object({
5348
5359
  signalName: z.string().register(z.globalRegistry, {
@@ -8028,6 +8039,7 @@ var zRestoreAsClusterAdminQuery = z.object({
8028
8039
  });
8029
8040
  var zRestoreAsClusterAdminResponse = zClusterRestoreResponse;
8030
8041
  var zGetClusterStatusResponse = zClusterStatusResponse;
8042
+ var zGetClusterUpgradeStatusResponse = zClusterUpgradeStatusResponse;
8031
8043
  var zGetClusterTopologyResponse = zClusterTopologyResponse;
8032
8044
  var zCreateUserBody = zUserRequest;
8033
8045
  var zCreateUserResponse = zUserCreateResult;
@@ -8423,6 +8435,7 @@ export {
8423
8435
  zClusterTakeHistoryBackupResponse,
8424
8436
  zClusterTakeRuntimeBackupResponse,
8425
8437
  zClusterTopologyResponse,
8438
+ zClusterUpgradeStatusResponse,
8426
8439
  zClusterVariableKindEnum,
8427
8440
  zClusterVariableKindExactMatch,
8428
8441
  zClusterVariableKindExactMatchWritable,
@@ -8693,6 +8706,7 @@ export {
8693
8706
  zGetClusterRebalanceResponse,
8694
8707
  zGetClusterStatusResponse,
8695
8708
  zGetClusterTopologyResponse,
8709
+ zGetClusterUpgradeStatusResponse,
8696
8710
  zGetDecisionDefinitionPath,
8697
8711
  zGetDecisionDefinitionResponse,
8698
8712
  zGetDecisionDefinitionXmlPath,
@@ -9506,4 +9520,4 @@ export {
9506
9520
  zWaitStateTypeFilterProperty,
9507
9521
  zWebappComponent
9508
9522
  };
9509
- //# sourceMappingURL=zod.gen-A3RZUVR4.js.map
9523
+ //# sourceMappingURL=zod.gen-YV3IADCI.js.map