@temporalio/workflow
Version:
Temporal.io SDK Workflow sub-package
109 lines (102 loc) • 3.46 kB
text/typescript
/**
* Type definitions for the Workflow end of the sinks mechanism.
*
* Sinks are a mechanism for exporting data from the Workflow isolate to the
* Node.js environment, they are necessary because the Workflow has no way to
* communicate with the outside World.
*
* Sinks are typically used for exporting logs, metrics and traces out from the
* Workflow.
*
* Sink functions may not return values to the Workflow in order to prevent
* breaking determinism.
*
* @module
*/
import { WorkflowInfo } from './interfaces';
import { assertInWorkflowContext } from './global-attributes';
/**
* Any function signature can be used for Sink functions as long as the return type is `void`.
*
* When calling a Sink function, arguments are copied from the Workflow isolate to the Node.js environment using
* {@link https://nodejs.org/api/worker_threads.html#worker_threads_port_postmessage_value_transferlist | postMessage}.
* This constrains the argument types to primitives (excluding Symbols).
*/
export type SinkFunction = (...args: any[]) => void;
/** A mapping of name to function, defines a single sink (e.g. logger) */
export type Sink = Record<string, SinkFunction>;
/**
* Workflow Sink are a mapping of name to {@link Sink}
*/
export type Sinks = Record<string, Sink>;
/**
* Call information for a Sink
*/
export interface SinkCall {
ifaceName: string;
fnName: string;
args: any[];
workflowInfo: WorkflowInfo;
}
/**
* Get a reference to Sinks for exporting data out of the Workflow.
*
* These Sinks **must** be registered with the Worker in order for this
* mechanism to work.
*
* @example
* ```ts
* import { proxySinks, Sinks } from '@temporalio/workflow';
*
* interface MySinks extends Sinks {
* logger: {
* info(message: string): void;
* error(message: string): void;
* };
* }
*
* const { logger } = proxySinks<MyDependencies>();
* logger.info('setting up');
*
* export function myWorkflow() {
* return {
* async execute() {
* logger.info("hey ho");
* logger.error("lets go");
* }
* };
* }
* ```
*/
export function proxySinks<T extends Sinks>(): T {
return new Proxy(
{},
{
get(_, ifaceName) {
return new Proxy(
{},
{
get(_, fnName) {
return (...args: any[]) => {
const activator = assertInWorkflowContext(
'Proxied sinks functions may only be used from a Workflow Execution.'
);
activator.sinkCalls.push({
ifaceName: ifaceName as string,
fnName: fnName as string,
// Sink function doesn't get called immediately. Make a clone of the sink's args, so that further mutations
// to these objects don't corrupt the args that the sink function will receive. Only available from node 17.
args: (globalThis as any).structuredClone ? (globalThis as any).structuredClone(args) : args,
// activator.info is internally copy-on-write. This ensure that any further mutations
// to the workflow state in the context of the present activation will not corrupt the
// workflowInfo state that gets passed when the sink function actually gets called.
workflowInfo: activator.info,
});
};
},
}
);
},
}
) as any;
}