@outputai/core 0.10.0 → 0.10.1-next.09ed166.0

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/bus.js +58 -20
  3. package/src/bus.spec.js +135 -77
  4. package/src/consts.js +2 -1
  5. package/src/helpers/object.js +48 -35
  6. package/src/helpers/object.spec.js +123 -75
  7. package/src/hooks/index.d.ts +48 -45
  8. package/src/hooks/index.js +49 -44
  9. package/src/hooks/index.spec.js +74 -102
  10. package/src/interface/workflow.js +92 -85
  11. package/src/interface/workflow.spec.js +135 -142
  12. package/src/sdk/helpers/objects.d.ts +24 -19
  13. package/src/sdk/runtime/events.d.ts +4 -6
  14. package/src/sdk/runtime/events.js +2 -15
  15. package/src/sdk/runtime/events.spec.js +14 -92
  16. package/src/worker/global_functions.js +2 -2
  17. package/src/worker/global_functions.spec.js +3 -3
  18. package/src/worker/index.js +3 -3
  19. package/src/worker/index.spec.js +6 -6
  20. package/src/worker/interceptors/activity.js +4 -4
  21. package/src/worker/interceptors/activity.spec.js +9 -9
  22. package/src/worker/interceptors/workflow.js +5 -5
  23. package/src/worker/interceptors/workflow.spec.js +7 -7
  24. package/src/worker/loader/activities.js +56 -40
  25. package/src/worker/loader/activities.spec.js +21 -22
  26. package/src/worker/loader/workflows.spec.js +1 -2
  27. package/src/worker/log_hooks.js +9 -9
  28. package/src/worker/log_hooks.spec.js +2 -2
  29. package/src/worker/sinks.js +8 -8
  30. package/src/worker/sinks.spec.js +9 -9
  31. package/src/worker/webpack_loaders/consts.js +6 -0
  32. package/src/worker/webpack_loaders/tools.js +1 -146
  33. package/src/worker/webpack_loaders/tools.spec.js +0 -23
  34. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +24 -32
  35. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +82 -279
  36. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +6 -7
  37. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +57 -119
  38. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.js +88 -0
  39. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +165 -0
  40. package/src/worker/webpack_loaders/workflow_validator/index.mjs +96 -29
  41. package/src/worker/webpack_loaders/workflow_validator/index.spec.js +110 -583
  42. package/src/worker/webpack_loaders/{npm_workflow_export_resolve.js → workflow_validator/npm_workflow_export_resolve.js} +1 -1
  43. package/src/worker/webpack_loaders/{npm_workflow_export_resolve.spec.js → workflow_validator/npm_workflow_export_resolve.spec.js} +1 -1
  44. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +0 -193
  45. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +0 -123
@@ -1,22 +1,33 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { BusEventType, ComponentType, WORKFLOW_CATALOG } from '#consts';
2
+ import { BusEventType } from '#consts';
3
3
 
4
4
  const logErrorMock = vi.hoisted( () => vi.fn() );
5
5
  const createChildLoggerMock = vi.hoisted( () =>
6
6
  vi.fn( () => ( { error: logErrorMock } ) )
7
7
  );
8
8
 
