@camunda8/orchestration-cluster-api 10.0.0-alpha.4 → 10.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +310 -0
- package/README.md +400 -68
- package/dist/{index-CtFmBFXM.d.ts → CamundaClient-BlKXG8gE.d.ts} +20596 -11224
- package/dist/{index-CvA10E3U.d.cts → CamundaClient-HroU9acH.d.cts} +20596 -11224
- package/dist/{chunk-YMG6EGPW.js → chunk-267HUCFY.js} +12168 -7407
- package/dist/chunk-267HUCFY.js.map +1 -0
- package/dist/{fp → effect}/index.cjs +18661 -12465
- package/dist/effect/index.cjs.map +1 -0
- package/dist/effect/index.d.cts +302 -0
- package/dist/effect/index.d.ts +302 -0
- package/dist/effect/index.js +367 -0
- package/dist/effect/index.js.map +1 -0
- package/dist/index.cjs +18698 -12458
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -6
- package/dist/index.d.ts +76 -6
- package/dist/index.js +316 -15
- package/dist/index.js.map +1 -1
- package/dist/threadWorkerEntry.cjs +9 -3
- package/dist/threadWorkerEntry.cjs.map +1 -1
- package/dist/threadWorkerEntry.js +9 -3
- package/dist/threadWorkerEntry.js.map +1 -1
- package/dist/zod.gen-2PU225MP.js +9475 -0
- package/dist/zod.gen-2PU225MP.js.map +1 -0
- package/package.json +44 -26
- package/dist/chunk-YMG6EGPW.js.map +0 -1
- package/dist/fp/index.cjs.map +0 -1
- package/dist/fp/index.d.cts +0 -4
- package/dist/fp/index.d.ts +0 -4
- package/dist/fp/index.js +0 -23
- package/dist/fp/index.js.map +0 -1
- package/dist/zod.gen-WZT74U4Q.js +0 -8387
- package/dist/zod.gen-WZT74U4Q.js.map +0 -1
|
@@ -35,6 +35,14 @@ function createClientProxy(port) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// src/runtime/moduleSpecifier.ts
|
|
39
|
+
var import_node_path = require("path");
|
|
40
|
+
var import_node_url = require("url");
|
|
41
|
+
function toModuleSpecifier(handlerModule) {
|
|
42
|
+
const isPath = handlerModule.startsWith(".") || handlerModule.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(handlerModule);
|
|
43
|
+
return isPath ? (0, import_node_url.pathToFileURL)((0, import_node_path.resolve)(handlerModule)).href : handlerModule;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
// src/runtime/threadWorkerEntry.ts
|
|
39
47
|
var JobActionReceipt = "JOB_ACTION_RECEIPT";
|
|
40
48
|
if (!import_node_worker_threads.parentPort) {
|
|
@@ -44,9 +52,7 @@ var handlerCache = /* @__PURE__ */ new Map();
|
|
|
44
52
|
async function loadHandler(handlerModule) {
|
|
45
53
|
const cached = handlerCache.get(handlerModule);
|
|
46
54
|
if (cached) return cached;
|
|
47
|
-
const
|
|
48
|
-
const modulePath = isPath ? new URL(handlerModule, `file://${process.cwd()}/`).href : handlerModule;
|
|
49
|
-
const mod = await import(modulePath);
|
|
55
|
+
const mod = await import(toModuleSpecifier(handlerModule));
|
|
50
56
|
const handler = mod.default ?? mod.handler;
|
|
51
57
|
if (typeof handler !== "function") {
|
|
52
58
|
throw new Error(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.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 type { JobResult as ApiJobResult } from '../gen/types.gen';\nimport { createClientProxy } from './clientProxy.ts';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency chain\n// (jobWorker.ts → ../gen/CamundaClient → entire SDK) in the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\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 // Convert filesystem paths to file:// URLs for dynamic import().\n // Handles relative (./ ../) paths, Unix absolute (/), and Windows absolute (C:\\).\n const isPath =\n handlerModule.startsWith('.') ||\n handlerModule.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(handlerModule);\n const modulePath = isPath\n ? new URL(handlerModule, `file://${process.cwd()}/`).href\n : handlerModule;\n const mod = await import(modulePath);\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/**\n * Build a job object with action methods that proxy through the client.\n * The job data is plain serialized data from the main thread.\n * Action methods (complete, fail, error, etc.) call back to the main thread client.\n */\nfunction 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: [{ variables, jobKey: jobData.jobKey, ...(result !== undefined && { result }) }],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [{ ...reason, jobKey: jobData.jobKey }],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [{ ...error, jobKey: jobData.jobKey }],\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({ changeset: { timeout: newTimeoutMs }, jobKey: jobData.jobKey });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({ changeset: { retries }, jobKey: jobData.jobKey });\n\n return job;\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 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"],"mappings":"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","names":["result"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.ts","../src/runtime/moduleSpecifier.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 type { JobResult as ApiJobResult } from '../gen/types.gen';\nimport { createClientProxy } from './clientProxy.ts';\nimport { toModuleSpecifier } from './moduleSpecifier.ts';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency chain\n// (jobWorker.ts → ../gen/CamundaClient → entire SDK) in the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\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/**\n * Build a job object with action methods that proxy through the client.\n * The job data is plain serialized data from the main thread.\n * Action methods (complete, fail, error, etc.) call back to the main thread client.\n */\nfunction 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: [{ variables, jobKey: jobData.jobKey, ...(result !== undefined && { result }) }],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [{ ...reason, jobKey: jobData.jobKey }],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [{ ...error, jobKey: jobData.jobKey }],\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({ changeset: { timeout: newTimeoutMs }, jobKey: jobData.jobKey });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({ changeset: { retries }, jobKey: jobData.jobKey });\n\n return job;\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 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"],"mappings":"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","names":["resolvePath","result"]}
|
|
@@ -33,6 +33,14 @@ function createClientProxy(port) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// src/runtime/moduleSpecifier.ts
|
|
37
|
+
import { resolve as resolvePath } from "path";
|
|
38
|
+
import { pathToFileURL } from "url";
|
|
39
|
+
function toModuleSpecifier(handlerModule) {
|
|
40
|
+
const isPath = handlerModule.startsWith(".") || handlerModule.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(handlerModule);
|
|
41
|
+
return isPath ? pathToFileURL(resolvePath(handlerModule)).href : handlerModule;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
// src/runtime/threadWorkerEntry.ts
|
|
37
45
|
var JobActionReceipt = "JOB_ACTION_RECEIPT";
|
|
38
46
|
if (!parentPort) {
|
|
@@ -42,9 +50,7 @@ var handlerCache = /* @__PURE__ */ new Map();
|
|
|
42
50
|
async function loadHandler(handlerModule) {
|
|
43
51
|
const cached = handlerCache.get(handlerModule);
|
|
44
52
|
if (cached) return cached;
|
|
45
|
-
const
|
|
46
|
-
const modulePath = isPath ? new URL(handlerModule, `file://${process.cwd()}/`).href : handlerModule;
|
|
47
|
-
const mod = await import(modulePath);
|
|
53
|
+
const mod = await import(toModuleSpecifier(handlerModule));
|
|
48
54
|
const handler = mod.default ?? mod.handler;
|
|
49
55
|
if (typeof handler !== "function") {
|
|
50
56
|
throw new Error(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.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 type { JobResult as ApiJobResult } from '../gen/types.gen';\nimport { createClientProxy } from './clientProxy.ts';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency chain\n// (jobWorker.ts → ../gen/CamundaClient → entire SDK) in the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\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 // Convert filesystem paths to file:// URLs for dynamic import().\n // Handles relative (./ ../) paths, Unix absolute (/), and Windows absolute (C:\\).\n const isPath =\n handlerModule.startsWith('.') ||\n handlerModule.startsWith('/') ||\n /^[a-zA-Z]:[\\\\/]/.test(handlerModule);\n const modulePath = isPath\n ? new URL(handlerModule, `file://${process.cwd()}/`).href\n : handlerModule;\n const mod = await import(modulePath);\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/**\n * Build a job object with action methods that proxy through the client.\n * The job data is plain serialized data from the main thread.\n * Action methods (complete, fail, error, etc.) call back to the main thread client.\n */\nfunction 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: [{ variables, jobKey: jobData.jobKey, ...(result !== undefined && { result }) }],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [{ ...reason, jobKey: jobData.jobKey }],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [{ ...error, jobKey: jobData.jobKey }],\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({ changeset: { timeout: newTimeoutMs }, jobKey: jobData.jobKey });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({ changeset: { retries }, jobKey: jobData.jobKey });\n\n return job;\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 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"],"mappings":"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","names":["result"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/threadWorkerEntry.ts","../src/runtime/clientProxy.ts","../src/runtime/moduleSpecifier.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 type { JobResult as ApiJobResult } from '../gen/types.gen';\nimport { createClientProxy } from './clientProxy.ts';\nimport { toModuleSpecifier } from './moduleSpecifier.ts';\n\n// Inline the JobActionReceipt constant to avoid importing the full SDK dependency chain\n// (jobWorker.ts → ../gen/CamundaClient → entire SDK) in the worker thread.\nconst JobActionReceipt = 'JOB_ACTION_RECEIPT' as const;\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/**\n * Build a job object with action methods that proxy through the client.\n * The job data is plain serialized data from the main thread.\n * Action methods (complete, fail, error, etc.) call back to the main thread client.\n */\nfunction 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: [{ variables, jobKey: jobData.jobKey, ...(result !== undefined && { result }) }],\n };\n return JobActionReceipt;\n };\n\n job.fail = async (reason: any) => {\n ack();\n job._completionAction = {\n method: 'failJob',\n args: [{ ...reason, jobKey: jobData.jobKey }],\n };\n return JobActionReceipt;\n };\n\n job.error = async (error: any) => {\n ack();\n job._completionAction = {\n method: 'throwJobError',\n args: [{ ...error, jobKey: jobData.jobKey }],\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({ changeset: { timeout: newTimeoutMs }, jobKey: jobData.jobKey });\n\n job.modifyRetries = ({ retries }: { retries: number }) =>\n client.updateJob({ changeset: { retries }, jobKey: jobData.jobKey });\n\n return job;\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 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"],"mappings":"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","names":["result"]}
|