@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.
- package/package.json +1 -1
- package/src/bus.js +58 -20
- package/src/bus.spec.js +135 -77
- package/src/consts.js +2 -1
- package/src/helpers/object.js +48 -35
- package/src/helpers/object.spec.js +123 -75
- package/src/hooks/index.d.ts +48 -45
- package/src/hooks/index.js +49 -44
- package/src/hooks/index.spec.js +74 -102
- package/src/interface/workflow.js +92 -85
- package/src/interface/workflow.spec.js +135 -142
- package/src/sdk/helpers/objects.d.ts +24 -19
- package/src/sdk/runtime/events.d.ts +4 -6
- package/src/sdk/runtime/events.js +2 -15
- package/src/sdk/runtime/events.spec.js +14 -92
- package/src/worker/global_functions.js +2 -2
- package/src/worker/global_functions.spec.js +3 -3
- package/src/worker/index.js +3 -3
- package/src/worker/index.spec.js +6 -6
- package/src/worker/interceptors/activity.js +4 -4
- package/src/worker/interceptors/activity.spec.js +9 -9
- package/src/worker/interceptors/workflow.js +5 -5
- package/src/worker/interceptors/workflow.spec.js +7 -7
- package/src/worker/loader/activities.js +56 -40
- package/src/worker/loader/activities.spec.js +21 -22
- package/src/worker/loader/workflows.spec.js +1 -2
- package/src/worker/log_hooks.js +9 -9
- package/src/worker/log_hooks.spec.js +2 -2
- package/src/worker/sinks.js +8 -8
- package/src/worker/sinks.spec.js +9 -9
- package/src/worker/webpack_loaders/consts.js +6 -0
- package/src/worker/webpack_loaders/tools.js +1 -146
- package/src/worker/webpack_loaders/tools.spec.js +0 -23
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +24 -32
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +82 -279
- package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +6 -7
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +57 -119
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.js +88 -0
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +165 -0
- package/src/worker/webpack_loaders/workflow_validator/index.mjs +96 -29
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +110 -583
- package/src/worker/webpack_loaders/{npm_workflow_export_resolve.js → workflow_validator/npm_workflow_export_resolve.js} +1 -1
- package/src/worker/webpack_loaders/{npm_workflow_export_resolve.spec.js → workflow_validator/npm_workflow_export_resolve.spec.js} +1 -1
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +0 -193
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +0 -123
package/src/hooks/index.spec.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { BusEventType
|
|
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
|
|
10
|
-
const
|
|
9
|
+
const mainOnHandlers = vi.hoisted( () => ( {} ) );
|
|
10
|
+
const stepOnHandlers = vi.hoisted( () => ( {} ) );
|
|
11
|
+
const mainEventBusMock = vi.hoisted( () => ( {
|
|
11
12
|
on: vi.fn( ( eventType, handler ) => {
|
|
12
|
-
|
|
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', () => ( {
|
|
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(
|
|
65
|
-
delete
|
|
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(
|
|
75
|
-
expect(
|
|
76
|
-
expect(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
146
|
-
await
|
|
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( '
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
|
194
|
-
|
|
170
|
+
const payload = {
|
|
171
|
+
eventId: 'evt-workflow-1',
|
|
172
|
+
eventDate,
|
|
173
|
+
workflowDetails,
|
|
174
|
+
extra: 'passthrough',
|
|
175
|
+
...extraFields
|
|
176
|
+
};
|
|
177
|
+
registerHook( handler );
|
|
195
178
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
} ) );
|
|
199
|
-
expect( handler ).not.toHaveBeenCalled();
|
|
179
|
+
expect( mainEventBusMock.on ).toHaveBeenCalledWith( eventType, expect.any( Function ) );
|
|
180
|
+
await mainOnHandlers[eventType]( payload );
|
|
200
181
|
|
|
201
|
-
|
|
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
|
|
193
|
+
it.each( cases )( '%s forwards internal activity bus fields', async ( _name, registerHook, eventType, extraFields = {} ) => {
|
|
218
194
|
const handler = vi.fn().mockResolvedValue( undefined );
|
|
219
|
-
|
|
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:
|
|
200
|
+
outputActivityKind: 'internal_step',
|
|
239
201
|
extra: 'passthrough',
|
|
240
202
|
...extraFields
|
|
241
|
-
}
|
|
203
|
+
};
|
|
204
|
+
registerHook( handler );
|
|
242
205
|
|
|
243
|
-
expect(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
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(
|
|
261
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
64
|
+
checkGlobalContextContamination( runId );
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
}
|