9
- const onHandlers = vi.hoisted( () => ( {} ) );
10
- const messageBusMock = vi.hoisted( () => ( {
9
+ const mainOnHandlers = vi.hoisted( () => ( {} ) );
10
+ const stepOnHandlers = vi.hoisted( () => ( {} ) );
11
+ const mainEventBusMock = vi.hoisted( () => ( {
11
12
  on: vi.fn( ( eventType, handler ) => {
12
- onHandlers[eventType] = handler;
13
+ mainOnHandlers[eventType] = handler;
14
+ } )
15
+ } ) );
16
+ const stepEventBusMock = vi.hoisted( () => ( {
17
+ emit: vi.fn( () => true ),
18
+ on: vi.fn( ( eventType, handler ) => {
19
+ stepOnHandlers[eventType] = handler;
13
20
  } )
14
21
  } ) );
15
22
 
16
23
  vi.mock( '#logger', () => ( { createChildLogger: createChildLoggerMock } ) );
17
- vi.mock( '#bus', () => ( { messageBus: messageBusMock } ) );
24
+ vi.mock( '#bus', () => ( {
25
+ mainEventBus: mainEventBusMock,
26
+ stepEventBus: stepEventBusMock
27
+ } ) );
18
28
 
19
29
  import {
30
+ emit,
20
31
  on,
21
32
  onActivityEnd,
22
33
  onActivityError,
@@ -38,11 +49,6 @@ const workflowDetails = {
38
49
  attempt: 1
39
50
  };
40
51
 
41
- const catalogWorkflowDetails = {
42
- ...workflowDetails,
43
- workflowType: WORKFLOW_CATALOG
44
- };
45
-
46
52
  const activityInfo = {
47
53
  activityId: 'act-1',
48
54
  activityType: 'wf#step',
@@ -61,8 +67,11 @@ const aggregations = {
61
67
  describe( 'hooks/index', () => {
62
68
  beforeEach( () => {
63
69
  vi.clearAllMocks();
64
- Object.keys( onHandlers ).forEach( k => {
65
- delete onHandlers[k];
70
+ Object.keys( mainOnHandlers ).forEach( k => {
71
+ delete mainOnHandlers[k];
72
+ } );
73
+ Object.keys( stepOnHandlers ).forEach( k => {
74
+ delete stepOnHandlers[k];
66
75
  } );
67
76
  } );
68
77
 
@@ -71,9 +80,9 @@ describe( 'hooks/index', () => {
71
80
  const handler = vi.fn().mockResolvedValue( undefined );
72
81
  onError( handler );
73
82
 
74
- expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, expect.any( Function ) );
75
- expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.WORKFLOW_ERROR, expect.any( Function ) );
76
- expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.RUNTIME_ERROR, expect.any( Function ) );
83
+ expect( mainEventBusMock.on ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, expect.any( Function ) );
84
+ expect( mainEventBusMock.on ).toHaveBeenCalledWith( BusEventType.WORKFLOW_ERROR, expect.any( Function ) );
85
+ expect( mainEventBusMock.on ).toHaveBeenCalledWith( BusEventType.RUNTIME_ERROR, expect.any( Function ) );
77
86
  } );
78
87
 
79
88
  it( 'invokes handler with activity-shaped payload, forwarding bus fields', async () => {
@@ -81,7 +90,7 @@ describe( 'hooks/index', () => {
81
90
  onError( handler );
82
91
 
83
92
  const err = new Error( 'act-fail' );
84
- await onHandlers[BusEventType.ACTIVITY_ERROR]( {
93
+ await mainOnHandlers[BusEventType.ACTIVITY_ERROR]( {
85
94
  eventId: 'evt-act-1',
86
95
  eventDate,
87
96
  activityInfo,
@@ -108,7 +117,7 @@ describe( 'hooks/index', () => {
108
117
  onError( handler );
109
118
 
110
119
  const err = new Error( 'wf-fail' );
111
- await onHandlers[BusEventType.WORKFLOW_ERROR]( {
120
+ await mainOnHandlers[BusEventType.WORKFLOW_ERROR]( {
112
121
  eventId: 'evt-wf-1',
113
122
  eventDate,
114
123
  workflowDetails,
@@ -131,7 +140,7 @@ describe( 'hooks/index', () => {
131
140
  onError( handler );
132
141
 
133
142
  const error = new Error( 'rt' );
134
- await onHandlers[BusEventType.RUNTIME_ERROR]( { eventId: 'evt-rt-1', eventDate, error } );
143
+ await mainOnHandlers[BusEventType.RUNTIME_ERROR]( { eventId: 'evt-rt-1', eventDate, error } );
135
144
 
136
145
  expect( handler ).toHaveBeenCalledWith( { eventId: 'evt-rt-1', eventDate, source: 'runtime', error } );
137
146
  } );
@@ -142,68 +151,35 @@ describe( 'hooks/index', () => {
142
151
  const handler = vi.fn().mockResolvedValue( undefined );
143
152
  onBeforeWorkerStart( handler );
144
153
 
145
- expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.WORKER_BEFORE_START, expect.any( Function ) );
146
- await onHandlers[BusEventType.WORKER_BEFORE_START]();
154
+ expect( mainEventBusMock.on ).toHaveBeenCalledWith( BusEventType.WORKER_BEFORE_START, expect.any( Function ) );
155
+ await mainOnHandlers[BusEventType.WORKER_BEFORE_START]();
147
156
 
148
157
  expect( handler ).toHaveBeenCalledWith( undefined );
149
158
  } );
150
159
  } );
151
160
 
152
- describe( 'onWorkflowStart', () => {
153
- it( 'skips catalog workflow and forwards bus fields for real workflows', async () => {
154
- const handler = vi.fn().mockResolvedValue( undefined );
155
- onWorkflowStart( handler );
156
-
157
- await Promise.resolve( onHandlers[BusEventType.WORKFLOW_START]( {
158
- eventId: 'evt-ignored', eventDate, workflowDetails: catalogWorkflowDetails
159
- } ) );
160
- expect( handler ).not.toHaveBeenCalled();
161
-
162
- await Promise.resolve( onHandlers[BusEventType.WORKFLOW_START]( {
163
- eventId: 'evt-start-1', eventDate, workflowDetails, extra: 'passthrough'
164
- } ) );
165
- expect( handler ).toHaveBeenCalledWith( {
166
- eventId: 'evt-start-1', eventDate, workflowDetails, extra: 'passthrough'
167
- } );
168
- } );
169
- } );
170
-
171
- describe( 'onWorkflowEnd', () => {
172
- it( 'skips catalog workflow and forwards bus fields for real workflows', async () => {
173
- const handler = vi.fn().mockResolvedValue( undefined );
174
- onWorkflowEnd( handler );
175
-
176
- await Promise.resolve( onHandlers[BusEventType.WORKFLOW_END]( {
177
- eventId: 'evt-ignored', eventDate, workflowDetails: catalogWorkflowDetails
178
- } ) );
179
- expect( handler ).not.toHaveBeenCalled();
180
-
181
- await Promise.resolve( onHandlers[BusEventType.WORKFLOW_END]( {
182
- eventId: 'evt-end-1', eventDate, workflowDetails, extra: 'passthrough'
183
- } ) );
184
- expect( handler ).toHaveBeenCalledWith( {
185
- eventId: 'evt-end-1', eventDate, workflowDetails, extra: 'passthrough'
186
- } );
187
- } );
188
- } );
161
+ describe( 'workflow lifecycle hooks', () => {
162
+ const cases = [
163
+ [ 'onWorkflowStart', onWorkflowStart, BusEventType.WORKFLOW_START, {} ],
164
+ [ 'onWorkflowEnd', onWorkflowEnd, BusEventType.WORKFLOW_END, {} ],
165
+ [ 'onWorkflowError', onWorkflowError, BusEventType.WORKFLOW_ERROR, { error: new Error( 'workflow failed' ) } ]
166
+ ];
189
167
 
190
- describe( 'onWorkflowError', () => {
191
- it( 'skips catalog workflow and forwards bus fields for real workflows', async () => {
168
+ it.each( cases )( '%s forwards bus fields', async ( _name, registerHook, eventType, extraFields ) => {
192
169
  const handler = vi.fn().mockResolvedValue( undefined );
193
- const err = new Error( 'wf' );
194
- onWorkflowError( handler );
170
+ const payload = {
171
+ eventId: 'evt-workflow-1',
172
+ eventDate,
173
+ workflowDetails,
174
+ extra: 'passthrough',
175
+ ...extraFields
176
+ };
177
+ registerHook( handler );
195
178
 
196
- await Promise.resolve( onHandlers[BusEventType.WORKFLOW_ERROR]( {
197
- eventId: 'evt-ignored', eventDate, workflowDetails: catalogWorkflowDetails, error: err
198
- } ) );
199
- expect( handler ).not.toHaveBeenCalled();
179
+ expect( mainEventBusMock.on ).toHaveBeenCalledWith( eventType, expect.any( Function ) );
180
+ await mainOnHandlers[eventType]( payload );
200
181
 
201
- await Promise.resolve( onHandlers[BusEventType.WORKFLOW_ERROR]( {
202
- eventId: 'evt-err-1', eventDate, workflowDetails, error: err, extra: 'passthrough'
203
- } ) );
204
- expect( handler ).toHaveBeenCalledWith( {
205
- eventId: 'evt-err-1', eventDate, workflowDetails, error: err, extra: 'passthrough'
206
- } );
182
+ expect( handler ).toHaveBeenCalledWith( payload );
207
183
  } );
208
184
  } );
209
185
 
@@ -214,53 +190,49 @@ describe( 'hooks/index', () => {
214
190
  [ 'onActivityError', onActivityError, BusEventType.ACTIVITY_ERROR, { error: new Error( 'activity failed' ) } ]
215
191
  ];
216
192
 
217
- it.each( cases )( '%s skips internal activities and forwards bus fields', async ( _name, registerHook, eventType, extraFields = {} ) => {
193
+ it.each( cases )( '%s forwards internal activity bus fields', async ( _name, registerHook, eventType, extraFields = {} ) => {
218
194
  const handler = vi.fn().mockResolvedValue( undefined );
219
- registerHook( handler );
220
-
221
- expect( messageBusMock.on ).toHaveBeenCalledWith( eventType, expect.any( Function ) );
222
-
223
- await Promise.resolve( onHandlers[eventType]( {
224
- eventId: 'evt-ignored',
225
- eventDate,
226
- activityInfo,
227
- workflowDetails,
228
- outputActivityKind: ComponentType.INTERNAL_STEP,
229
- ...extraFields
230
- } ) );
231
- expect( handler ).not.toHaveBeenCalled();
232
-
233
- await Promise.resolve( onHandlers[eventType]( {
195
+ const payload = {
234
196
  eventId: 'evt-activity-1',
235
197
  eventDate,
236
198
  activityInfo,
237
199
  workflowDetails,
238
- outputActivityKind: ComponentType.STEP,
200
+ outputActivityKind: 'internal_step',
239
201
  extra: 'passthrough',
240
202
  ...extraFields
241
- } ) );
203
+ };
204
+ registerHook( handler );
242
205
 
243
- expect( handler ).toHaveBeenCalledWith( {
244
- eventId: 'evt-activity-1',
245
- eventDate,
246
- activityInfo,
247
- workflowDetails,
248
- outputActivityKind: ComponentType.STEP,
249
- extra: 'passthrough',
250
- ...extraFields
251
- } );
206
+ expect( mainEventBusMock.on ).toHaveBeenCalledWith( eventType, expect.any( Function ) );
207
+ await mainOnHandlers[eventType]( payload );
208
+
209
+ expect( handler ).toHaveBeenCalledWith( payload );
252
210
  } );
253
211
  } );
254
212
 
255
213
  describe( 'on', () => {
256
- it( 'subscribes to external event channel and forwards payload', async () => {
214
+ it( 'subscribes to SDK and user event channels and forwards payloads', async () => {
257
215
  const handler = vi.fn().mockResolvedValue( undefined );
258
216
  on( 'myEvent', handler );
259
217
 
260
- expect( messageBusMock.on ).toHaveBeenCalledWith( 'external:myEvent', expect.any( Function ) );
261
- await onHandlers['external:myEvent']( { foo: 1 } );
218
+ expect( stepEventBusMock.on ).toHaveBeenCalledWith( 'sdk:myEvent', expect.any( Function ) );
219
+ expect( stepEventBusMock.on ).toHaveBeenCalledWith( 'usr:myEvent', expect.any( Function ) );
220
+ await stepOnHandlers['sdk:myEvent']( { payload: { source: 'sdk' } } );
221
+ await stepOnHandlers['usr:myEvent']( { payload: { source: 'user' } } );
222
+
223
+ expect( handler ).toHaveBeenNthCalledWith( 1, { payload: { source: 'sdk' } } );
224
+ expect( handler ).toHaveBeenNthCalledWith( 2, { payload: { source: 'user' } } );
225
+ } );
226
+ } );
227
+
228
+ describe( 'emit', () => {
229
+ it( 'emits payloads on the user event channel', () => {
230
+ const payload = { foo: 1 };
231
+
232
+ const emitted = emit( 'myEvent', payload );
262
233
 
263
- expect( handler ).toHaveBeenCalledWith( { foo: 1 } );
234
+ expect( stepEventBusMock.emit ).toHaveBeenCalledWith( 'usr:myEvent', payload );
235
+ expect( emitted ).toBe( true );
264
236
  } );
265
237
  } );
266
238
  } );
@@ -7,104 +7,111 @@ import { TraceInfo } from '#helpers/trace_info';
7
7
  import { deepMerge } from '#helpers/object';
8
8
  import { defaultOptions } from './workflow_activity_options.js';
9
9
  import { createWorkflow } from '#helpers/component';
10
- import {
11
- ACTIVITY_GET_TRACE_DESTINATIONS,
12
- METADATA_ACCESS_SYMBOL,
13
- SHARED_STEP_PREFIX,
14
- WORKFLOW_WRAPPER_VERSION_FIELD
15
- } from '#consts';
10
+ import { FatalError } from '#errors';
11
+ import * as C from '#consts';
16
12
 
17
13
  /**
18
- * Create a new workflow and return a wrapper function around its fn handler
14
+ * Execute the workflow without temporal, using the fn handler function.
15
+ * This is important to allow for workflows to have unit tests
19
16
  */
17
+ const executeWithoutTemporal = async ( { input, validator, handler, contextOverrides = {} } ) => {
18
+ validator.validateInput( input );
19
+ const output = await handler( input, deepMerge( WorkflowContext.build(), contextOverrides ) );
20
+ validator.validateOutput( output );
21
+ return output;
22
+ };
23
+
24
+ /**
25
+ * Add a global dispatcher function to be used to invoke activities.
26
+ * This will replace direct activity invocation in the user code by the webpack loader.
27
+ *
28
+ * Important: Keep this as a configurable global assignment (configurable=true, enumerable=true),
29
+ * so Temporal's reusable VM can delete it when switching workflow scopes.
30
+ */
31
+ const createGlobalDispatcher = ( { runId, workflowType, activities } ) => {
32
+ const dispatcher = async ( activityType, ...args ) => activities[`${workflowType}#${activityType}`]( ...args ).then( r => r.output );
33
+ dispatcher.runId = runId;
34
+ globalThis[C.INVOKE_ACTIVITY_SYMBOL] = dispatcher;
35
+ };
36
+
37
+ /** Validate if the global dispatcher wasn't set by another workflow, indicating global context contamination. */
38
+ const checkGlobalContextContamination = runId => {
39
+ const globalContextRunId = globalThis?.[C.INVOKE_ACTIVITY_SYMBOL]?.runId;
40
+ if ( globalContextRunId && globalContextRunId !== runId ) {
41
+ throw new FatalError( 'Contamination of the workflow Node global context.' +
42
+ ` Var "globalThis[${String( C.INVOKE_ACTIVITY_SYMBOL )}]" was set by another workflow (${globalContextRunId})` );
43
+ }
44
+ };
45
+
46
+ /** Create a new workflow and return a wrapper function around its fn handler */
20
47
  export function workflow( { name, description, inputSchema, outputSchema, fn, options = {}, aliases = [] } ) {
21
48
  WorkflowValidator.validateDefinition( { name, description, inputSchema, outputSchema, fn, options, aliases } );
22
49
 
23
- const { disableTrace, activityOptions } = deepMerge( defaultOptions, options );
50
+ // Disable trace can only be defined at the definition level
51
+ const disableTrace = options.disableTrace ?? defaultOptions.disableTrace;
24
52
  const validator = new WorkflowValidator( { name, inputSchema, outputSchema } );
25
53
 
26
- return createWorkflow( {
27
- name,
28
- description,
29
- inputSchema,
30
- outputSchema,
31
- options,
32
- aliases,
33
- handler: async ( input, extra = {} ) => {
34
- validator.validateInvocationOptions( extra );
35
-
36
- // this returns a plain function, for example, in unit tests
37
- if ( !inWorkflowContext() ) {
38
- validator.validateInput( input );
39
- const output = await fn( input, deepMerge( WorkflowContext.build(), extra?.context ) );
40
- validator.validateOutput( output );
41
- return output;
42
- }
54
+ const handler = async ( input, invocationOptions = {} ) => {
55
+ validator.validateInvocationOptions( invocationOptions );
43
56
 
44
- const { workflowId, memo, root } = workflowInfo();
45
-
46
- // if the stack already includes this workflowId, means the workflow() function was called
47
- // from within a running workflow, meaning it is suppose to start a child workflow
48
- const isChild = Array.isArray( memo.stack ) ? memo.stack.includes( workflowId ) : false;
49
-
50
- if ( isChild ) {
51
- const result = await executeChild( name, {
52
- args: undefined === input ? [] : [ input ],
53
- workflowId: `${workflowId}-${toUrlSafeBase64( uuid4() )}`,
54
- parentClosePolicy: ParentClosePolicy[extra?.detached ? 'ABANDON' : 'TERMINATE'],
55
- memo: {
56
- ...memo, // Preserve memo and mix activityOptions, if provided
57
- ...( extra?.activityOptions && {
58
- activityOptions: deepMerge( memo?.activityOptions ?? {}, extra?.activityOptions )
59
- } )
60
- }
61
- } );
62
- return result.output;
63
- }
57
+ // If called outside Temporal workflow context, just execute the handler function
58
+ if ( !inWorkflowContext() ) {
59
+ return executeWithoutTemporal( { input, validator, handler: fn, contextOverrides: invocationOptions?.context } );
60
+ }
61
+
62
+ const { workflowId, runId, memo, root } = workflowInfo();
64
63
 
65
- const isRoot = !root;
64
+ checkGlobalContextContamination( runId );
66
65
 
67
- memo.stack = [ ...memo.stack ?? [], workflowId ];
68
- // Parent options have prevalence on nested calls, child will be overwritten
69
- memo.activityOptions = deepMerge( activityOptions, memo.activityOptions );
70
- // Trace info is only added in the root and only when trace is not disabled
71
- if ( isRoot && !disableTrace ) {
72
- memo.traceInfo = TraceInfo.build();
66
+ // If the parent workflow already installed the activity dispatcher, it means that calls to workflow() will trigger child workflows
67
+ const isChildWorkflowCall = !!globalThis[C.INVOKE_ACTIVITY_SYMBOL];
68
+ if ( isChildWorkflowCall ) {
69
+ const parentClosePolicy = ParentClosePolicy[invocationOptions?.detached ? 'ABANDON' : 'TERMINATE'];
70
+ const childWorkflowId = `${workflowId}-${toUrlSafeBase64( uuid4() )}`;
71
+ const args = [ input, { activityOptions: invocationOptions?.activityOptions } ];
72
+ return executeChild( name, { args, workflowId: childWorkflowId, parentClosePolicy, memo } ).then( r => r.output );
73
+ }
74
+
75
+ const isRoot = !root; // Check if this is the root most workflow
76
+
77
+ // Trace info is only added in the root and only when trace is not disabled
78
+ if ( isRoot && !disableTrace ) {
79
+ memo.traceInfo = TraceInfo.build();
80
+ }
81
+
82
+ // Resolve the activity options: invocation options > definition options > parent options > default options
83
+ const activityOptions = deepMerge(
84
+ defaultOptions.activityOptions, // default
85
+ memo?.parentActivityOptions, // parent options
86
+ options?.activityOptions, // definition options
87
+ invocationOptions.activityOptions // invocation options
88
+ );
89
+ // Resolved activity options are added to memo so child workflow executions can continue the policy chain.
90
+ memo.parentActivityOptions = activityOptions;
91
+ const activities = proxyActivities( activityOptions );
92
+
93
+ createGlobalDispatcher( { runId, workflowType: name, activities } );
94
+
95
+ const traceDestinations = isRoot && {
96
+ trace: {
97
+ destinations: disableTrace ? {} : await activities[C.ACTIVITY_GET_TRACE_DESTINATIONS]( memo.traceInfo ).then( r => r.output ) ?? {}
73
98
  }
99
+ };
74
100
 
75
- const steps = proxyActivities( memo.activityOptions );
76
- const traceDestinations = isRoot && {
77
- trace: {
78
- destinations: disableTrace ? {} : ( await steps[ACTIVITY_GET_TRACE_DESTINATIONS]( memo.traceInfo ) ).output ?? {}
79
- }
80
- };
81
-
82
- try {
83
- validator.validateInput( input );
84
-
85
- // Creates an activity caller based on a prefix
86
- const createCaller = prefix => async ( t, ...args ) => ( await steps[`${prefix}#${t}`]( ...args ) ).output;
87
-
88
- // This are functions used by the AST to replace direct activity (step/evaluator) calls
89
- const dispatchers = {
90
- invokeStep: createCaller( name ),
91
- invokeSharedStep: createCaller( SHARED_STEP_PREFIX ),
92
- invokeEvaluator: createCaller( name ),
93
- invokeSharedEvaluator: createCaller( SHARED_STEP_PREFIX )
94
- };
95
-
96
- // The workflow function execution with "this" set with the dispatchers
97
- const output = await fn.call( dispatchers, input, WorkflowContext.build() );
98
- validator.validateOutput( output );
99
-
100
- return { [WORKFLOW_WRAPPER_VERSION_FIELD]: 1, output, ...traceDestinations };
101
- } catch ( error ) {
102
- if ( traceDestinations ) {
103
- // Append the trace destinations so it is carried to interceptor
104
- error[METADATA_ACCESS_SYMBOL] = traceDestinations;
105
- }
106
- throw error;
101
+ try {
102
+ validator.validateInput( input );
103
+ const output = await fn( input, WorkflowContext.build() );
104
+ validator.validateOutput( output );
105
+
106
+ return { [C.WORKFLOW_WRAPPER_VERSION_FIELD]: 1, output, ...traceDestinations };
107
+ } catch ( error ) {
108
+ if ( traceDestinations ) {
109
+ // Append the trace destinations so it is carried to interceptor
110
+ error[C.METADATA_ACCESS_SYMBOL] = traceDestinations;
107
111
  }
112
+ throw error;
108
113
  }
109
- } );
114
+ };
115
+
116
+ return createWorkflow( { name, description, inputSchema, outputSchema, options, aliases, handler } );
110
117
  }