UNPKG

@temporalio/worker

Version:
141 lines 6.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const node_worker_threads_1 = require("node:worker_threads"); const common_1 = require("@temporalio/common"); const proto_1 = require("@temporalio/proto"); const reusable_vm_1 = require("./reusable-vm"); const vm_1 = require("./vm"); const bun_1 = require("./bun"); if (node_worker_threads_1.isMainThread) { throw new common_1.IllegalStateError(`Imported ${__filename} from main thread`); } if (node_worker_threads_1.parentPort === null) { throw new TypeError(`${__filename} got a null parentPort`); } // Create a new parentPort variable that is not nullable to please TS const parentPort = node_worker_threads_1.parentPort; function ok(requestId) { return { requestId, result: { type: 'ok' } }; } let workflowCreator; let workflowGetter; /** * Process a `WorkerThreadRequest` and resolve with a `WorkerThreadResponse`. */ async function handleRequest({ requestId, input }) { switch (input.type) { case 'init': if (input.reuseV8Context) { workflowCreator = await reusable_vm_1.ReusableVMWorkflowCreator.create(input.workflowBundle, input.isolateExecutionTimeoutMs, input.registeredActivityNames); workflowGetter = (runId) => reusable_vm_1.ReusableVMWorkflowCreator.workflowByRunId.get(runId); } else { workflowCreator = await vm_1.VMWorkflowCreator.create(input.workflowBundle, input.isolateExecutionTimeoutMs, input.registeredActivityNames); workflowGetter = (runId) => vm_1.VMWorkflowCreator.workflowByRunId.get(runId); } return ok(requestId); case 'destroy': await workflowCreator?.destroy(); return ok(requestId); case 'create-workflow': { if (workflowCreator === undefined) { throw new common_1.IllegalStateError('No WorkflowCreator in Worker thread'); } await workflowCreator.createWorkflow(input.options); return ok(requestId); } case 'activate-workflow': { const workflow = workflowGetter(input.runId); if (workflow === undefined) { throw new common_1.IllegalStateError(`Tried to activate non running workflow with runId: ${input.runId}`); } let activation; if (input.activation instanceof Uint8Array) { // Some activation messages get silently dropped by Bun's postMessage. // To work around this bug, we encode activations activation = proto_1.coresdk.workflow_activation.WorkflowActivation.decode(input.activation); } else { activation = proto_1.coresdk.workflow_activation.WorkflowActivation.fromObject(input.activation); } const completion = await workflow.activate(activation); const maybeEncodedCompletion = bun_1.isBun ? proto_1.coresdk.workflow_completion.WorkflowActivationCompletion.encode(completion).finish() : completion; return { requestId, result: { type: 'ok', output: { type: 'activation-completion', completion: maybeEncodedCompletion, }, }, }; } case 'extract-sink-calls': { const workflow = workflowGetter(input.runId); if (workflow === undefined) { throw new common_1.IllegalStateError(`Tried to activate non running workflow with runId: ${input.runId}`); } const calls = await workflow.getAndResetSinkCalls(); calls.forEach((call) => { // Delete .now because functions can't be serialized / sent to thread. delete call.workflowInfo.unsafe.now; // Use structuredClone when available // This lets us work around a bug in Bun's postMessage where // shared object references get corrupted during serialization. call.workflowInfo = 'structuredClone' in globalThis ? structuredClone(call.workflowInfo) : { ...call.workflowInfo, unsafe: { ...call.workflowInfo.unsafe } }; }); return { requestId, result: { type: 'ok', output: { type: 'sink-calls', calls } }, }; } case 'dispose-workflow': { const workflow = workflowGetter(input.runId); if (workflow === undefined) { throw new common_1.IllegalStateError(`Tried to dispose non running workflow with runId: ${input.runId}`); } await workflow.dispose(); return ok(requestId); } } } /** * Transfer a response to the parent thread with zero-copy semantics when possible. * * For Bun, we use structuredClone with transfer option because Bun's postMessage * doesn't properly detach buffers when transferring from worker to main thread. * See: https://github.com/oven-sh/bun/issues/18705 */ function postResponse(response) { const completion = response.result.type === 'ok' ? response.result.output : undefined; if (bun_1.isBun && completion?.type === 'activation-completion' && completion.completion instanceof Uint8Array) { const buffer = completion.completion.buffer; const cloned = structuredClone(response, { transfer: [buffer] }); parentPort.postMessage(cloned); } else { parentPort.postMessage(response); } } /** * Listen on messages delivered from the parent thread (the SDK Worker), * process any requests and respond back with result or error. */ parentPort.on('message', async (request) => { try { postResponse(await handleRequest(request)); } catch (err) { parentPort.postMessage({ requestId: request.requestId, result: { type: 'error', message: err.message, name: err.name, stack: err.stack }, }); } }); //# sourceMappingURL=workflow-worker-thread.js.map