@outputai/core 0.2.1-next.fd72d95.0 → 0.3.1-next.00e0047.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 +7 -7
- package/src/activity_integration/tracing.d.ts +30 -17
- package/src/activity_integration/tracing.js +33 -17
- package/src/hooks/index.d.ts +66 -3
- package/src/hooks/index.js +40 -24
- package/src/hooks/index.spec.js +176 -0
- package/src/tracing/internal_interface.js +34 -28
- package/src/tracing/processors/local/index.js +43 -10
- package/src/tracing/processors/local/index.spec.js +66 -36
- package/src/tracing/processors/s3/index.js +10 -5
- package/src/tracing/processors/s3/index.spec.js +7 -7
- package/src/tracing/tools/build_trace_tree.js +17 -12
- package/src/tracing/tools/build_trace_tree.spec.js +63 -20
- package/src/tracing/tools/utils.js +28 -0
- package/src/tracing/tools/utils.spec.js +134 -2
- package/src/tracing/trace_consts.js +9 -0
- package/src/tracing/trace_engine.js +10 -10
- package/src/tracing/trace_engine.spec.js +23 -23
- package/src/worker/configs.js +9 -1
- package/src/worker/index.js +8 -2
- package/src/worker/index.spec.js +7 -1
- package/src/worker/proxy.js +32 -0
- package/src/worker/proxy.spec.js +71 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1-next.00e0047.0",
|
|
4
4
|
"description": "The core module of the output framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -32,16 +32,16 @@
|
|
|
32
32
|
"output-copy-assets": "./bin/copy_assets.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@aws-sdk/client-s3": "3.
|
|
35
|
+
"@aws-sdk/client-s3": "3.1038.0",
|
|
36
36
|
"@babel/generator": "7.29.1",
|
|
37
37
|
"@babel/parser": "7.29.2",
|
|
38
38
|
"@babel/traverse": "7.29.0",
|
|
39
39
|
"@babel/types": "7.29.0",
|
|
40
|
-
"@temporalio/activity": "1.
|
|
41
|
-
"@temporalio/client": "1.
|
|
42
|
-
"@temporalio/common": "1.
|
|
43
|
-
"@temporalio/worker": "1.
|
|
44
|
-
"@temporalio/workflow": "1.
|
|
40
|
+
"@temporalio/activity": "1.17.0",
|
|
41
|
+
"@temporalio/client": "1.17.0",
|
|
42
|
+
"@temporalio/common": "1.17.0",
|
|
43
|
+
"@temporalio/worker": "1.17.0",
|
|
44
|
+
"@temporalio/workflow": "1.17.0",
|
|
45
45
|
"redis": "5.12.1",
|
|
46
46
|
"stacktrace-parser": "0.1.11",
|
|
47
47
|
"undici": "8.1.0",
|
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Creates a new event.
|
|
3
3
|
*
|
|
4
|
-
* @param args
|
|
5
|
-
* @param args.id - A unique id for the Event
|
|
4
|
+
* @param args
|
|
5
|
+
* @param args.id - A unique id for the Event.
|
|
6
6
|
* @param args.kind - The kind of Event, like HTTP, DiskWrite, DBOp, etc.
|
|
7
|
-
* @param args.name - The human
|
|
8
|
-
* @param args.details - Arbitrary
|
|
7
|
+
* @param args.name - The human-friendly name of the Event: query, request, create.
|
|
8
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "input" field.
|
|
9
9
|
*/
|
|
10
10
|
export declare function addEventStart( args: { id: string; kind: string; name: string; details: unknown } ): void;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Concludes an event.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* @param args -
|
|
18
|
-
* @param args.id - Identifier matching the event's start phase.
|
|
19
|
-
* @param args.details - Arbitrary metadata associated with this phase (e.g., results, response body).
|
|
15
|
+
* @param args
|
|
16
|
+
* @param args.id - The id of the event to conclude.
|
|
17
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "output" field.
|
|
20
18
|
*/
|
|
21
19
|
export declare function addEventEnd( args: { id: string; details: unknown } ): void;
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* Use the same id as the start phase to correlate phases.
|
|
22
|
+
* Concludes an event with an error.
|
|
27
23
|
*
|
|
28
|
-
* @param args
|
|
29
|
-
* @param args.id -
|
|
30
|
-
* @param args.details - Arbitrary
|
|
24
|
+
* @param args
|
|
25
|
+
* @param args.id - The id of the event to conclude.
|
|
26
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "error" field.
|
|
31
27
|
*/
|
|
32
28
|
export declare function addEventError( args: { id: string; details: unknown } ): void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Adds an attribute to an event.
|
|
32
|
+
*
|
|
33
|
+
* @param args
|
|
34
|
+
* @param args.eventId - The id of the event to attach the attribute to.
|
|
35
|
+
* @param args.name - The attribute name
|
|
36
|
+
* @param args.value - The attribute value
|
|
37
|
+
*/
|
|
38
|
+
export declare function addEventAttribute( args: { eventId: string; name: string, value: unknown } ): void;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Known attributes.
|
|
42
|
+
*/
|
|
43
|
+
export declare const Attribute: {
|
|
44
|
+
COST: 'cost';
|
|
45
|
+
};
|
|
@@ -1,37 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { addEventActionWithContext, EventAction } from '#tracing';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Creates a new event.
|
|
5
5
|
*
|
|
6
6
|
* @param {object} args
|
|
7
|
-
* @param {string} args.id - A unique id for the Event
|
|
7
|
+
* @param {string} args.id - A unique id for the Event.
|
|
8
8
|
* @param {string} args.kind - The kind of Event, like HTTP, DiskWrite, DBOp, etc.
|
|
9
|
-
* @param {string} args.name - The human
|
|
10
|
-
* @param {object} args.details -
|
|
9
|
+
* @param {string} args.name - The human-friendly name of the Event: query, request, create.
|
|
10
|
+
* @param {object} args.details - Arbitrary data to add to this event, it will be used as the "input" field.
|
|
11
11
|
* @returns {void}
|
|
12
12
|
*/
|
|
13
|
-
export const addEventStart = ( { id, kind, name, details } ) =>
|
|
13
|
+
export const addEventStart = ( { id, kind, name, details } ) =>
|
|
14
|
+
addEventActionWithContext( EventAction.START, { kind, name, details, id } );
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* It needs to use the same id of the start phase.
|
|
17
|
+
* Concludes an event.
|
|
19
18
|
*
|
|
20
19
|
* @param {object} args
|
|
21
|
-
* @param {string} args.id -
|
|
22
|
-
* @param {object} args.details -
|
|
20
|
+
* @param {string} args.id - The id of the event to conclude.
|
|
21
|
+
* @param {object} args.details - Arbitrary data to add to this event, it will be used as the "output" field.
|
|
23
22
|
* @returns {void}
|
|
24
23
|
*/
|
|
25
|
-
export const addEventEnd = ( { id, details } ) =>
|
|
24
|
+
export const addEventEnd = ( { id, details } ) => addEventActionWithContext( EventAction.END, { id, details } );
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
|
-
*
|
|
27
|
+
* Concludes an event with an error.
|
|
29
28
|
*
|
|
30
|
-
*
|
|
29
|
+
* @param {object} args
|
|
30
|
+
* @param {string} args.id - The id of the event to conclude.
|
|
31
|
+
* @param {object} args.details - Arbitrary data to add to this event, it will be used as the "error" field.
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
export const addEventError = ( { id, details } ) => addEventActionWithContext( EventAction.ERROR, { id, details } );
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Adds an attribute to an event.
|
|
31
38
|
*
|
|
32
39
|
* @param {object} args
|
|
33
|
-
* @param {string} args.
|
|
34
|
-
* @param {
|
|
40
|
+
* @param {string} args.eventId - The id of the event to attach the attribute to.
|
|
41
|
+
* @param {string} args.name - The attribute name
|
|
42
|
+
* @param {unknown} args.value - The attribute value
|
|
35
43
|
* @returns {void}
|
|
36
44
|
*/
|
|
37
|
-
export const
|
|
45
|
+
export const addEventAttribute = ( { eventId, name, value } ) =>
|
|
46
|
+
addEventActionWithContext( EventAction.ADD_ATTR, { id: eventId, details: { name, value } } );
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Known attributes
|
|
50
|
+
*/
|
|
51
|
+
export const Attribute = {
|
|
52
|
+
COST: 'cost'
|
|
53
|
+
};
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -12,6 +12,42 @@ export interface ErrorHookPayload {
|
|
|
12
12
|
error: Error;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Payload passed to the onWorkflowStart handler when a workflow run begins.
|
|
17
|
+
*/
|
|
18
|
+
export interface WorkflowStartHookPayload {
|
|
19
|
+
/** Identifier of the workflow run. */
|
|
20
|
+
id: string;
|
|
21
|
+
/** Name of the workflow. */
|
|
22
|
+
name: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Payload passed to the onWorkflowEnd handler when a workflow run completes successfully.
|
|
27
|
+
*/
|
|
28
|
+
export interface WorkflowEndHookPayload {
|
|
29
|
+
/** Identifier of the workflow run. */
|
|
30
|
+
id: string;
|
|
31
|
+
/** Name of the workflow. */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Duration of the workflow run in milliseconds. */
|
|
34
|
+
duration: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Payload passed to the onWorkflowError handler when a workflow run fails.
|
|
39
|
+
*/
|
|
40
|
+
export interface WorkflowErrorHookPayload {
|
|
41
|
+
/** Identifier of the workflow run. */
|
|
42
|
+
id: string;
|
|
43
|
+
/** Name of the workflow. */
|
|
44
|
+
name: string;
|
|
45
|
+
/** Elapsed time before failure in milliseconds. */
|
|
46
|
+
duration: number;
|
|
47
|
+
/** The error thrown. */
|
|
48
|
+
error: Error;
|
|
49
|
+
}
|
|
50
|
+
|
|
15
51
|
/**
|
|
16
52
|
* Register a handler to be invoked on workflow, activity or runtime errors.
|
|
17
53
|
*
|
|
@@ -21,11 +57,38 @@ export declare function onError( handler: ( payload: ErrorHookPayload ) => void
|
|
|
21
57
|
|
|
22
58
|
/**
|
|
23
59
|
* Register a handler to be invoked once, before the worker starts processing tasks.
|
|
24
|
-
*
|
|
60
|
+
* It is invoked before Worker.create().
|
|
25
61
|
*
|
|
26
62
|
* @param handler - Function called with no arguments.
|
|
27
63
|
*/
|
|
28
|
-
export declare function
|
|
64
|
+
export declare function onBeforeWorkerStart( handler: () => void ): void;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register a handler to be invoked when a workflow run starts.
|
|
68
|
+
*
|
|
69
|
+
* Excludes the $catalog internal workflow.
|
|
70
|
+
*
|
|
71
|
+
* @param handler - Function called with the workflow start payload.
|
|
72
|
+
*/
|
|
73
|
+
export declare function onWorkflowStart( handler: ( payload: WorkflowStartHookPayload ) => void ): void;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Register a handler to be invoked when a workflow run completes successfully.
|
|
77
|
+
*
|
|
78
|
+
* Excludes the $catalog internal workflow.
|
|
79
|
+
*
|
|
80
|
+
* @param handler - Function called with the workflow end payload.
|
|
81
|
+
*/
|
|
82
|
+
export declare function onWorkflowEnd( handler: ( payload: WorkflowEndHookPayload ) => void ): void;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Register a handler to be invoked when a workflow run fails.
|
|
86
|
+
*
|
|
87
|
+
* Excludes the $catalog internal workflow.
|
|
88
|
+
*
|
|
89
|
+
* @param handler - Function called with the workflow error payload.
|
|
90
|
+
*/
|
|
91
|
+
export declare function onWorkflowError( handler: ( payload: WorkflowErrorHookPayload ) => void ): void;
|
|
29
92
|
|
|
30
93
|
/**
|
|
31
94
|
* Register a handler to be invoked when a given event happens
|
|
@@ -33,4 +96,4 @@ export declare function onBeforeStart( handler: () => void ): void;
|
|
|
33
96
|
* @param eventName - The name of the event to subscribe
|
|
34
97
|
* @param handler - Function called with the event payload
|
|
35
98
|
*/
|
|
36
|
-
export declare function on( eventName: string, handler: ( payload
|
|
99
|
+
export declare function on( eventName: string, handler: ( payload?: object ) => void ): void;
|
package/src/hooks/index.js
CHANGED
|
@@ -1,34 +1,50 @@
|
|
|
1
1
|
import { messageBus } from '#bus';
|
|
2
|
-
import { BusEventType } from '#consts';
|
|
2
|
+
import { BusEventType, WORKFLOW_CATALOG } from '#consts';
|
|
3
3
|
import { createChildLogger } from '#logger';
|
|
4
4
|
|
|
5
5
|
const log = createChildLogger( 'Hooks' );
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Invokes an external hook handler function with a try catch around it
|
|
9
|
+
*
|
|
10
|
+
* @param {Function} fn
|
|
11
|
+
* @param {any} args - Args to invoke the function with
|
|
12
|
+
* @param {string} hookName - hookName to identify this hook function in the logs
|
|
13
|
+
*/
|
|
14
|
+
const safeInvoke = async ( fn, args, hookName ) => {
|
|
15
|
+
try {
|
|
16
|
+
await fn( args );
|
|
17
|
+
} catch ( error ) {
|
|
18
|
+
log.error( `${hookName} hook error`, { message: error.message, stack: error.stack } );
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Triggers on any errors: workflow, activity and runtime */
|
|
7
23
|
export const onError = handler => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
log.error( 'onError hook error', { error } );
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
messageBus.on( BusEventType.ACTIVITY_ERROR, async ( { name, workflowName, error } ) =>
|
|
17
|
-
invokeHandler( { source: 'activity', activityName: name, workflowName, error } ) );
|
|
18
|
-
messageBus.on( BusEventType.WORKFLOW_ERROR, async ( { name, error } ) =>
|
|
19
|
-
invokeHandler( { source: 'workflow', workflowName: name, error } ) );
|
|
24
|
+
messageBus.on( BusEventType.ACTIVITY_ERROR, async ( { id, name, workflowId, workflowName, error } ) =>
|
|
25
|
+
safeInvoke( handler, { source: 'activity', activityId: id, activityName: name, workflowId, workflowName, error }, 'onError' ) );
|
|
26
|
+
messageBus.on( BusEventType.WORKFLOW_ERROR, async ( { id, name, error } ) =>
|
|
27
|
+
safeInvoke( handler, { source: 'workflow', workflowId: id, workflowName: name, error }, 'onError' ) );
|
|
20
28
|
messageBus.on( BusEventType.RUNTIME_ERROR, async ( { error } ) =>
|
|
21
|
-
|
|
29
|
+
safeInvoke( handler, { source: 'runtime', error }, 'onError' ) );
|
|
22
30
|
};
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
/** Listen to worker before start events */
|
|
33
|
+
export const onBeforeWorkerStart = handler => messageBus.on( BusEventType.WORKER_BEFORE_START, () =>
|
|
34
|
+
safeInvoke( handler, undefined, 'onBeforeWorkerStart' ) );
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
/** Listen to workflow start events, excludes catalog workflow */
|
|
37
|
+
export const onWorkflowStart = handler => messageBus.on( BusEventType.WORKFLOW_START, ( { id, name } ) =>
|
|
38
|
+
WORKFLOW_CATALOG !== name ? safeInvoke( handler, { id, name }, 'onWorkflowStart' ) : null );
|
|
39
|
+
|
|
40
|
+
/** Listen to workflow end events, excludes catalog workflow */
|
|
41
|
+
export const onWorkflowEnd = handler => messageBus.on( BusEventType.WORKFLOW_END, ( { id, name, duration } ) =>
|
|
42
|
+
WORKFLOW_CATALOG !== name ? safeInvoke( handler, { id, name, duration }, 'onWorkflowEnd' ) : null );
|
|
43
|
+
|
|
44
|
+
/** Listen to workflow error events, excludes catalog workflow */
|
|
45
|
+
export const onWorkflowError = handler => messageBus.on( BusEventType.WORKFLOW_ERROR, ( { id, name, duration, error } ) =>
|
|
46
|
+
WORKFLOW_CATALOG !== name ? safeInvoke( handler, { id, name, duration, error }, 'onWorkflowError' ) : null );
|
|
47
|
+
|
|
48
|
+
/** Generic listener for events emitted elsewhere (outside core) */
|
|
49
|
+
export const on = ( eventName, handler ) => messageBus.on( `external:${eventName}`, payload =>
|
|
50
|
+
safeInvoke( handler, payload, eventName ) );
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { BusEventType, WORKFLOW_CATALOG } from '#consts';
|
|
3
|
+
|
|
4
|
+
const logErrorMock = vi.hoisted( () => vi.fn() );
|
|
5
|
+
const createChildLoggerMock = vi.hoisted( () =>
|
|
6
|
+
vi.fn( () => ( { error: logErrorMock } ) )
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
const onHandlers = vi.hoisted( () => ( {} ) );
|
|
10
|
+
const messageBusMock = vi.hoisted( () => ( {
|
|
11
|
+
on: vi.fn( ( eventType, handler ) => {
|
|
12
|
+
onHandlers[eventType] = handler;
|
|
13
|
+
} )
|
|
14
|
+
} ) );
|
|
15
|
+
|
|
16
|
+
vi.mock( '#logger', () => ( { createChildLogger: createChildLoggerMock } ) );
|
|
17
|
+
vi.mock( '#bus', () => ( { messageBus: messageBusMock } ) );
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
on,
|
|
21
|
+
onBeforeWorkerStart,
|
|
22
|
+
onError,
|
|
23
|
+
onWorkflowEnd,
|
|
24
|
+
onWorkflowError,
|
|
25
|
+
onWorkflowStart
|
|
26
|
+
} from './index.js';
|
|
27
|
+
|
|
28
|
+
describe( 'hooks/index', () => {
|
|
29
|
+
beforeEach( () => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
Object.keys( onHandlers ).forEach( k => {
|
|
32
|
+
delete onHandlers[k];
|
|
33
|
+
} );
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
describe( 'onError', () => {
|
|
37
|
+
it( 'registers activity, workflow, and runtime error listeners', () => {
|
|
38
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
39
|
+
onError( handler );
|
|
40
|
+
|
|
41
|
+
expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, expect.any( Function ) );
|
|
42
|
+
expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.WORKFLOW_ERROR, expect.any( Function ) );
|
|
43
|
+
expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.RUNTIME_ERROR, expect.any( Function ) );
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
it( 'invokes handler with activity-shaped payload', async () => {
|
|
47
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
48
|
+
onError( handler );
|
|
49
|
+
|
|
50
|
+
const err = new Error( 'act-fail' );
|
|
51
|
+
await onHandlers[BusEventType.ACTIVITY_ERROR]( {
|
|
52
|
+
id: 'act-1',
|
|
53
|
+
name: 'wf#step',
|
|
54
|
+
workflowId: 'wf-run-1',
|
|
55
|
+
workflowName: 'wf',
|
|
56
|
+
error: err
|
|
57
|
+
} );
|
|
58
|
+
|
|
59
|
+
expect( handler ).toHaveBeenCalledWith( {
|
|
60
|
+
source: 'activity',
|
|
61
|
+
activityId: 'act-1',
|
|
62
|
+
activityName: 'wf#step',
|
|
63
|
+
workflowId: 'wf-run-1',
|
|
64
|
+
workflowName: 'wf',
|
|
65
|
+
error: err
|
|
66
|
+
} );
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
it( 'invokes handler with workflow-shaped payload', async () => {
|
|
70
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
71
|
+
onError( handler );
|
|
72
|
+
|
|
73
|
+
const err = new Error( 'wf-fail' );
|
|
74
|
+
await onHandlers[BusEventType.WORKFLOW_ERROR]( {
|
|
75
|
+
id: 'wf-run-2',
|
|
76
|
+
name: 'myWorkflow',
|
|
77
|
+
error: err
|
|
78
|
+
} );
|
|
79
|
+
|
|
80
|
+
expect( handler ).toHaveBeenCalledWith( {
|
|
81
|
+
source: 'workflow',
|
|
82
|
+
workflowId: 'wf-run-2',
|
|
83
|
+
workflowName: 'myWorkflow',
|
|
84
|
+
error: err
|
|
85
|
+
} );
|
|
86
|
+
} );
|
|
87
|
+
|
|
88
|
+
it( 'logs and does not rethrow when handler rejects', async () => {
|
|
89
|
+
const handler = vi.fn().mockRejectedValue( new Error( 'boom' ) );
|
|
90
|
+
onError( handler );
|
|
91
|
+
|
|
92
|
+
const error = new Error( 'rt' );
|
|
93
|
+
await onHandlers[BusEventType.RUNTIME_ERROR]( { error } );
|
|
94
|
+
|
|
95
|
+
expect( handler ).toHaveBeenCalledWith( { source: 'runtime', error } );
|
|
96
|
+
} );
|
|
97
|
+
} );
|
|
98
|
+
|
|
99
|
+
describe( 'onBeforeWorkerStart', () => {
|
|
100
|
+
it( 'registers and invokes handler with undefined payload', async () => {
|
|
101
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
102
|
+
onBeforeWorkerStart( handler );
|
|
103
|
+
|
|
104
|
+
expect( messageBusMock.on ).toHaveBeenCalledWith( BusEventType.WORKER_BEFORE_START, expect.any( Function ) );
|
|
105
|
+
await onHandlers[BusEventType.WORKER_BEFORE_START]();
|
|
106
|
+
|
|
107
|
+
expect( handler ).toHaveBeenCalledWith( undefined );
|
|
108
|
+
} );
|
|
109
|
+
} );
|
|
110
|
+
|
|
111
|
+
describe( 'onWorkflowStart', () => {
|
|
112
|
+
it( 'skips catalog workflow name', async () => {
|
|
113
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
114
|
+
onWorkflowStart( handler );
|
|
115
|
+
|
|
116
|
+
await Promise.resolve( onHandlers[BusEventType.WORKFLOW_START]( { id: '1', name: WORKFLOW_CATALOG } ) );
|
|
117
|
+
expect( handler ).not.toHaveBeenCalled();
|
|
118
|
+
|
|
119
|
+
await Promise.resolve( onHandlers[BusEventType.WORKFLOW_START]( { id: '2', name: 'myWorkflow' } ) );
|
|
120
|
+
expect( handler ).toHaveBeenCalledWith( { id: '2', name: 'myWorkflow' } );
|
|
121
|
+
} );
|
|
122
|
+
} );
|
|
123
|
+
|
|
124
|
+
describe( 'onWorkflowEnd', () => {
|
|
125
|
+
it( 'skips catalog workflow name', async () => {
|
|
126
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
127
|
+
onWorkflowEnd( handler );
|
|
128
|
+
|
|
129
|
+
await Promise.resolve( onHandlers[BusEventType.WORKFLOW_END]( {
|
|
130
|
+
id: '1',
|
|
131
|
+
name: WORKFLOW_CATALOG,
|
|
132
|
+
duration: 10
|
|
133
|
+
} ) );
|
|
134
|
+
expect( handler ).not.toHaveBeenCalled();
|
|
135
|
+
|
|
136
|
+
await Promise.resolve( onHandlers[BusEventType.WORKFLOW_END]( { id: '2', name: 'myWorkflow', duration: 5 } ) );
|
|
137
|
+
expect( handler ).toHaveBeenCalledWith( { id: '2', name: 'myWorkflow', duration: 5 } );
|
|
138
|
+
} );
|
|
139
|
+
} );
|
|
140
|
+
|
|
141
|
+
describe( 'onWorkflowError', () => {
|
|
142
|
+
it( 'skips catalog workflow name', async () => {
|
|
143
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
144
|
+
const err = new Error( 'wf' );
|
|
145
|
+
onWorkflowError( handler );
|
|
146
|
+
|
|
147
|
+
await Promise.resolve( onHandlers[BusEventType.WORKFLOW_ERROR]( {
|
|
148
|
+
id: '1',
|
|
149
|
+
name: WORKFLOW_CATALOG,
|
|
150
|
+
duration: 1,
|
|
151
|
+
error: err
|
|
152
|
+
} ) );
|
|
153
|
+
expect( handler ).not.toHaveBeenCalled();
|
|
154
|
+
|
|
155
|
+
await Promise.resolve( onHandlers[BusEventType.WORKFLOW_ERROR]( {
|
|
156
|
+
id: '2',
|
|
157
|
+
name: 'myWorkflow',
|
|
158
|
+
duration: 2,
|
|
159
|
+
error: err
|
|
160
|
+
} ) );
|
|
161
|
+
expect( handler ).toHaveBeenCalledWith( { id: '2', name: 'myWorkflow', duration: 2, error: err } );
|
|
162
|
+
} );
|
|
163
|
+
} );
|
|
164
|
+
|
|
165
|
+
describe( 'on', () => {
|
|
166
|
+
it( 'subscribes to external event channel and forwards payload', async () => {
|
|
167
|
+
const handler = vi.fn().mockResolvedValue( undefined );
|
|
168
|
+
on( 'myEvent', handler );
|
|
169
|
+
|
|
170
|
+
expect( messageBusMock.on ).toHaveBeenCalledWith( 'external:myEvent', expect.any( Function ) );
|
|
171
|
+
await onHandlers['external:myEvent']( { foo: 1 } );
|
|
172
|
+
|
|
173
|
+
expect( handler ).toHaveBeenCalledWith( { foo: 1 } );
|
|
174
|
+
} );
|
|
175
|
+
} );
|
|
176
|
+
} );
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { addEventAction, addEventActionWithContext, init, getDestinations } from './trace_engine.js';
|
|
2
|
+
import { EventAction } from './trace_consts.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Init method, if not called, no processors are attached and trace functions are dummy
|
|
@@ -6,66 +7,71 @@ import { addEventPhase, addEventPhaseWithContext, init, getDestinations } from '
|
|
|
6
7
|
export { init, getDestinations };
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Internal use only - adds
|
|
10
|
+
* Internal use only - adds an action with AsyncLocalStorage context resolution
|
|
10
11
|
*/
|
|
11
|
-
export {
|
|
12
|
+
export { addEventActionWithContext };
|
|
13
|
+
|
|
14
|
+
export { EventAction };
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* Trace nomenclature
|
|
15
18
|
*
|
|
16
19
|
* Trace - The collection of Events;
|
|
17
|
-
* Event - Any entry in the Trace file
|
|
18
|
-
*
|
|
20
|
+
* Event - Any entry in the Trace file must have both START and END/ERROR actions, plus any number of ADD_ATTR actions;
|
|
21
|
+
* Action - A specific part of an Event: START, END, ERROR, or ADD_ATTR;
|
|
19
22
|
*/
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Internal use only
|
|
23
26
|
*
|
|
24
|
-
*
|
|
27
|
+
* Creates a new event and appends it to the trace.
|
|
25
28
|
*
|
|
26
29
|
* @param {object} args
|
|
27
|
-
* @param {string} args.id - A unique id for the Event
|
|
30
|
+
* @param {string} args.id - A unique id for the Event.
|
|
28
31
|
* @param {string} args.kind - The kind of Event, like HTTP, DiskWrite, DBOp, etc.
|
|
29
|
-
* @param {string} args.name - The human
|
|
30
|
-
* @param {any} args.details -
|
|
31
|
-
* @param {string} args.parentId - The parent Event, used to build a
|
|
32
|
+
* @param {string} args.name - The human-friendly name of the Event: query, request, create.
|
|
33
|
+
* @param {any} args.details - Arbitrary data to add to this event, it will be used as the "input" field.
|
|
34
|
+
* @param {string} args.parentId - The parent Event, used to build a tree.
|
|
32
35
|
* @param {object} args.executionContext - The original execution context from the workflow
|
|
33
36
|
* @returns {void}
|
|
34
37
|
*/
|
|
35
|
-
export const addEventStart = options =>
|
|
38
|
+
export const addEventStart = options => addEventAction( EventAction.START, options );
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* Internal use only
|
|
39
42
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* It needs to use the same id of the start phase.
|
|
43
|
+
* Concludes an event, matching by its id.
|
|
43
44
|
*
|
|
44
45
|
* @param {object} args
|
|
45
|
-
* @param {string} args.id -
|
|
46
|
-
* @param {
|
|
47
|
-
* @param {string} args.name - The human friendly name of the Event: query, request, create.
|
|
48
|
-
* @param {any} args.details - All details attached to this Event Phase. DB queried records, HTTP response body.
|
|
49
|
-
* @param {string} args.parentId - The parent Event, used to build a three.
|
|
46
|
+
* @param {string} args.id - The id of the event to conclude.
|
|
47
|
+
* @param {any} args.details - Arbitrary data to add to the event; it is used as the "output" field.
|
|
50
48
|
* @param {object} args.executionContext - The original execution context from the workflow
|
|
51
49
|
* @returns {void}
|
|
52
50
|
*/
|
|
53
|
-
export const addEventEnd = options =>
|
|
51
|
+
export const addEventEnd = options => addEventAction( EventAction.END, options );
|
|
54
52
|
|
|
55
53
|
/**
|
|
56
54
|
* Internal use only
|
|
57
55
|
*
|
|
58
|
-
*
|
|
56
|
+
* Concludes an event with an error, matching by its id.
|
|
57
|
+
*
|
|
58
|
+
* @param {object} args
|
|
59
|
+
* @param {string} args.id - The id of the event to conclude.
|
|
60
|
+
* @param {any} args.details - Arbitrary data to add to the event; it is used as the "error" field.
|
|
61
|
+
* @param {object} args.executionContext - The original execution context from the workflow
|
|
62
|
+
* @returns {void}
|
|
63
|
+
*/
|
|
64
|
+
export const addEventError = options => addEventAction( EventAction.ERROR, options );
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Internal use only
|
|
59
68
|
*
|
|
60
|
-
*
|
|
69
|
+
* Adds an attribute to an event using its id.
|
|
61
70
|
*
|
|
62
71
|
* @param {object} args
|
|
63
|
-
* @param {string} args.id -
|
|
64
|
-
* @param {
|
|
65
|
-
* @param {string} args.name - The human friendly name of the Event: query, request, create.
|
|
66
|
-
* @param {any} args.details - All details attached to this Event Phase. DB queried records, HTTP response body.
|
|
67
|
-
* @param {string} args.parentId - The parent Event, used to build a three.
|
|
72
|
+
* @param {string} args.id - The id of the event to attach the attribute to.
|
|
73
|
+
* @param {object} args.details - The attribute to add to this event, must be in `{ name: string, value: any }` format.
|
|
68
74
|
* @param {object} args.executionContext - The original execution context from the workflow
|
|
69
75
|
* @returns {void}
|
|
70
76
|
*/
|
|
71
|
-
export const
|
|
77
|
+
export const addEventAttribute = options => addEventAction( EventAction.ADD_ATTR, options );
|