@outputai/core 0.5.3-next.bdf47aa.0 → 0.6.1-next.1f47248.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 +2 -2
- package/src/activity_integration/context.d.ts +5 -9
- package/src/activity_integration/context.js +5 -4
- package/src/activity_integration/context.spec.js +10 -15
- package/src/activity_integration/events.d.ts +2 -4
- package/src/activity_integration/events.js +8 -3
- package/src/activity_integration/events.spec.js +58 -29
- package/src/bus.js +18 -9
- package/src/bus.spec.js +30 -0
- package/src/hooks/index.d.ts +112 -58
- package/src/hooks/index.js +15 -12
- package/src/hooks/index.spec.js +60 -32
- package/src/interface/workflow.js +19 -35
- package/src/interface/workflow.spec.js +104 -15
- package/src/internal_activities/index.js +3 -3
- package/src/internal_activities/index.spec.js +31 -1
- package/src/internal_utils/temporal_context.js +12 -0
- package/src/internal_utils/temporal_context.spec.ts +83 -0
- package/src/internal_utils/trace_info.js +21 -0
- package/src/internal_utils/trace_info.spec.js +47 -0
- package/src/internal_utils/workflow_context.js +29 -0
- package/src/internal_utils/workflow_context.spec.js +46 -0
- package/src/logger/development.js +61 -0
- package/src/logger/development.spec.js +70 -0
- package/src/logger/index.js +14 -0
- package/src/logger/index.spec.js +27 -0
- package/src/logger/production.js +15 -0
- package/src/logger/production.spec.js +52 -0
- package/src/tracing/internal_interface.js +4 -4
- package/src/tracing/processors/local/index.js +21 -26
- package/src/tracing/processors/local/index.spec.js +39 -45
- package/src/tracing/processors/s3/index.js +13 -23
- package/src/tracing/processors/s3/index.spec.js +33 -26
- package/src/tracing/trace_attribute.js +0 -1
- package/src/tracing/trace_engine.js +8 -12
- package/src/tracing/trace_engine.spec.js +31 -27
- package/src/worker/configs.js +2 -0
- package/src/worker/configs.spec.js +25 -0
- package/src/worker/index.js +4 -1
- package/src/worker/index.spec.js +4 -0
- package/src/worker/interceptors/activity.js +31 -29
- package/src/worker/interceptors/activity.spec.js +58 -26
- package/src/worker/interceptors/workflow.js +7 -2
- package/src/worker/interceptors/workflow.spec.js +42 -6
- package/src/worker/log_hooks.js +35 -46
- package/src/worker/log_hooks.spec.js +43 -46
- package/src/worker/setup_telemetry.js +19 -0
- package/src/worker/setup_telemetry.spec.js +80 -0
- package/src/worker/sinks.js +24 -24
- package/src/interface/workflow_context.js +0 -33
- package/src/logger.js +0 -73
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1-next.1f47248.0",
|
|
4
4
|
"description": "The core module of the output framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"#bus": "./src/bus.js",
|
|
66
66
|
"#consts": "./src/consts.js",
|
|
67
67
|
"#errors": "./src/errors.js",
|
|
68
|
-
"#logger": "./src/logger.js",
|
|
68
|
+
"#logger": "./src/logger/index.js",
|
|
69
69
|
"#utils": "./src/utils/index.js",
|
|
70
70
|
"#internal_utils/*": "./src/internal_utils/*.js",
|
|
71
71
|
"#tracing": "./src/tracing/internal_interface.js",
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
+
import type { Info } from '@temporalio/activity';
|
|
1
2
|
/**
|
|
2
3
|
* Context returned by {@link getContext} when running inside a Temporal Activity (step or evaluator).
|
|
3
4
|
*/
|
|
4
5
|
export type Context = {
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** Workflow name (Temporal's workflow "type" value) */
|
|
10
|
-
name: string;
|
|
11
|
-
/** Path of the workflow file */
|
|
12
|
-
filename: string;
|
|
13
|
-
}
|
|
6
|
+
/** Temporal info about the current activity */
|
|
7
|
+
activityInfo: Info,
|
|
8
|
+
/** Path of the workflow file */
|
|
9
|
+
workflowFilename: string
|
|
14
10
|
};
|
|
15
11
|
|
|
16
12
|
/**
|
|
@@ -7,11 +7,12 @@ import { Storage } from '#async_storage';
|
|
|
7
7
|
*/
|
|
8
8
|
export const getExecutionContext = () => {
|
|
9
9
|
const ctx = Storage.load();
|
|
10
|
-
|
|
11
|
-
if ( !ctx?.executionContext || !ctx?.workflowFilename ) {
|
|
10
|
+
if ( !ctx ) {
|
|
12
11
|
return null;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
return {
|
|
15
|
+
workflowFilename: ctx.workflowFilename,
|
|
16
|
+
activityInfo: ctx.activityInfo
|
|
17
|
+
};
|
|
17
18
|
};
|
|
@@ -17,26 +17,21 @@ describe( 'getExecutionContext', () => {
|
|
|
17
17
|
expect( getExecutionContext() ).toBeNull();
|
|
18
18
|
} );
|
|
19
19
|
|
|
20
|
-
it( 'returns
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
loadMock.mockReturnValue( { executionContext: { workflowId: 'wf-1', workflowName: 'myWorkflow' } } );
|
|
28
|
-
const { getExecutionContext } = await import( './index.js' );
|
|
29
|
-
expect( getExecutionContext() ).toBeNull();
|
|
30
|
-
} );
|
|
31
|
-
|
|
32
|
-
it( 'returns workflow context when storage has full context', async () => {
|
|
20
|
+
it( 'returns activity execution context from storage', async () => {
|
|
21
|
+
const activityInfo = {
|
|
22
|
+
activityId: 'activity-1',
|
|
23
|
+
activityType: 'myActivity',
|
|
24
|
+
workflowExecution: { workflowId: 'wf-1', runId: 'run-1' },
|
|
25
|
+
workflowType: 'myWorkflow'
|
|
26
|
+
};
|
|
33
27
|
loadMock.mockReturnValue( {
|
|
34
|
-
|
|
28
|
+
activityInfo,
|
|
35
29
|
workflowFilename: '/workflows/myWorkflow.js'
|
|
36
30
|
} );
|
|
37
31
|
const { getExecutionContext } = await import( './index.js' );
|
|
38
32
|
expect( getExecutionContext() ).toEqual( {
|
|
39
|
-
|
|
33
|
+
activityInfo,
|
|
34
|
+
workflowFilename: '/workflows/myWorkflow.js'
|
|
40
35
|
} );
|
|
41
36
|
} );
|
|
42
37
|
} );
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Emits a custom event on the in-process message bus.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* subscribers registered via `on(...)` always receive those identifiers
|
|
7
|
-
* alongside whatever custom fields the emitter supplies.
|
|
4
|
+
* When called inside an Output activity context, the framework automatically
|
|
5
|
+
* attaches `activityInfo`, `workflowDetails`, and `outputActivityKind` onto the emitted payload.
|
|
8
6
|
*
|
|
9
7
|
* @param eventName - The name of the event to emit
|
|
10
8
|
* @param payload - An optional payload to send to the event
|
|
@@ -4,7 +4,12 @@ import { Storage } from '#async_storage';
|
|
|
4
4
|
export const emitEvent = ( eventName, payload ) => {
|
|
5
5
|
const ctx = Storage.load();
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
messageBus.emit( `external:${eventName}`, {
|
|
8
|
+
...payload ?? {},
|
|
9
|
+
...( ctx && {
|
|
10
|
+
activityInfo: ctx.activityInfo,
|
|
11
|
+
workflowDetails: ctx.workflowDetails,
|
|
12
|
+
outputActivityKind: ctx.outputActivityKind
|
|
13
|
+
} )
|
|
14
|
+
} );
|
|
10
15
|
};
|
|
@@ -21,69 +21,98 @@ describe( 'emitEvent', () => {
|
|
|
21
21
|
vi.clearAllMocks();
|
|
22
22
|
} );
|
|
23
23
|
|
|
24
|
-
it( 'forwards
|
|
24
|
+
it( 'forwards activityInfo, workflowDetails, and outputActivityKind from storage', () => {
|
|
25
|
+
const activityInfo = {
|
|
26
|
+
activityId: 'act-1',
|
|
27
|
+
activityType: 'step',
|
|
28
|
+
workflowExecution: { workflowId: 'wf-1', runId: 'run-1' },
|
|
29
|
+
workflowType: 'workflow'
|
|
30
|
+
};
|
|
31
|
+
const workflowDetails = {
|
|
32
|
+
workflowId: 'wf-1',
|
|
33
|
+
runId: 'run-1',
|
|
34
|
+
workflowType: 'workflow'
|
|
35
|
+
};
|
|
25
36
|
loadMock.mockReturnValue( {
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
activityInfo,
|
|
38
|
+
workflowDetails,
|
|
39
|
+
outputActivityKind: 'step'
|
|
28
40
|
} );
|
|
29
41
|
|
|
30
42
|
emitEvent( 'cost:llm:request', { modelId: 'gpt-4o' } );
|
|
31
43
|
|
|
32
44
|
expect( emitMock ).toHaveBeenCalledWith( 'external:cost:llm:request', expect.objectContaining( {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
activityInfo,
|
|
46
|
+
workflowDetails,
|
|
47
|
+
outputActivityKind: 'step',
|
|
36
48
|
modelId: 'gpt-4o'
|
|
37
49
|
} ) );
|
|
38
50
|
} );
|
|
39
51
|
|
|
40
|
-
it( '
|
|
52
|
+
it( 'emits payload without context when storage is missing', () => {
|
|
41
53
|
loadMock.mockReturnValue( undefined );
|
|
42
54
|
|
|
43
55
|
emitEvent( 'foo:bar', { x: 1 } );
|
|
44
56
|
|
|
45
|
-
expect( emitMock ).toHaveBeenCalledWith( 'external:foo:bar',
|
|
46
|
-
workflowId: undefined,
|
|
47
|
-
runId: undefined,
|
|
48
|
-
activityId: undefined,
|
|
49
|
-
x: 1
|
|
50
|
-
} ) );
|
|
57
|
+
expect( emitMock ).toHaveBeenCalledWith( 'external:foo:bar', { x: 1 } );
|
|
51
58
|
} );
|
|
52
59
|
|
|
53
60
|
it( 'handles missing payload', () => {
|
|
61
|
+
const activityInfo = {
|
|
62
|
+
activityId: 'act-2',
|
|
63
|
+
activityType: 'step',
|
|
64
|
+
workflowExecution: { workflowId: 'wf-2', runId: 'run-2' },
|
|
65
|
+
workflowType: 'workflow'
|
|
66
|
+
};
|
|
67
|
+
const workflowDetails = {
|
|
68
|
+
workflowId: 'wf-2',
|
|
69
|
+
runId: 'run-2',
|
|
70
|
+
workflowType: 'workflow'
|
|
71
|
+
};
|
|
54
72
|
loadMock.mockReturnValue( {
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
activityInfo,
|
|
74
|
+
workflowDetails,
|
|
75
|
+
outputActivityKind: 'step'
|
|
57
76
|
} );
|
|
58
77
|
|
|
59
78
|
emitEvent( 'lifecycle:start' );
|
|
60
79
|
|
|
61
80
|
expect( emitMock ).toHaveBeenCalledWith( 'external:lifecycle:start', expect.objectContaining( {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
activityInfo,
|
|
82
|
+
workflowDetails,
|
|
83
|
+
outputActivityKind: 'step'
|
|
65
84
|
} ) );
|
|
66
85
|
} );
|
|
67
86
|
|
|
68
|
-
it( 'does not let payload override
|
|
87
|
+
it( 'does not let payload override activityInfo, workflowDetails, or outputActivityKind', () => {
|
|
88
|
+
const activityInfo = {
|
|
89
|
+
activityId: 'act-3',
|
|
90
|
+
activityType: 'step',
|
|
91
|
+
workflowExecution: { workflowId: 'wf-3', runId: 'run-3' },
|
|
92
|
+
workflowType: 'workflow'
|
|
93
|
+
};
|
|
94
|
+
const workflowDetails = {
|
|
95
|
+
workflowId: 'wf-3',
|
|
96
|
+
runId: 'run-3',
|
|
97
|
+
workflowType: 'workflow'
|
|
98
|
+
};
|
|
69
99
|
loadMock.mockReturnValue( {
|
|
70
|
-
|
|
71
|
-
|
|
100
|
+
activityInfo,
|
|
101
|
+
workflowDetails,
|
|
102
|
+
outputActivityKind: 'step'
|
|
72
103
|
} );
|
|
73
104
|
|
|
74
105
|
emitEvent( 'cost:http:request', {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
activityInfo: { activityId: 'should-be-overridden' },
|
|
107
|
+
workflowDetails: { workflowId: 'should-be-overridden' },
|
|
108
|
+
outputActivityKind: 'should-be-overridden',
|
|
78
109
|
url: 'https://example.com'
|
|
79
110
|
} );
|
|
80
111
|
|
|
81
|
-
// Context fields are spread after the payload, so caller-supplied
|
|
82
|
-
// workflowId / runId / activityId cannot escape the executionContext.
|
|
83
112
|
expect( emitMock ).toHaveBeenCalledWith( 'external:cost:http:request', expect.objectContaining( {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
activityInfo,
|
|
114
|
+
workflowDetails,
|
|
115
|
+
outputActivityKind: 'step',
|
|
87
116
|
url: 'https://example.com'
|
|
88
117
|
} ) );
|
|
89
118
|
} );
|
package/src/bus.js
CHANGED
|
@@ -3,19 +3,28 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { isPlainObject } from '#utils';
|
|
4
4
|
|
|
5
5
|
const emitter = new EventEmitter();
|
|
6
|
-
const originalEmit = emitter.emit.bind( emitter );
|
|
7
|
-
|
|
8
|
-
const attachEventId = payload => ( { ...payload, eventId: payload.eventId ?? randomUUID() } );
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* Every object payload emitted through `messageBus` is stamped with a UUID v4 `eventId`.
|
|
12
9
|
*/
|
|
13
|
-
emitter.emit = (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
emitter.emit = new Proxy( emitter.emit, {
|
|
11
|
+
apply( target, thisArg, args ) {
|
|
12
|
+
const [ eventName, payload, ...extras ] = args;
|
|
13
|
+
const newArguments = [];
|
|
14
|
+
|
|
15
|
+
// do now push arguments that dont exist. if payload is undefined with len=1, means it was never defined
|
|
16
|
+
// if payload is undefined with len>1, means user passed 'undefined' as argument
|
|
17
|
+
if ( args.length > 1 ) {
|
|
18
|
+
newArguments.push( isPlainObject( payload ) ? {
|
|
19
|
+
eventId: randomUUID(),
|
|
20
|
+
eventDate: Date.now(),
|
|
21
|
+
...payload
|
|
22
|
+
} : payload );
|
|
23
|
+
newArguments.push( ...extras );
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return Reflect.apply( target, thisArg, [ eventName, ...newArguments ] );
|
|
17
27
|
}
|
|
18
|
-
|
|
19
|
-
};
|
|
28
|
+
} );
|
|
20
29
|
|
|
21
30
|
export const messageBus = emitter;
|
package/src/bus.spec.js
CHANGED
|
@@ -47,6 +47,35 @@ describe( 'messageBus', () => {
|
|
|
47
47
|
} ) );
|
|
48
48
|
} );
|
|
49
49
|
|
|
50
|
+
it( 'stamps eventDate on every object payload', () => {
|
|
51
|
+
const handler = vi.fn();
|
|
52
|
+
const now = new Date( '2026-06-02T12:00:00.000Z' );
|
|
53
|
+
vi.useFakeTimers();
|
|
54
|
+
vi.setSystemTime( now );
|
|
55
|
+
messageBus.on( 'test:event', handler );
|
|
56
|
+
|
|
57
|
+
messageBus.emit( 'test:event', { foo: 'bar' } );
|
|
58
|
+
|
|
59
|
+
expect( handler ).toHaveBeenCalledWith( expect.objectContaining( {
|
|
60
|
+
eventDate: now.getTime(),
|
|
61
|
+
foo: 'bar'
|
|
62
|
+
} ) );
|
|
63
|
+
|
|
64
|
+
vi.useRealTimers();
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
it( 'preserves a caller-supplied eventDate', () => {
|
|
68
|
+
const handler = vi.fn();
|
|
69
|
+
messageBus.on( 'test:event', handler );
|
|
70
|
+
|
|
71
|
+
messageBus.emit( 'test:event', { eventDate: 1234, foo: 'bar' } );
|
|
72
|
+
|
|
73
|
+
expect( handler ).toHaveBeenCalledWith( expect.objectContaining( {
|
|
74
|
+
eventDate: 1234,
|
|
75
|
+
foo: 'bar'
|
|
76
|
+
} ) );
|
|
77
|
+
} );
|
|
78
|
+
|
|
50
79
|
it( 'does not mutate the caller-supplied payload object', () => {
|
|
51
80
|
const handler = vi.fn();
|
|
52
81
|
messageBus.on( 'test:event', handler );
|
|
@@ -55,6 +84,7 @@ describe( 'messageBus', () => {
|
|
|
55
84
|
messageBus.emit( 'test:event', payload );
|
|
56
85
|
|
|
57
86
|
expect( payload ).not.toHaveProperty( 'eventId' );
|
|
87
|
+
expect( payload ).not.toHaveProperty( 'eventDate' );
|
|
58
88
|
} );
|
|
59
89
|
} );
|
|
60
90
|
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -1,63 +1,130 @@
|
|
|
1
|
+
import type { Info } from '@temporalio/activity';
|
|
2
|
+
|
|
3
|
+
export interface WorkflowDetails {
|
|
4
|
+
/**
|
|
5
|
+
* ID of the Workflow, this can be set by the client during Workflow creation.
|
|
6
|
+
* A single Workflow may run multiple times e.g. when scheduled with cron.
|
|
7
|
+
*/
|
|
8
|
+
workflowId: string;
|
|
9
|
+
/**
|
|
10
|
+
* ID of a single Workflow run
|
|
11
|
+
*/
|
|
12
|
+
runId: string;
|
|
13
|
+
/**
|
|
14
|
+
* Workflow function's name
|
|
15
|
+
*/
|
|
16
|
+
workflowType: string;
|
|
17
|
+
/**
|
|
18
|
+
* Parent Workflow info (present if this is a Child Workflow)
|
|
19
|
+
*/
|
|
20
|
+
parent?: {
|
|
21
|
+
/**
|
|
22
|
+
* ID of the Workflow, this can be set by the client during Workflow creation.
|
|
23
|
+
* A single Workflow may run multiple times e.g. when scheduled with cron.
|
|
24
|
+
*/
|
|
25
|
+
workflowId: string;
|
|
26
|
+
/**
|
|
27
|
+
* ID of a single Workflow run
|
|
28
|
+
*/
|
|
29
|
+
runId: string;
|
|
30
|
+
/**
|
|
31
|
+
* Namespace this Workflow is executing in
|
|
32
|
+
*/
|
|
33
|
+
namespace: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The root workflow execution, defined as follows:
|
|
37
|
+
* 1. A workflow without a parent workflow is its own root workflow.
|
|
38
|
+
* 2. A workflow with a parent workflow has the same root workflow as
|
|
39
|
+
* its parent.
|
|
40
|
+
*
|
|
41
|
+
* When there is no parent workflow, i.e., the workflow is its own root workflow,
|
|
42
|
+
* this field is `undefined`.
|
|
43
|
+
*
|
|
44
|
+
* Note that Continue-as-New (or reset) propagates the workflow parentage relationship,
|
|
45
|
+
* and therefore, whether the new workflow has the same root workflow as the original one
|
|
46
|
+
* depends on whether it had a parent.
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
root?: {
|
|
50
|
+
/**
|
|
51
|
+
* ID of the Workflow, this can be set by the client during Workflow creation.
|
|
52
|
+
* A single Workflow may run multiple times e.g. when scheduled with cron.
|
|
53
|
+
*/
|
|
54
|
+
workflowId: string;
|
|
55
|
+
/**
|
|
56
|
+
* ID of a single Workflow run
|
|
57
|
+
*/
|
|
58
|
+
runId: string;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Run Id of the first Run in this Execution Chain
|
|
62
|
+
*/
|
|
63
|
+
firstExecutionRunId: string;
|
|
64
|
+
/**
|
|
65
|
+
* The last Run Id in this Execution Chain
|
|
66
|
+
*/
|
|
67
|
+
continuedFromExecutionRunId?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Time at which this [Workflow Execution Chain](https://docs.temporal.io/workflows#workflow-execution-chain) was started
|
|
70
|
+
*/
|
|
71
|
+
startTime: number;
|
|
72
|
+
/**
|
|
73
|
+
* Time at which the current Workflow Run started
|
|
74
|
+
*/
|
|
75
|
+
runStartTime: number;
|
|
76
|
+
/**
|
|
77
|
+
* Starts at 1 and increments for every retry if there is a `retryPolicy`
|
|
78
|
+
*/
|
|
79
|
+
attempt: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
1
82
|
/**
|
|
2
|
-
* Payload passed to the onError handler when a workflow, activity or runtime error occurs.
|
|
83
|
+
* Payload passed to the onError() handler when a workflow, activity or runtime error occurs.
|
|
3
84
|
*/
|
|
4
85
|
export interface ErrorHookPayload {
|
|
5
86
|
/** UUID v4 stamped per emit. Stable per-emit idempotency key. */
|
|
6
87
|
eventId: string;
|
|
7
88
|
/** Origin of the error: workflow execution, activity execution, or runtime. */
|
|
8
89
|
source: 'workflow' | 'activity' | 'runtime';
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
|
|
90
|
+
/** Information about the current workflow execution */
|
|
91
|
+
workflowDetails?: WorkflowDetails;
|
|
92
|
+
/** Temporal's activityInfo(). If source is activity */
|
|
93
|
+
activityInfo?: Info;
|
|
94
|
+
/** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
|
|
95
|
+
outputActivityKind?: string;
|
|
13
96
|
/** The error thrown. */
|
|
14
97
|
error: Error;
|
|
15
98
|
}
|
|
16
99
|
|
|
17
100
|
/**
|
|
18
|
-
* Payload passed to the onWorkflowStart handler when a workflow run begins.
|
|
101
|
+
* Payload passed to the onWorkflowStart() handler when a workflow run begins.
|
|
19
102
|
*/
|
|
20
103
|
export interface WorkflowStartHookPayload {
|
|
21
104
|
/** UUID v4 stamped per emit. Stable per-emit idempotency key. */
|
|
22
105
|
eventId: string;
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
/** Temporal run id for the current execution attempt. */
|
|
26
|
-
runId: string;
|
|
27
|
-
/** Name of the workflow. */
|
|
28
|
-
name: string;
|
|
106
|
+
/** Information about the current workflow execution */
|
|
107
|
+
workflowDetails: WorkflowDetails;
|
|
29
108
|
}
|
|
30
109
|
|
|
31
110
|
/**
|
|
32
|
-
* Payload passed to the onWorkflowEnd handler when a workflow run completes successfully.
|
|
111
|
+
* Payload passed to the onWorkflowEnd() handler when a workflow run completes successfully.
|
|
33
112
|
*/
|
|
34
113
|
export interface WorkflowEndHookPayload {
|
|
35
114
|
/** UUID v4 stamped per emit. Stable per-emit idempotency key. */
|
|
36
115
|
eventId: string;
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
/** Temporal run id for the current execution attempt. */
|
|
40
|
-
runId: string;
|
|
41
|
-
/** Name of the workflow. */
|
|
42
|
-
name: string;
|
|
43
|
-
/** Duration of the workflow run in milliseconds. */
|
|
44
|
-
duration: number;
|
|
116
|
+
/** Information about the current workflow execution */
|
|
117
|
+
workflowDetails: WorkflowDetails;
|
|
45
118
|
}
|
|
46
119
|
|
|
47
120
|
/**
|
|
48
|
-
* Payload passed to the onWorkflowError handler when a workflow run fails.
|
|
121
|
+
* Payload passed to the onWorkflowError() handler when a workflow run fails.
|
|
49
122
|
*/
|
|
50
123
|
export interface WorkflowErrorHookPayload {
|
|
51
124
|
/** UUID v4 stamped per emit. Stable per-emit idempotency key. */
|
|
52
125
|
eventId: string;
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
/** Temporal run id for the current execution attempt. */
|
|
56
|
-
runId: string;
|
|
57
|
-
/** Name of the workflow. */
|
|
58
|
-
name: string;
|
|
59
|
-
/** Elapsed time before failure in milliseconds. */
|
|
60
|
-
duration: number;
|
|
126
|
+
/** Information about the current workflow execution */
|
|
127
|
+
workflowDetails: WorkflowDetails;
|
|
61
128
|
/** The error thrown. */
|
|
62
129
|
error: Error;
|
|
63
130
|
}
|
|
@@ -105,42 +172,29 @@ export declare function onWorkflowEnd( handler: ( payload: WorkflowEndHookPayloa
|
|
|
105
172
|
export declare function onWorkflowError( handler: ( payload: WorkflowErrorHookPayload ) => void ): void;
|
|
106
173
|
|
|
107
174
|
/**
|
|
108
|
-
*
|
|
109
|
-
* through `@outputai/http`'s `fetch`. Fires for success, non-2xx, and network
|
|
110
|
-
* failure paths — `cost:http:request` continues to fire only when the consumer
|
|
111
|
-
* has attached a cost via `addRequestCost`.
|
|
112
|
-
*
|
|
113
|
-
* The framework auto-attaches `workflowId`, `runId`, and `activityId` onto the
|
|
114
|
-
* payload before broadcast, so consumers receive those identifiers in addition
|
|
115
|
-
* to the fields listed here.
|
|
175
|
+
* Framework-managed envelope added to payloads passed to on() handlers.
|
|
116
176
|
*/
|
|
117
|
-
export interface
|
|
177
|
+
export interface OnHookEnvelope {
|
|
118
178
|
/** UUID v4 stamped per emit. Stable per-emit idempotency key. */
|
|
119
179
|
eventId: string;
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
/** Temporal
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
/** UUID generated per request inside `@outputai/http`. */
|
|
127
|
-
requestId: string;
|
|
128
|
-
/** HTTP method (uppercase). */
|
|
129
|
-
method: string;
|
|
130
|
-
/** Absolute request URL. */
|
|
131
|
-
url: string;
|
|
132
|
-
/** HTTP status code; undefined on network failure. */
|
|
133
|
-
status?: number;
|
|
134
|
-
/** Elapsed time from request issuance to response (or failure), in milliseconds. */
|
|
135
|
-
durationMs: number;
|
|
136
|
-
/** Outcome bucket: `success` (2xx-3xx), `error` (status >= 400), `failure` (DNS / timeout / abort). */
|
|
137
|
-
outcome: 'success' | 'error' | 'failure';
|
|
180
|
+
/** Information about the current workflow execution */
|
|
181
|
+
workflowDetails: WorkflowDetails;
|
|
182
|
+
/** Temporal's activityInfo(). */
|
|
183
|
+
activityInfo: Info;
|
|
184
|
+
/** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
|
|
185
|
+
outputActivityKind?: string;
|
|
138
186
|
}
|
|
139
187
|
|
|
188
|
+
export type OnHookPayload<TAttributes extends Record<string, unknown> = Record<string, unknown>> =
|
|
189
|
+
OnHookEnvelope & TAttributes;
|
|
190
|
+
|
|
140
191
|
/**
|
|
141
192
|
* Register a handler to be invoked when a given event happens
|
|
142
193
|
*
|
|
143
194
|
* @param eventName - The name of the event to subscribe
|
|
144
195
|
* @param handler - Function called with the event payload
|
|
145
196
|
*/
|
|
146
|
-
export declare function on
|
|
197
|
+
export declare function on<TAttributes extends Record<string, unknown> = Record<string, unknown>>(
|
|
198
|
+
eventName: string,
|
|
199
|
+
handler: ( payload: OnHookPayload<TAttributes> ) => void
|
|
200
|
+
): void;
|
package/src/hooks/index.js
CHANGED
|
@@ -21,29 +21,32 @@ const safeInvoke = async ( fn, args, hookName ) => {
|
|
|
21
21
|
|
|
22
22
|
/** Triggers on any errors: workflow, activity and runtime */
|
|
23
23
|
export const onError = handler => {
|
|
24
|
-
messageBus.on( BusEventType.ACTIVITY_ERROR, async
|
|
25
|
-
safeInvoke( handler, {
|
|
26
|
-
messageBus.on( BusEventType.WORKFLOW_ERROR, async
|
|
27
|
-
safeInvoke( handler, {
|
|
28
|
-
messageBus.on( BusEventType.RUNTIME_ERROR, async
|
|
29
|
-
safeInvoke( handler, {
|
|
24
|
+
messageBus.on( BusEventType.ACTIVITY_ERROR, async payload =>
|
|
25
|
+
safeInvoke( handler, { source: 'activity', ...payload }, 'onError' ) );
|
|
26
|
+
messageBus.on( BusEventType.WORKFLOW_ERROR, async payload =>
|
|
27
|
+
safeInvoke( handler, { source: 'workflow', ...payload }, 'onError' ) );
|
|
28
|
+
messageBus.on( BusEventType.RUNTIME_ERROR, async payload =>
|
|
29
|
+
safeInvoke( handler, { source: 'runtime', ...payload }, 'onError' ) );
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
/** Listen to worker before start events */
|
|
33
33
|
export const onBeforeWorkerStart = handler => messageBus.on( BusEventType.WORKER_BEFORE_START, () =>
|
|
34
34
|
safeInvoke( handler, undefined, 'onBeforeWorkerStart' ) );
|
|
35
35
|
|
|
36
|
+
/** Catalog workflow events should not be emitted */
|
|
37
|
+
const shouldEmitWorkflowEvent = workflowDetails => WORKFLOW_CATALOG !== workflowDetails.workflowType;
|
|
38
|
+
|
|
36
39
|
/** Listen to workflow start events, excludes catalog workflow */
|
|
37
|
-
export const onWorkflowStart = handler => messageBus.on( BusEventType.WORKFLOW_START, ( {
|
|
38
|
-
|
|
40
|
+
export const onWorkflowStart = handler => messageBus.on( BusEventType.WORKFLOW_START, ( { workflowDetails, ...eventFields } ) =>
|
|
41
|
+
shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowStart' ) : null );
|
|
39
42
|
|
|
40
43
|
/** Listen to workflow end events, excludes catalog workflow */
|
|
41
|
-
export const onWorkflowEnd = handler => messageBus.on( BusEventType.WORKFLOW_END, ( {
|
|
42
|
-
|
|
44
|
+
export const onWorkflowEnd = handler => messageBus.on( BusEventType.WORKFLOW_END, ( { workflowDetails, ...eventFields } ) =>
|
|
45
|
+
shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowEnd' ) : null );
|
|
43
46
|
|
|
44
47
|
/** Listen to workflow error events, excludes catalog workflow */
|
|
45
|
-
export const onWorkflowError = handler => messageBus.on( BusEventType.WORKFLOW_ERROR, ( {
|
|
46
|
-
|
|
48
|
+
export const onWorkflowError = handler => messageBus.on( BusEventType.WORKFLOW_ERROR, ( { workflowDetails, ...eventFields } ) =>
|
|
49
|
+
shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowError' ) : null );
|
|
47
50
|
|
|
48
51
|
/** Generic listener for events emitted elsewhere (outside core) */
|
|
49
52
|
export const on = ( eventName, handler ) => messageBus.on( `external:${eventName}`, payload =>
|