@temporalio/worker
Version:
Temporal.io SDK Worker sub-package
141 lines • 6.25 kB
JavaScript
;
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