@outputai/core 0.8.1 → 0.8.2-next.0f9af4b.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 +9 -10
- package/src/bus.js +1 -1
- package/src/consts.js +5 -2
- package/src/helpers/component.js +12 -0
- package/src/helpers/component.spec.js +54 -0
- package/src/helpers/fetch.js +105 -0
- package/src/helpers/fetch.spec.js +203 -0
- package/src/helpers/function.js +15 -0
- package/src/helpers/function.spec.js +48 -0
- package/src/helpers/object.js +98 -0
- package/src/helpers/object.spec.js +377 -0
- package/src/helpers/promise.js +29 -0
- package/src/helpers/promise.spec.js +35 -0
- package/src/helpers/string.js +30 -0
- package/src/helpers/string.spec.js +64 -0
- package/src/interface/evaluator.js +14 -12
- package/src/interface/evaluator.spec.js +10 -6
- package/src/interface/index.d.ts +7 -6
- package/src/interface/index.js +2 -0
- package/src/interface/logger.d.ts +53 -0
- package/src/interface/logger.js +68 -0
- package/src/interface/logger.spec.js +138 -0
- package/src/interface/step.js +15 -12
- package/src/interface/step.spec.js +10 -6
- package/src/interface/webhook.d.ts +21 -2
- package/src/interface/workflow.js +85 -78
- package/src/interface/workflow.spec.js +11 -4
- package/src/internal_activities/index.js +38 -35
- package/src/internal_activities/index.spec.js +27 -4
- package/src/logger/development.js +2 -2
- package/src/logger/development.spec.js +19 -2
- package/src/logger/production.js +1 -1
- package/src/logger/production.spec.js +24 -5
- package/src/sdk/README.md +47 -0
- package/src/sdk/helpers/component_metadata.d.ts +17 -0
- package/src/sdk/helpers/component_metadata.js +6 -0
- package/src/sdk/helpers/component_metadata.spec.js +30 -0
- package/src/sdk/helpers/index.d.ts +12 -0
- package/src/sdk/helpers/index.js +3 -0
- package/src/sdk/helpers/objects.d.ts +51 -0
- package/src/sdk/helpers/objects.js +8 -0
- package/src/sdk/helpers/objects.spec.js +16 -0
- package/src/sdk/helpers/path.d.ts +11 -0
- package/src/sdk/helpers/path.js +32 -0
- package/src/{utils/resolve_invocation_dir.spec.js → sdk/helpers/path.spec.js} +9 -9
- package/src/sdk/runtime/context.d.ts +30 -0
- package/src/sdk/runtime/context.js +15 -0
- package/src/{activity_integration → sdk/runtime}/context.spec.js +5 -5
- package/src/sdk/runtime/events.d.ts +15 -0
- package/src/sdk/runtime/events.js +18 -0
- package/src/{activity_integration → sdk/runtime}/events.spec.js +8 -9
- package/src/sdk/runtime/index.d.ts +12 -0
- package/src/sdk/runtime/index.js +3 -0
- package/src/sdk/runtime/tracing.d.ts +46 -0
- package/src/sdk/runtime/tracing.js +11 -0
- package/src/tracing/processors/s3/redis_client.spec.js +0 -6
- package/src/tracing/processors/s3/s3_client.spec.js +0 -6
- package/src/tracing/trace_engine.js +1 -1
- package/src/worker/catalog_workflow/catalog_job.js +1 -1
- package/src/worker/catalog_workflow/index.spec.js +8 -11
- package/src/worker/configs.js +1 -1
- package/src/worker/connection_monitor.js +1 -1
- package/src/worker/global_functions.js +14 -0
- package/src/worker/global_functions.spec.js +55 -0
- package/src/worker/index.js +4 -1
- package/src/worker/index.spec.js +7 -0
- package/src/worker/interceptors/activity.js +2 -2
- package/src/worker/interceptors/workflow.js +3 -3
- package/src/worker/interceptors/workflow.spec.js +1 -1
- package/src/worker/loader/matchers.js +1 -1
- package/src/worker/loader/tools.js +1 -1
- package/src/worker/loader/tools.spec.js +1 -1
- package/src/worker/log_hooks.js +14 -0
- package/src/worker/log_hooks.spec.js +83 -2
- package/src/worker/sinks.js +7 -1
- package/src/worker/sinks.spec.js +203 -0
- package/src/activity_integration/context.d.ts +0 -23
- package/src/activity_integration/context.js +0 -18
- package/src/activity_integration/event_id_integration.spec.js +0 -52
- package/src/activity_integration/events.d.ts +0 -10
- package/src/activity_integration/events.js +0 -15
- package/src/activity_integration/index.d.ts +0 -9
- package/src/activity_integration/index.js +0 -3
- package/src/activity_integration/tracing.d.ts +0 -40
- package/src/activity_integration/tracing.js +0 -48
- package/src/utils/index.d.ts +0 -180
- package/src/utils/index.js +0 -2
- package/src/utils/resolve_invocation_dir.js +0 -34
- package/src/utils/utils.js +0 -334
- package/src/utils/utils.spec.js +0 -723
- /package/src/{internal_utils → helpers}/aggregations.js +0 -0
- /package/src/{internal_utils → helpers}/aggregations.spec.js +0 -0
- /package/src/{internal_utils → helpers}/errors.js +0 -0
- /package/src/{internal_utils → helpers}/errors.spec.js +0 -0
- /package/src/{internal_utils → helpers}/temporal_context.js +0 -0
- /package/src/{internal_utils → helpers}/temporal_context.spec.ts +0 -0
- /package/src/{internal_utils → helpers}/trace_info.js +0 -0
- /package/src/{internal_utils → helpers}/trace_info.spec.js +0 -0
- /package/src/{internal_utils → helpers}/workflow_context.js +0 -0
- /package/src/{internal_utils → helpers}/workflow_context.spec.js +0 -0
|
@@ -5,7 +5,7 @@ vi.mock( '#async_storage', () => ( {
|
|
|
5
5
|
Storage: { load: loadMock }
|
|
6
6
|
} ) );
|
|
7
7
|
|
|
8
|
-
describe( '
|
|
8
|
+
describe( 'Context.getActivityContext', () => {
|
|
9
9
|
beforeEach( () => {
|
|
10
10
|
vi.clearAllMocks();
|
|
11
11
|
vi.resetModules();
|
|
@@ -13,8 +13,8 @@ describe( 'getExecutionContext', () => {
|
|
|
13
13
|
|
|
14
14
|
it( 'returns null when no context is stored', async () => {
|
|
15
15
|
loadMock.mockReturnValue( undefined );
|
|
16
|
-
const {
|
|
17
|
-
expect(
|
|
16
|
+
const { Context } = await import( './index.js' );
|
|
17
|
+
expect( Context.getActivityContext() ).toBeNull();
|
|
18
18
|
} );
|
|
19
19
|
|
|
20
20
|
it( 'returns activity execution context from storage', async () => {
|
|
@@ -28,8 +28,8 @@ describe( 'getExecutionContext', () => {
|
|
|
28
28
|
activityInfo,
|
|
29
29
|
workflowFilename: '/workflows/myWorkflow.js'
|
|
30
30
|
} );
|
|
31
|
-
const {
|
|
32
|
-
expect(
|
|
31
|
+
const { Context } = await import( './index.js' );
|
|
32
|
+
expect( Context.getActivityContext() ).toEqual( {
|
|
33
33
|
activityInfo,
|
|
34
34
|
workflowFilename: '/workflows/myWorkflow.js'
|
|
35
35
|
} );
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools to interact with Events
|
|
3
|
+
*/
|
|
4
|
+
export declare const Event: {
|
|
5
|
+
/**
|
|
6
|
+
* Emits a custom event on the in-process message bus.
|
|
7
|
+
*
|
|
8
|
+
* When called inside an Output activity context, the framework automatically
|
|
9
|
+
* attaches `activityInfo`, `workflowDetails`, and `outputActivityKind` onto the emitted payload.
|
|
10
|
+
*
|
|
11
|
+
* @param eventName - The name of the event to emit
|
|
12
|
+
* @param payload - An optional payload to send to the event
|
|
13
|
+
*/
|
|
14
|
+
emit( eventName: string, payload?: object ): void;
|
|
15
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { messageBus } from '#bus';
|
|
2
|
+
import { Storage } from '#async_storage';
|
|
3
|
+
|
|
4
|
+
export const Event = {
|
|
5
|
+
emit: ( eventName, payload ) => {
|
|
6
|
+
const ctx = Storage.load();
|
|
7
|
+
|
|
8
|
+
messageBus.emit( `external:${eventName}`, {
|
|
9
|
+
...payload ?? {},
|
|
10
|
+
...( ctx && {
|
|
11
|
+
activityInfo: ctx.activityInfo,
|
|
12
|
+
workflowDetails: ctx.workflowDetails,
|
|
13
|
+
outputActivityKind: ctx.outputActivityKind
|
|
14
|
+
} )
|
|
15
|
+
} );
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
@@ -11,12 +11,11 @@ vi.mock( '#bus', () => ( {
|
|
|
11
11
|
messageBus: { emit: emitMock }
|
|
12
12
|
} ) );
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { Event } from './events.js';
|
|
15
15
|
|
|
16
|
-
// `eventId` stamping is the bus layer's responsibility (see bus.spec.js
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
describe( 'emitEvent', () => {
|
|
16
|
+
// `eventId` stamping is the bus layer's responsibility (see bus.spec.js).
|
|
17
|
+
// Assertions here use `objectContaining` so they don't have to know about that enrichment.
|
|
18
|
+
describe( 'Event.emit', () => {
|
|
20
19
|
beforeEach( () => {
|
|
21
20
|
vi.clearAllMocks();
|
|
22
21
|
} );
|
|
@@ -39,7 +38,7 @@ describe( 'emitEvent', () => {
|
|
|
39
38
|
outputActivityKind: 'step'
|
|
40
39
|
} );
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
Event.emit( 'cost:llm:request', { modelId: 'gpt-4o' } );
|
|
43
42
|
|
|
44
43
|
expect( emitMock ).toHaveBeenCalledWith( 'external:cost:llm:request', expect.objectContaining( {
|
|
45
44
|
activityInfo,
|
|
@@ -52,7 +51,7 @@ describe( 'emitEvent', () => {
|
|
|
52
51
|
it( 'emits payload without context when storage is missing', () => {
|
|
53
52
|
loadMock.mockReturnValue( undefined );
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
Event.emit( 'foo:bar', { x: 1 } );
|
|
56
55
|
|
|
57
56
|
expect( emitMock ).toHaveBeenCalledWith( 'external:foo:bar', { x: 1 } );
|
|
58
57
|
} );
|
|
@@ -75,7 +74,7 @@ describe( 'emitEvent', () => {
|
|
|
75
74
|
outputActivityKind: 'step'
|
|
76
75
|
} );
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
Event.emit( 'lifecycle:start' );
|
|
79
78
|
|
|
80
79
|
expect( emitMock ).toHaveBeenCalledWith( 'external:lifecycle:start', expect.objectContaining( {
|
|
81
80
|
activityInfo,
|
|
@@ -102,7 +101,7 @@ describe( 'emitEvent', () => {
|
|
|
102
101
|
outputActivityKind: 'step'
|
|
103
102
|
} );
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
Event.emit( 'cost:http:request', {
|
|
106
105
|
activityInfo: { activityId: 'should-be-overridden' },
|
|
107
106
|
workflowDetails: { workflowId: 'should-be-overridden' },
|
|
108
107
|
outputActivityKind: 'should-be-overridden',
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* > [!WARNING]
|
|
3
|
+
* > **Internal use only.** Not part of the public API; may change without notice.
|
|
4
|
+
*
|
|
5
|
+
* These are helpers for other SDK modules integration.
|
|
6
|
+
* These need Temporal activity runtime to work, as they access state, emit events and use node:* modules.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export * from './context.js';
|
|
11
|
+
export * from './events.js';
|
|
12
|
+
export * from './tracing.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Attribute } from '#trace_attribute';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tools to interact with Tracing
|
|
5
|
+
*/
|
|
6
|
+
export declare const Tracing: {
|
|
7
|
+
Attribute: typeof Attribute;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new event.
|
|
11
|
+
*
|
|
12
|
+
* @param args
|
|
13
|
+
* @param args.id - A unique id for the Event.
|
|
14
|
+
* @param args.kind - The kind of Event, like HTTP, DiskWrite, DBOp, etc.
|
|
15
|
+
* @param args.name - The human-friendly name of the Event: query, request, create.
|
|
16
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "input" field.
|
|
17
|
+
*/
|
|
18
|
+
addEventStart( args: { id: string; kind: string; name: string; details: unknown } ): void;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Concludes an event.
|
|
22
|
+
*
|
|
23
|
+
* @param args
|
|
24
|
+
* @param args.id - The id of the event to conclude.
|
|
25
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "output" field.
|
|
26
|
+
*/
|
|
27
|
+
addEventEnd( args: { id: string; details: unknown } ): void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Concludes an event with an error.
|
|
31
|
+
*
|
|
32
|
+
* @param args
|
|
33
|
+
* @param args.id - The id of the event to conclude.
|
|
34
|
+
* @param args.details - Arbitrary data to add to this event, it will be used as the "error" field.
|
|
35
|
+
*/
|
|
36
|
+
addEventError( args: { id: string; details: unknown } ): void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Adds an attribute to an event.
|
|
40
|
+
*
|
|
41
|
+
* @param args
|
|
42
|
+
* @param args.eventId - The id of the event to attach the attribute to.
|
|
43
|
+
* @param args.attribute - The attribute to attach to the event.
|
|
44
|
+
*/
|
|
45
|
+
addEventAttribute( args: { eventId: string; attribute: Attribute.Instance } ): void;
|
|
46
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { addEventActionWithContext as send, EventAction } from '#tracing';
|
|
2
|
+
|
|
3
|
+
import { Attribute } from '#trace_attribute';
|
|
4
|
+
|
|
5
|
+
export const Tracing = {
|
|
6
|
+
Attribute,
|
|
7
|
+
addEventStart: ( { id, kind, name, details } ) => send( EventAction.START, { kind, name, details, id } ),
|
|
8
|
+
addEventEnd: ( { id, details } ) => send( EventAction.END, { id, details } ),
|
|
9
|
+
addEventError: ( { id, details } ) => send( EventAction.ERROR, { id, details } ),
|
|
10
|
+
addEventAttribute: ( { eventId, attribute } ) => send( EventAction.ADD_ATTR, { id: eventId, details: attribute } )
|
|
11
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Storage } from '#async_storage';
|
|
2
2
|
import { EventEmitter } from 'node:events';
|
|
3
3
|
import { serializeError } from './tools/utils.js';
|
|
4
|
-
import { isStringboolTrue } from '#
|
|
4
|
+
import { isStringboolTrue } from '#helpers/string';
|
|
5
5
|
import * as localProcessor from './processors/local/index.js';
|
|
6
6
|
import * as s3Processor from './processors/s3/index.js';
|
|
7
7
|
import { ComponentType } from '#consts';
|
|
@@ -3,7 +3,7 @@ import { WorkflowExecutionAlreadyStartedError, WorkflowIdConflictPolicy } from '
|
|
|
3
3
|
import { WORKFLOW_CATALOG } from '#consts';
|
|
4
4
|
import { catalogId, taskQueue } from '../configs.js';
|
|
5
5
|
import { createChildLogger } from '#logger';
|
|
6
|
-
import { CancellablePromise } from '#
|
|
6
|
+
import { CancellablePromise } from '#helpers/promise';
|
|
7
7
|
|
|
8
8
|
const log = createChildLogger( 'Catalog' );
|
|
9
9
|
|
|
@@ -7,9 +7,6 @@ vi.mock( '#consts', () => ( {
|
|
|
7
7
|
METADATA_ACCESS_SYMBOL
|
|
8
8
|
} ) );
|
|
9
9
|
|
|
10
|
-
const setMetadata = ( target, values ) =>
|
|
11
|
-
Object.defineProperty( target, METADATA_ACCESS_SYMBOL, { value: values, writable: false, enumerable: false, configurable: false } );
|
|
12
|
-
|
|
13
10
|
describe( 'createCatalog', () => {
|
|
14
11
|
it( 'builds catalog with activities grouped by workflow path and returns Catalog with CatalogWorkflow entries', async () => {
|
|
15
12
|
const { createCatalog } = await import( './index.js' );
|
|
@@ -32,40 +29,40 @@ describe( 'createCatalog', () => {
|
|
|
32
29
|
];
|
|
33
30
|
|
|
34
31
|
const activity1 = () => {};
|
|
35
|
-
|
|
32
|
+
activity1[METADATA_ACCESS_SYMBOL] = {
|
|
36
33
|
name: 'A1',
|
|
37
34
|
path: '/flows/flow1#A1',
|
|
38
35
|
description: 'desc-a1',
|
|
39
36
|
inputSchema: z.object( { in: z.literal( 'a1' ) } ),
|
|
40
37
|
outputSchema: z.object( { out: z.literal( 'a1' ) } )
|
|
41
|
-
}
|
|
38
|
+
};
|
|
42
39
|
|
|
43
40
|
const activity2 = () => {};
|
|
44
|
-
|
|
41
|
+
activity2[METADATA_ACCESS_SYMBOL] = {
|
|
45
42
|
name: 'A2',
|
|
46
43
|
path: '/flows/flow1#A2',
|
|
47
44
|
description: 'desc-a2',
|
|
48
45
|
inputSchema: z.object( { in: z.literal( 'a2' ) } ),
|
|
49
46
|
outputSchema: z.object( { out: z.literal( 'a2' ) } )
|
|
50
|
-
}
|
|
47
|
+
};
|
|
51
48
|
|
|
52
49
|
const activity3 = () => {};
|
|
53
|
-
|
|
50
|
+
activity3[METADATA_ACCESS_SYMBOL] = {
|
|
54
51
|
name: 'B1',
|
|
55
52
|
path: '/flows/flow2#B1',
|
|
56
53
|
description: 'desc-b1',
|
|
57
54
|
inputSchema: z.object( { in: z.literal( 'b1' ) } ),
|
|
58
55
|
outputSchema: z.object( { out: z.literal( 'b1' ) } )
|
|
59
|
-
}
|
|
56
|
+
};
|
|
60
57
|
|
|
61
58
|
const activity4 = () => {};
|
|
62
|
-
|
|
59
|
+
activity4[METADATA_ACCESS_SYMBOL] = {
|
|
63
60
|
name: 'X',
|
|
64
61
|
path: '/other#X',
|
|
65
62
|
description: 'desc-x',
|
|
66
63
|
inputSchema: z.object( { in: z.literal( 'x' ) } ),
|
|
67
64
|
outputSchema: z.object( { out: z.literal( 'x' ) } )
|
|
68
|
-
}
|
|
65
|
+
};
|
|
69
66
|
|
|
70
67
|
const activities = {
|
|
71
68
|
'/flows/flow1#A1': activity1,
|
package/src/worker/configs.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { messageBus } from '#bus';
|
|
2
|
+
import { ACTIVITY_LOGGER_SYMBOL, BusEventType } from '#consts';
|
|
3
|
+
import { activityInfo as activityInfoFn } from '@temporalio/activity';
|
|
4
|
+
import { assignImmutableProperty } from '#helpers/object';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sets global functions on globalThis
|
|
8
|
+
*/
|
|
9
|
+
export const bindGlobalFunctions = () => {
|
|
10
|
+
/** Defines the activity logger function, accessible in activity context via logger interface */
|
|
11
|
+
assignImmutableProperty( globalThis, ACTIVITY_LOGGER_SYMBOL, ( { level, message, metadata } ) =>
|
|
12
|
+
messageBus.emit( BusEventType.ACTIVITY_LOG, { level, message, metadata, activityInfo: activityInfoFn() } )
|
|
13
|
+
);
|
|
14
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
ACTIVITY_LOGGER_SYMBOL,
|
|
5
|
+
BusEventType
|
|
6
|
+
} = vi.hoisted( () => ( {
|
|
7
|
+
ACTIVITY_LOGGER_SYMBOL: Symbol( 'activity_logger' ),
|
|
8
|
+
BusEventType: {
|
|
9
|
+
ACTIVITY_LOG: 'activity:log'
|
|
10
|
+
}
|
|
11
|
+
} ) );
|
|
12
|
+
|
|
13
|
+
const messageBusMock = vi.hoisted( () => ( {
|
|
14
|
+
emit: vi.fn()
|
|
15
|
+
} ) );
|
|
16
|
+
const activityInfoMock = vi.hoisted( () => vi.fn( () => ( {
|
|
17
|
+
activityId: 'act-1',
|
|
18
|
+
activityType: 'myStep'
|
|
19
|
+
} ) ) );
|
|
20
|
+
|
|
21
|
+
vi.mock( '#consts', () => ( { ACTIVITY_LOGGER_SYMBOL, BusEventType } ) );
|
|
22
|
+
vi.mock( '#bus', () => ( { messageBus: messageBusMock } ) );
|
|
23
|
+
vi.mock( '@temporalio/activity', () => ( {
|
|
24
|
+
activityInfo: activityInfoMock
|
|
25
|
+
} ) );
|
|
26
|
+
|
|
27
|
+
describe( 'worker/global_functions', () => {
|
|
28
|
+
beforeEach( () => {
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
delete globalThis[ACTIVITY_LOGGER_SYMBOL];
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'binds activity logger global function that emits activity log events with metadata', async () => {
|
|
34
|
+
const { bindGlobalFunctions } = await import( './global_functions.js' );
|
|
35
|
+
const metadata = { requestId: 'req-1' };
|
|
36
|
+
|
|
37
|
+
bindGlobalFunctions();
|
|
38
|
+
globalThis[ACTIVITY_LOGGER_SYMBOL]( {
|
|
39
|
+
level: 'debug',
|
|
40
|
+
message: 'activity detail',
|
|
41
|
+
metadata
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
expect( activityInfoMock ).toHaveBeenCalledTimes( 1 );
|
|
45
|
+
expect( messageBusMock.emit ).toHaveBeenCalledWith( BusEventType.ACTIVITY_LOG, {
|
|
46
|
+
level: 'debug',
|
|
47
|
+
message: 'activity detail',
|
|
48
|
+
metadata,
|
|
49
|
+
activityInfo: {
|
|
50
|
+
activityId: 'act-1',
|
|
51
|
+
activityType: 'myStep'
|
|
52
|
+
}
|
|
53
|
+
} );
|
|
54
|
+
} );
|
|
55
|
+
} );
|
package/src/worker/index.js
CHANGED
|
@@ -17,7 +17,8 @@ import { messageBus } from '#bus';
|
|
|
17
17
|
import { BusEventType } from '#consts';
|
|
18
18
|
import { setupTelemetry } from './telemetry.js';
|
|
19
19
|
import { TemporalConnectionMonitor } from './connection_monitor.js';
|
|
20
|
-
import {
|
|
20
|
+
import { bindGlobalFunctions } from './global_functions.js';
|
|
21
|
+
import { runOnce } from '#helpers/function';
|
|
21
22
|
|
|
22
23
|
import './log_hooks.js';
|
|
23
24
|
|
|
@@ -76,6 +77,8 @@ const execute = async () => {
|
|
|
76
77
|
log.info( 'Using gRPC proxy', { targetHost: grpcProxy } );
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
bindGlobalFunctions();
|
|
81
|
+
|
|
79
82
|
state.connection = await NativeConnection.connect( { address, tls: Boolean( apiKey ), apiKey, proxy } );
|
|
80
83
|
|
|
81
84
|
log.info( 'Creating connection monitor...' );
|
package/src/worker/index.spec.js
CHANGED
|
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
catalogJobInstance,
|
|
5
|
+
bindGlobalFunctionsMock,
|
|
5
6
|
configValues,
|
|
6
7
|
connectionMonitorInstance,
|
|
7
8
|
createCatalogMock,
|
|
@@ -98,6 +99,7 @@ const {
|
|
|
98
99
|
|
|
99
100
|
return {
|
|
100
101
|
catalogJobInstance,
|
|
102
|
+
bindGlobalFunctionsMock: vi.fn(),
|
|
101
103
|
configValues,
|
|
102
104
|
connectionMonitorInstance,
|
|
103
105
|
createCatalogMock: vi.fn().mockReturnValue( { workflows: [], activities: {} } ),
|
|
@@ -145,6 +147,7 @@ vi.mock( './interceptors/index.js', () => ( { initInterceptors: initInterceptors
|
|
|
145
147
|
vi.mock( './proxy.js', () => ( { bootstrapFetchProxy: vi.fn() } ) );
|
|
146
148
|
vi.mock( './telemetry.js', () => ( { setupTelemetry: setupTelemetryMock } ) );
|
|
147
149
|
vi.mock( './interruption.js', () => ( { setupInterruptionHandler: setupInterruptionHandlerMock } ) );
|
|
150
|
+
vi.mock( './global_functions.js', () => ( { bindGlobalFunctions: bindGlobalFunctionsMock } ) );
|
|
148
151
|
vi.mock( './connection_monitor.js', () => ( {
|
|
149
152
|
TemporalConnectionMonitor: vi.fn( function () {
|
|
150
153
|
return connectionMonitorInstance;
|
|
@@ -222,6 +225,10 @@ describe( 'worker/index', () => {
|
|
|
222
225
|
apiKey: undefined,
|
|
223
226
|
proxy: undefined
|
|
224
227
|
} );
|
|
228
|
+
expect( bindGlobalFunctionsMock ).toHaveBeenCalledTimes( 1 );
|
|
229
|
+
expect( bindGlobalFunctionsMock.mock.invocationCallOrder[0] ).toBeLessThan(
|
|
230
|
+
NativeConnection.connect.mock.invocationCallOrder[0]
|
|
231
|
+
);
|
|
225
232
|
expect( TemporalConnectionMonitor ).toHaveBeenCalledWith( mockConnection );
|
|
226
233
|
expect( CatalogJob ).toHaveBeenCalledWith( {
|
|
227
234
|
connection: mockConnection,
|
|
@@ -5,8 +5,8 @@ import { headersToObject } from './headers.js';
|
|
|
5
5
|
import { ACTIVITY_WRAPPER_VERSION_FIELD, BusEventType, METADATA_ACCESS_SYMBOL } from '#consts';
|
|
6
6
|
import { activityHeartbeatEnabled, activityHeartbeatIntervalMs } from '../configs.js';
|
|
7
7
|
import { messageBus } from '#bus';
|
|
8
|
-
import { aggregateAttributes } from '#
|
|
9
|
-
import { buildApplicationFailureWithDetails } from '#
|
|
8
|
+
import { aggregateAttributes } from '#helpers/aggregations';
|
|
9
|
+
import { buildApplicationFailureWithDetails } from '#helpers/errors';
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
12
|
This interceptor wraps every activity execution with cross-cutting concerns:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
|
|
2
2
|
import { workflowInfo, proxySinks, ContinueAsNew, isCancellation } from '@temporalio/workflow';
|
|
3
3
|
import { memoToHeaders } from './headers.js';
|
|
4
|
-
import { deepMerge } from '#
|
|
5
|
-
import { buildApplicationFailureWithDetails } from '#
|
|
4
|
+
import { deepMerge } from '#helpers/object';
|
|
5
|
+
import { buildApplicationFailureWithDetails } from '#helpers/errors';
|
|
6
6
|
import { METADATA_ACCESS_SYMBOL, WorkflowSpecialOutput } from '#consts';
|
|
7
|
-
import { createWorkflowDetails } from '#
|
|
7
|
+
import { createWorkflowDetails } from '#helpers/temporal_context';
|
|
8
8
|
|
|
9
9
|
// this is a dynamic generated file with activity configs overwrites
|
|
10
10
|
import stepOptions from '../temp/__activity_options.js';
|
|
@@ -54,7 +54,7 @@ const memoToHeadersMock = vi.fn( memo => ( memo ? { ...memo, __asHeaders: true }
|
|
|
54
54
|
vi.mock( './headers.js', () => ( { memoToHeaders: ( ...args ) => memoToHeadersMock( ...args ) } ) );
|
|
55
55
|
|
|
56
56
|
const deepMergeMock = vi.fn( ( a, b ) => ( { ...( a || {} ), ...( b || {} ) } ) );
|
|
57
|
-
vi.mock( '#
|
|
57
|
+
vi.mock( '#helpers/object', () => ( { deepMerge: ( ...args ) => deepMergeMock( ...args ) } ) );
|
|
58
58
|
|
|
59
59
|
const stepOptionsDefault = {};
|
|
60
60
|
vi.mock( '../temp/__activity_options.js', () => ( { default: stepOptionsDefault } ) );
|
|
@@ -252,7 +252,7 @@ export const hashSourceCode = async rootDir => {
|
|
|
252
252
|
try {
|
|
253
253
|
const { hash } = await hashElement( rootDir, {
|
|
254
254
|
folders: {
|
|
255
|
-
exclude: [ '.*', 'node_modules', 'test_coverage', 'vendor', 'test', 'logs', '
|
|
255
|
+
exclude: [ '.*', 'node_modules', 'test_coverage', 'vendor', 'test', 'logs', 'temp' ],
|
|
256
256
|
ignoreRootName: true
|
|
257
257
|
},
|
|
258
258
|
files: {
|
|
@@ -574,7 +574,7 @@ describe( 'hashSourceCode', () => {
|
|
|
574
574
|
|
|
575
575
|
// The cruft tree is identical source plus large excluded artifacts that
|
|
576
576
|
// boot must not walk: local trace dumps under logs/ and build output under dist/.
|
|
577
|
-
for ( const excluded of [ 'logs', 'logs/runs', '
|
|
577
|
+
for ( const excluded of [ 'logs', 'logs/runs', 'node_modules' ] ) {
|
|
578
578
|
const dir = join( withCruft, excluded );
|
|
579
579
|
mkdirSync( dir, { recursive: true } );
|
|
580
580
|
writeFileSync( join( dir, 'dump.json' ), JSON.stringify( { blob: 'x'.repeat( 50_000 ) } ) );
|
package/src/worker/log_hooks.js
CHANGED
|
@@ -47,6 +47,13 @@ messageBus.on( BusEventType.ACTIVITY_ERROR, ( { activityInfo, outputActivityKind
|
|
|
47
47
|
} )
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
+
messageBus.on( BusEventType.ACTIVITY_LOG, ( { level, message, metadata, activityInfo } ) =>
|
|
51
|
+
activityLog[level]( message, {
|
|
52
|
+
...metadata ?? {},
|
|
53
|
+
...serializedActivityFields( activityInfo )
|
|
54
|
+
} )
|
|
55
|
+
);
|
|
56
|
+
|
|
50
57
|
/*
|
|
51
58
|
╔═════════════════╗
|
|
52
59
|
║ Workflow events ║
|
|
@@ -82,3 +89,10 @@ messageBus.on( BusEventType.WORKFLOW_ERROR, ( { workflowDetails, error } ) =>
|
|
|
82
89
|
error: error.message
|
|
83
90
|
} )
|
|
84
91
|
);
|
|
92
|
+
|
|
93
|
+
messageBus.on( BusEventType.WORKFLOW_LOG, ( { level, message, metadata, workflowDetails } ) =>
|
|
94
|
+
workflowLog[level]( message, {
|
|
95
|
+
...metadata ?? {},
|
|
96
|
+
...serializeWorkflowFields( workflowDetails )
|
|
97
|
+
} )
|
|
98
|
+
);
|
|
@@ -6,8 +6,17 @@ import {
|
|
|
6
6
|
WORKFLOW_CATALOG
|
|
7
7
|
} from '#consts';
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const createLoggerMock = vi.hoisted( () => () => ( {
|
|
10
|
+
error: vi.fn(),
|
|
11
|
+
warn: vi.fn(),
|
|
12
|
+
info: vi.fn(),
|
|
13
|
+
http: vi.fn(),
|
|
14
|
+
verbose: vi.fn(),
|
|
15
|
+
debug: vi.fn(),
|
|
16
|
+
silly: vi.fn()
|
|
17
|
+
} ) );
|
|
18
|
+
const activityLogMock = vi.hoisted( () => createLoggerMock() );
|
|
19
|
+
const workflowLogMock = vi.hoisted( () => createLoggerMock() );
|
|
11
20
|
const createChildLoggerMock = vi.hoisted( () =>
|
|
12
21
|
vi.fn( name => ( name === 'Activity' ? activityLogMock : workflowLogMock ) )
|
|
13
22
|
);
|
|
@@ -125,6 +134,44 @@ describe( 'log_hooks', () => {
|
|
|
125
134
|
|
|
126
135
|
expect( activityLogMock.error ).not.toHaveBeenCalled();
|
|
127
136
|
} );
|
|
137
|
+
|
|
138
|
+
it( 'ACTIVITY_LOG logs dynamic levels with metadata and serialized activity fields', () => {
|
|
139
|
+
onHandlers[BusEventType.ACTIVITY_LOG]( {
|
|
140
|
+
activityInfo,
|
|
141
|
+
level: 'debug',
|
|
142
|
+
message: 'activity detail',
|
|
143
|
+
metadata: {
|
|
144
|
+
custom: 'value',
|
|
145
|
+
workflowId: 'metadata-workflow-id'
|
|
146
|
+
}
|
|
147
|
+
} );
|
|
148
|
+
|
|
149
|
+
expect( activityLogMock.debug ).toHaveBeenCalledTimes( 1 );
|
|
150
|
+
expect( activityLogMock.debug ).toHaveBeenCalledWith( 'activity detail', {
|
|
151
|
+
custom: 'value',
|
|
152
|
+
activityId: 'act-1',
|
|
153
|
+
activityType: 'myWorkflow#myStep',
|
|
154
|
+
workflowId: 'wf-1',
|
|
155
|
+
workflowType: 'myWorkflow',
|
|
156
|
+
runId: 'run-1'
|
|
157
|
+
} );
|
|
158
|
+
} );
|
|
159
|
+
|
|
160
|
+
it( 'ACTIVITY_LOG accepts omitted metadata', () => {
|
|
161
|
+
onHandlers[BusEventType.ACTIVITY_LOG]( {
|
|
162
|
+
activityInfo,
|
|
163
|
+
level: 'info',
|
|
164
|
+
message: 'activity detail'
|
|
165
|
+
} );
|
|
166
|
+
|
|
167
|
+
expect( activityLogMock.info ).toHaveBeenCalledWith( 'activity detail', {
|
|
168
|
+
activityId: 'act-1',
|
|
169
|
+
activityType: 'myWorkflow#myStep',
|
|
170
|
+
workflowId: 'wf-1',
|
|
171
|
+
workflowType: 'myWorkflow',
|
|
172
|
+
runId: 'run-1'
|
|
173
|
+
} );
|
|
174
|
+
} );
|
|
128
175
|
} );
|
|
129
176
|
|
|
130
177
|
describe( 'workflow events', () => {
|
|
@@ -210,5 +257,39 @@ describe( 'log_hooks', () => {
|
|
|
210
257
|
|
|
211
258
|
expect( workflowLogMock.error ).not.toHaveBeenCalled();
|
|
212
259
|
} );
|
|
260
|
+
|
|
261
|
+
it( 'WORKFLOW_LOG logs dynamic levels with metadata and serialized workflow fields', () => {
|
|
262
|
+
onHandlers[BusEventType.WORKFLOW_LOG]( {
|
|
263
|
+
workflowDetails,
|
|
264
|
+
level: 'warn',
|
|
265
|
+
message: 'workflow detail',
|
|
266
|
+
metadata: {
|
|
267
|
+
custom: 'value',
|
|
268
|
+
runId: 'metadata-run-id'
|
|
269
|
+
}
|
|
270
|
+
} );
|
|
271
|
+
|
|
272
|
+
expect( workflowLogMock.warn ).toHaveBeenCalledTimes( 1 );
|
|
273
|
+
expect( workflowLogMock.warn ).toHaveBeenCalledWith( 'workflow detail', {
|
|
274
|
+
custom: 'value',
|
|
275
|
+
workflowId: 'wf-1',
|
|
276
|
+
workflowType: 'myWorkflow',
|
|
277
|
+
runId: 'run-1'
|
|
278
|
+
} );
|
|
279
|
+
} );
|
|
280
|
+
|
|
281
|
+
it( 'WORKFLOW_LOG accepts omitted metadata', () => {
|
|
282
|
+
onHandlers[BusEventType.WORKFLOW_LOG]( {
|
|
283
|
+
workflowDetails,
|
|
284
|
+
level: 'info',
|
|
285
|
+
message: 'workflow detail'
|
|
286
|
+
} );
|
|
287
|
+
|
|
288
|
+
expect( workflowLogMock.info ).toHaveBeenCalledWith( 'workflow detail', {
|
|
289
|
+
workflowId: 'wf-1',
|
|
290
|
+
workflowType: 'myWorkflow',
|
|
291
|
+
runId: 'run-1'
|
|
292
|
+
} );
|
|
293
|
+
} );
|
|
213
294
|
} );
|
|
214
295
|
} );
|