@outputai/core 0.8.2-next.2da7213.0 → 0.8.2-next.4b5c049.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 -5
- package/src/consts.js +5 -2
- package/src/interface/evaluator.js +1 -1
- 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 +1 -1
- package/src/interface/workflow.js +2 -1
- package/src/internal_activities/index.js +2 -1
- package/src/internal_utils/component.js +9 -0
- package/src/logger/development.js +1 -1
- package/src/logger/development.spec.js +18 -1
- 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 +11 -0
- package/src/sdk/helpers/index.js +2 -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/utils/index.d.ts +0 -32
- package/src/utils/index.js +0 -1
- package/src/utils/utils.js +0 -27
- package/src/worker/global_functions.js +20 -0
- package/src/worker/global_functions.spec.js +55 -0
- package/src/worker/index.js +3 -0
- package/src/worker/index.spec.js +7 -0
- 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 +6 -0
- 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/resolve_invocation_dir.js +0 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/core",
|
|
3
|
-
"version": "0.8.2-next.
|
|
3
|
+
"version": "0.8.2-next.4b5c049.0",
|
|
4
4
|
"description": "The core module of the output framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -12,9 +12,13 @@
|
|
|
12
12
|
"types": "./src/hooks/index.d.ts",
|
|
13
13
|
"import": "./src/hooks/index.js"
|
|
14
14
|
},
|
|
15
|
-
"./
|
|
16
|
-
"types": "./src/
|
|
17
|
-
"import": "./src/
|
|
15
|
+
"./sdk/helpers": {
|
|
16
|
+
"types": "./src/sdk/helpers/index.d.ts",
|
|
17
|
+
"import": "./src/sdk/helpers/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./sdk/runtime": {
|
|
20
|
+
"types": "./src/sdk/runtime/index.d.ts",
|
|
21
|
+
"import": "./src/sdk/runtime/index.js"
|
|
18
22
|
},
|
|
19
23
|
"./sdk_utils": {
|
|
20
24
|
"types": "./src/utils/index.d.ts",
|
|
@@ -48,7 +52,7 @@
|
|
|
48
52
|
"json-stream-stringify": "3.1.6",
|
|
49
53
|
"redis": "5.12.1",
|
|
50
54
|
"stacktrace-parser": "0.1.11",
|
|
51
|
-
"undici": "8.
|
|
55
|
+
"undici": "8.5.0",
|
|
52
56
|
"winston": "3.19.0"
|
|
53
57
|
},
|
|
54
58
|
"peerDependencies": {
|
package/src/consts.js
CHANGED
|
@@ -2,6 +2,7 @@ export const ACTIVITY_GET_TRACE_DESTINATIONS = '__internal#getTraceDestinations'
|
|
|
2
2
|
export const ACTIVITY_OPTIONS_FILENAME = '__activity_options.js';
|
|
3
3
|
export const ACTIVITY_SEND_HTTP_REQUEST = '__internal#sendHttpRequest';
|
|
4
4
|
export const ACTIVITY_WRAPPER_VERSION_FIELD = '__output_activity_wrapper_version';
|
|
5
|
+
export const ACTIVITY_LOGGER_SYMBOL = Symbol.for( '__activity_logger' );
|
|
5
6
|
export const METADATA_ACCESS_SYMBOL = Symbol( '__metadata' );
|
|
6
7
|
export const SHARED_STEP_PREFIX = '$shared';
|
|
7
8
|
export const WORKFLOW_CATALOG = '$catalog';
|
|
@@ -24,13 +25,15 @@ export const LifecycleEvent = {
|
|
|
24
25
|
export const BusEventType = {
|
|
25
26
|
WORKER_BEFORE_START: 'worker:before_start',
|
|
26
27
|
|
|
27
|
-
WORKFLOW_START: 'workflow:start',
|
|
28
28
|
WORKFLOW_END: 'workflow:end',
|
|
29
29
|
WORKFLOW_ERROR: 'workflow:error',
|
|
30
|
+
WORKFLOW_LOG: 'workflow:log',
|
|
31
|
+
WORKFLOW_START: 'workflow:start',
|
|
30
32
|
|
|
31
|
-
ACTIVITY_START: 'activity:start',
|
|
32
33
|
ACTIVITY_END: 'activity:end',
|
|
33
34
|
ACTIVITY_ERROR: 'activity:error',
|
|
35
|
+
ACTIVITY_LOG: 'activity:log',
|
|
36
|
+
ACTIVITY_START: 'activity:start',
|
|
34
37
|
|
|
35
38
|
RUNTIME_ERROR: 'runtime_error'
|
|
36
39
|
};
|
package/src/interface/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Exports all public methods from the interface
|
|
3
3
|
*/
|
|
4
|
-
export * from './webhook.
|
|
5
|
-
export * from './workflow_utils.
|
|
6
|
-
export * from './step.
|
|
7
|
-
export * from './evaluator.
|
|
8
|
-
export * from './workflow.
|
|
9
|
-
export * from './evaluation_result.
|
|
4
|
+
export * from './webhook.js';
|
|
5
|
+
export * from './workflow_utils.js';
|
|
6
|
+
export * from './step.js';
|
|
7
|
+
export * from './evaluator.js';
|
|
8
|
+
export * from './workflow.js';
|
|
9
|
+
export * from './evaluation_result.js';
|
|
10
|
+
export * as Logger from './logger.js';
|
package/src/interface/index.js
CHANGED
|
@@ -10,11 +10,13 @@ import { step } from './step.js';
|
|
|
10
10
|
import { workflow } from './workflow.js';
|
|
11
11
|
import { executeInParallel } from './workflow_utils.js';
|
|
12
12
|
import { sendHttpRequest, sendPostRequestAndAwaitWebhook } from './webhook.js';
|
|
13
|
+
import * as Logger from './logger.js';
|
|
13
14
|
|
|
14
15
|
export {
|
|
15
16
|
evaluator,
|
|
16
17
|
step,
|
|
17
18
|
workflow,
|
|
19
|
+
Logger,
|
|
18
20
|
EvaluationNumberResult,
|
|
19
21
|
EvaluationStringResult,
|
|
20
22
|
EvaluationBooleanResult,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Additional structured fields attached to a log record.
|
|
3
|
+
*/
|
|
4
|
+
export type LogMetadata = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Log an error (level 0)
|
|
8
|
+
* @param message Log message
|
|
9
|
+
* @param metadata Additional information to be displayed
|
|
10
|
+
*/
|
|
11
|
+
export declare function error( message: string, metadata?: LogMetadata ) : void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Log a warn (level 1)
|
|
15
|
+
* @param message Log message
|
|
16
|
+
* @param metadata Additional information to be displayed
|
|
17
|
+
*/
|
|
18
|
+
export declare function warn( message: string, metadata?: LogMetadata ) : void;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Log an info (level 2)
|
|
22
|
+
* @param message Log message
|
|
23
|
+
* @param metadata Additional information to be displayed
|
|
24
|
+
*/
|
|
25
|
+
export declare function info( message: string, metadata?: LogMetadata ) : void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Log http (level 3)
|
|
29
|
+
* @param message Log message
|
|
30
|
+
* @param metadata Additional information to be displayed
|
|
31
|
+
*/
|
|
32
|
+
export declare function http( message: string, metadata?: LogMetadata ) : void;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Log verbose (level 4)
|
|
36
|
+
* @param message Log message
|
|
37
|
+
* @param metadata Additional information to be displayed
|
|
38
|
+
*/
|
|
39
|
+
export declare function verbose( message: string, metadata?: LogMetadata ) : void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Log debug (level 5)
|
|
43
|
+
* @param message Log message
|
|
44
|
+
* @param metadata Additional information to be displayed
|
|
45
|
+
*/
|
|
46
|
+
export declare function debug( message: string, metadata?: LogMetadata ) : void;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Log silly (level 6)
|
|
50
|
+
* @param message Log message
|
|
51
|
+
* @param metadata Additional information to be displayed
|
|
52
|
+
*/
|
|
53
|
+
export declare function silly( message: string, metadata?: LogMetadata ) : void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { inWorkflowContext } from '@temporalio/workflow';
|
|
2
|
+
import { proxySinks } from '@temporalio/workflow';
|
|
3
|
+
import { ACTIVITY_LOGGER_SYMBOL } from '#consts';
|
|
4
|
+
import { isPlainObject } from '#utils';
|
|
5
|
+
|
|
6
|
+
const reservedMetadataFields = new Set( [
|
|
7
|
+
// Winston fields
|
|
8
|
+
'label',
|
|
9
|
+
'level',
|
|
10
|
+
'message',
|
|
11
|
+
'metadata',
|
|
12
|
+
'namespace',
|
|
13
|
+
'splat',
|
|
14
|
+
'stack',
|
|
15
|
+
'timestamp',
|
|
16
|
+
// reserved fields enriched by us
|
|
17
|
+
'workflowId',
|
|
18
|
+
'workflowType',
|
|
19
|
+
'runId',
|
|
20
|
+
'activityId',
|
|
21
|
+
'activityType',
|
|
22
|
+
'service',
|
|
23
|
+
'environment'
|
|
24
|
+
] );
|
|
25
|
+
|
|
26
|
+
// This is inoffensive and can be used outside workflow sandbox
|
|
27
|
+
const sinks = proxySinks();
|
|
28
|
+
|
|
29
|
+
// Convert npm log levels to console levels
|
|
30
|
+
const levelToConsole = {
|
|
31
|
+
error: 'error',
|
|
32
|
+
warn: 'warn',
|
|
33
|
+
info: 'info',
|
|
34
|
+
http: 'log',
|
|
35
|
+
verbose: 'log',
|
|
36
|
+
debug: 'debug',
|
|
37
|
+
silly: 'log'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Drops reserved keys from object */
|
|
41
|
+
const removeReservedFields = obj => Object.fromEntries( Object.entries( obj ).filter( ( [ k ] ) => !reservedMetadataFields.has( k ) ) );
|
|
42
|
+
|
|
43
|
+
const log = ( level, message, metadata ) => {
|
|
44
|
+
const sanitized = {
|
|
45
|
+
message: String( message ),
|
|
46
|
+
...( isPlainObject( metadata ) && { metadata: removeReservedFields( metadata ) } )
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// When inside workflow, use sinks to send logs out
|
|
50
|
+
if ( inWorkflowContext() ) {
|
|
51
|
+
sinks.workflow.log( { level, ...sanitized } );
|
|
52
|
+
// When inside activities, use the global function to ship logs
|
|
53
|
+
} else if ( typeof globalThis[ACTIVITY_LOGGER_SYMBOL] === 'function' ) {
|
|
54
|
+
globalThis[ACTIVITY_LOGGER_SYMBOL]( { level, ...sanitized } );
|
|
55
|
+
// This fallback is used on unit tests
|
|
56
|
+
} else {
|
|
57
|
+
console[levelToConsole[level]]( sanitized.message, sanitized.metadata );
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Winston uses npm levels by default: https://github.com/winstonjs/winston#logging-levels
|
|
62
|
+
export const error = log.bind( null, 'error' );
|
|
63
|
+
export const warn = log.bind( null, 'warn' );
|
|
64
|
+
export const info = log.bind( null, 'info' );
|
|
65
|
+
export const http = log.bind( null, 'http' );
|
|
66
|
+
export const verbose = log.bind( null, 'verbose' );
|
|
67
|
+
export const debug = log.bind( null, 'debug' );
|
|
68
|
+
export const silly = log.bind( null, 'silly' );
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { ACTIVITY_LOGGER_SYMBOL } from '#consts';
|
|
3
|
+
|
|
4
|
+
const workflowLogMock = vi.fn();
|
|
5
|
+
const proxySinksMock = vi.fn( () => ( {
|
|
6
|
+
workflow: {
|
|
7
|
+
log: workflowLogMock
|
|
8
|
+
}
|
|
9
|
+
} ) );
|
|
10
|
+
const inWorkflowContextMock = vi.fn( () => false );
|
|
11
|
+
|
|
12
|
+
vi.mock( '@temporalio/workflow', () => ( {
|
|
13
|
+
inWorkflowContext: inWorkflowContextMock,
|
|
14
|
+
proxySinks: proxySinksMock
|
|
15
|
+
} ) );
|
|
16
|
+
|
|
17
|
+
const logLevels = [
|
|
18
|
+
'error',
|
|
19
|
+
'warn',
|
|
20
|
+
'info',
|
|
21
|
+
'http',
|
|
22
|
+
'verbose',
|
|
23
|
+
'debug',
|
|
24
|
+
'silly'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const consoleMethodsByLevel = {
|
|
28
|
+
error: 'error',
|
|
29
|
+
warn: 'warn',
|
|
30
|
+
info: 'info',
|
|
31
|
+
http: 'log',
|
|
32
|
+
verbose: 'log',
|
|
33
|
+
debug: 'debug',
|
|
34
|
+
silly: 'log'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const loadLogger = async () => import( './logger.js' );
|
|
38
|
+
|
|
39
|
+
describe( 'interface/logger', () => {
|
|
40
|
+
beforeEach( () => {
|
|
41
|
+
vi.clearAllMocks();
|
|
42
|
+
inWorkflowContextMock.mockReturnValue( false );
|
|
43
|
+
delete globalThis[ACTIVITY_LOGGER_SYMBOL];
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
afterEach( () => {
|
|
47
|
+
delete globalThis[ACTIVITY_LOGGER_SYMBOL];
|
|
48
|
+
vi.restoreAllMocks();
|
|
49
|
+
} );
|
|
50
|
+
|
|
51
|
+
it( 'proxies sinks once when the module is loaded', async () => {
|
|
52
|
+
await loadLogger();
|
|
53
|
+
|
|
54
|
+
expect( proxySinksMock ).toHaveBeenCalledTimes( 1 );
|
|
55
|
+
} );
|
|
56
|
+
|
|
57
|
+
it( 'logs every level through workflow sinks inside workflow context', async () => {
|
|
58
|
+
const logger = await loadLogger();
|
|
59
|
+
inWorkflowContextMock.mockReturnValue( true );
|
|
60
|
+
|
|
61
|
+
logLevels.forEach( level => {
|
|
62
|
+
logger[level]( `${level} message`, { requestId: level } );
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
logLevels.forEach( ( level, index ) => {
|
|
66
|
+
const payload = {
|
|
67
|
+
level,
|
|
68
|
+
message: `${level} message`,
|
|
69
|
+
metadata: { requestId: level }
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
expect( workflowLogMock ).toHaveBeenNthCalledWith( index + 1, payload );
|
|
73
|
+
} );
|
|
74
|
+
} );
|
|
75
|
+
|
|
76
|
+
it( 'sanitizes messages and metadata before logging', async () => {
|
|
77
|
+
const logger = await loadLogger();
|
|
78
|
+
inWorkflowContextMock.mockReturnValue( true );
|
|
79
|
+
|
|
80
|
+
logger.info( 123, {
|
|
81
|
+
requestId: 'req-1',
|
|
82
|
+
level: 'error',
|
|
83
|
+
message: 'metadata message'
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
expect( workflowLogMock ).toHaveBeenCalledWith( {
|
|
87
|
+
level: 'info',
|
|
88
|
+
message: '123',
|
|
89
|
+
metadata: { requestId: 'req-1' }
|
|
90
|
+
} );
|
|
91
|
+
} );
|
|
92
|
+
|
|
93
|
+
it( 'logs every level through the activity global logger when it is set outside workflow context', async () => {
|
|
94
|
+
const logger = await loadLogger();
|
|
95
|
+
const activityLoggerMock = vi.fn();
|
|
96
|
+
globalThis[ACTIVITY_LOGGER_SYMBOL] = activityLoggerMock;
|
|
97
|
+
|
|
98
|
+
logLevels.forEach( level => {
|
|
99
|
+
logger[level]( `${level} message`, { requestId: level } );
|
|
100
|
+
} );
|
|
101
|
+
|
|
102
|
+
logLevels.forEach( ( level, index ) => {
|
|
103
|
+
expect( activityLoggerMock ).toHaveBeenNthCalledWith( index + 1, {
|
|
104
|
+
level,
|
|
105
|
+
message: `${level} message`,
|
|
106
|
+
metadata: { requestId: level }
|
|
107
|
+
} );
|
|
108
|
+
} );
|
|
109
|
+
expect( workflowLogMock ).not.toHaveBeenCalled();
|
|
110
|
+
} );
|
|
111
|
+
|
|
112
|
+
it( 'logs every level to its native console method when no workflow or activity logger is available', async () => {
|
|
113
|
+
const logger = await loadLogger();
|
|
114
|
+
const consoleMocks = Object.fromEntries(
|
|
115
|
+
[ 'debug', 'error', 'info', 'log', 'warn' ].map( method => [
|
|
116
|
+
method,
|
|
117
|
+
vi.spyOn( console, method ).mockImplementation( () => {} )
|
|
118
|
+
] )
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
logLevels.forEach( level => {
|
|
122
|
+
logger[level]( `${level} message`, { requestId: level } );
|
|
123
|
+
} );
|
|
124
|
+
|
|
125
|
+
logLevels.forEach( level => {
|
|
126
|
+
expect( consoleMocks[consoleMethodsByLevel[level]] ).toHaveBeenCalledWith(
|
|
127
|
+
`${level} message`,
|
|
128
|
+
{ requestId: level }
|
|
129
|
+
);
|
|
130
|
+
} );
|
|
131
|
+
expect( consoleMocks.error ).toHaveBeenCalledTimes( 1 );
|
|
132
|
+
expect( consoleMocks.warn ).toHaveBeenCalledTimes( 1 );
|
|
133
|
+
expect( consoleMocks.info ).toHaveBeenCalledTimes( 1 );
|
|
134
|
+
expect( consoleMocks.debug ).toHaveBeenCalledTimes( 1 );
|
|
135
|
+
expect( consoleMocks.log ).toHaveBeenCalledTimes( 3 );
|
|
136
|
+
expect( workflowLogMock ).not.toHaveBeenCalled();
|
|
137
|
+
} );
|
|
138
|
+
} );
|
package/src/interface/step.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
|
|
2
2
|
import { proxyActivities, inWorkflowContext, executeChild, workflowInfo, uuid4, ParentClosePolicy } from '@temporalio/workflow';
|
|
3
3
|
import { WorkflowValidator } from './validations/index.js';
|
|
4
|
-
import { deepMerge,
|
|
4
|
+
import { deepMerge, toUrlSafeBase64 } from '#utils';
|
|
5
5
|
import { WorkflowContext } from '#internal_utils/workflow_context';
|
|
6
|
+
import { setMetadata } from '#internal_utils/component';
|
|
6
7
|
import { TraceInfo } from '#internal_utils/trace_info';
|
|
7
8
|
import { defaultOptions } from './workflow_activity_options.js';
|
|
8
9
|
import {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FatalError } from '#errors';
|
|
2
2
|
import { fetch } from 'undici';
|
|
3
|
-
import {
|
|
3
|
+
import { serializeFetchResponse, serializeBodyAndInferContentType } from '#utils';
|
|
4
|
+
import { setMetadata } from '#internal_utils/component';
|
|
4
5
|
import { ComponentType } from '#consts';
|
|
5
6
|
import { createChildLogger } from '#logger';
|
|
6
7
|
import { getDestinations } from '#tracing';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { METADATA_ACCESS_SYMBOL } from '#consts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add metadata "values" property to a given object
|
|
5
|
+
* @param {object} target
|
|
6
|
+
* @param {object} values
|
|
7
|
+
*/
|
|
8
|
+
export const setMetadata = ( target, values ) =>
|
|
9
|
+
Object.defineProperty( target, METADATA_ACCESS_SYMBOL, { value: values, writable: false, enumerable: false, configurable: false } );
|
|
@@ -47,7 +47,7 @@ const getColor = v =>
|
|
|
47
47
|
assignedColors.get( v ) ?? assignedColors.set( v, COLORS[assignedColors.size % COLORS.length] ).get( v );
|
|
48
48
|
|
|
49
49
|
export const options = {
|
|
50
|
-
level: 'debug',
|
|
50
|
+
level: process.env.OUTPUT_LOG_LEVEL ?? 'debug',
|
|
51
51
|
transports: [ new transports.Console() ],
|
|
52
52
|
format: format.combine(
|
|
53
53
|
format.colorize(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
3
|
const LEVEL = Symbol.for( 'level' );
|
|
4
4
|
const MESSAGE = Symbol.for( 'message' );
|
|
@@ -14,6 +14,10 @@ const loadDevelopmentLogger = async () => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
describe( 'logger/development', () => {
|
|
17
|
+
afterEach( () => {
|
|
18
|
+
vi.unstubAllEnvs();
|
|
19
|
+
} );
|
|
20
|
+
|
|
17
21
|
describe( 'formatJson', () => {
|
|
18
22
|
it( 'formats nested plain objects and arrays', async () => {
|
|
19
23
|
const { formatJson } = await loadDevelopmentLogger();
|
|
@@ -40,6 +44,19 @@ describe( 'logger/development', () => {
|
|
|
40
44
|
} );
|
|
41
45
|
|
|
42
46
|
describe( 'options.format', () => {
|
|
47
|
+
it( 'uses debug level by default', async () => {
|
|
48
|
+
const { options } = await loadDevelopmentLogger();
|
|
49
|
+
|
|
50
|
+
expect( options.level ).toBe( 'debug' );
|
|
51
|
+
} );
|
|
52
|
+
|
|
53
|
+
it( 'uses OUTPUT_LOG_LEVEL when configured', async () => {
|
|
54
|
+
vi.stubEnv( 'OUTPUT_LOG_LEVEL', 'http' );
|
|
55
|
+
const { options } = await loadDevelopmentLogger();
|
|
56
|
+
|
|
57
|
+
expect( options.level ).toBe( 'http' );
|
|
58
|
+
} );
|
|
59
|
+
|
|
43
60
|
it( 'formats level, namespace, message, and metadata fields', async () => {
|
|
44
61
|
const { options } = await loadDevelopmentLogger();
|
|
45
62
|
const info = options.format.transform( {
|
package/src/logger/production.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { options } from './production.js';
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
2
|
|
|
4
3
|
const LEVEL = Symbol.for( 'level' );
|
|
5
4
|
const MESSAGE = Symbol.for( 'message' );
|
|
6
5
|
|
|
6
|
+
const loadProductionLogger = async () => {
|
|
7
|
+
vi.resetModules();
|
|
8
|
+
return import( './production.js' );
|
|
9
|
+
};
|
|
10
|
+
|
|
7
11
|
describe( 'logger/production', () => {
|
|
8
|
-
|
|
12
|
+
afterEach( () => {
|
|
13
|
+
vi.unstubAllEnvs();
|
|
14
|
+
} );
|
|
15
|
+
|
|
16
|
+
it( 'uses info level and default production metadata', async () => {
|
|
17
|
+
const { options } = await loadProductionLogger();
|
|
18
|
+
|
|
9
19
|
expect( options.level ).toBe( 'info' );
|
|
10
20
|
expect( options.defaultMeta ).toEqual( {
|
|
11
21
|
service: 'output-worker',
|
|
@@ -13,7 +23,15 @@ describe( 'logger/production', () => {
|
|
|
13
23
|
} );
|
|
14
24
|
} );
|
|
15
25
|
|
|
16
|
-
it( '
|
|
26
|
+
it( 'uses OUTPUT_LOG_LEVEL when configured', async () => {
|
|
27
|
+
vi.stubEnv( 'OUTPUT_LOG_LEVEL', 'debug' );
|
|
28
|
+
const { options } = await loadProductionLogger();
|
|
29
|
+
|
|
30
|
+
expect( options.level ).toBe( 'debug' );
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'formats logs as JSON with timestamp and metadata fields', async () => {
|
|
34
|
+
const { options } = await loadProductionLogger();
|
|
17
35
|
const info = options.format.transform( {
|
|
18
36
|
[LEVEL]: 'info',
|
|
19
37
|
level: 'info',
|
|
@@ -33,7 +51,8 @@ describe( 'logger/production', () => {
|
|
|
33
51
|
expect( output.timestamp ).toEqual( expect.stringMatching( /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}$/u ) );
|
|
34
52
|
} );
|
|
35
53
|
|
|
36
|
-
it( 'includes error stack when formatting Error messages', () => {
|
|
54
|
+
it( 'includes error stack when formatting Error messages', async () => {
|
|
55
|
+
const { options } = await loadProductionLogger();
|
|
37
56
|
const error = new Error( 'boom' );
|
|
38
57
|
const info = options.format.transform( {
|
|
39
58
|
[LEVEL]: 'error',
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
## SDK integration API for Output packages
|
|
2
|
+
|
|
3
|
+
**THESE MODULES ARE NOT PUBLIC. DO NOT IMPORT THEM ON YOUR PROJECT.**
|
|
4
|
+
|
|
5
|
+
This folder contains integration helpers used by other Output SDK packages.
|
|
6
|
+
|
|
7
|
+
The two subfolders represent a hard Temporal boundary. Code imported by
|
|
8
|
+
workflow-bundled modules must only depend on `helpers`. Code that needs worker
|
|
9
|
+
or activity runtime state belongs in `runtime`.
|
|
10
|
+
|
|
11
|
+
Do not change these without reviewing call-sites.
|
|
12
|
+
|
|
13
|
+
### `helpers`
|
|
14
|
+
|
|
15
|
+
Helpers must be safe to import while Temporal workflows are bundled and executed
|
|
16
|
+
in the workflow sandbox.
|
|
17
|
+
|
|
18
|
+
Allowed:
|
|
19
|
+
|
|
20
|
+
- Stateless functions and namespace objects.
|
|
21
|
+
- Deterministic code with no runtime side effects at import time.
|
|
22
|
+
- Imports from other sandbox-safe helper modules.
|
|
23
|
+
- Small, dependency-free helpers for any deterministic computation that can run
|
|
24
|
+
without worker or activity state.
|
|
25
|
+
|
|
26
|
+
Not allowed:
|
|
27
|
+
|
|
28
|
+
- Access to activity context, async storage, sinks, tracing state, workers, or
|
|
29
|
+
clients.
|
|
30
|
+
- Imports from `@temporalio/activity`, `@temporalio/worker`, or Node-only APIs
|
|
31
|
+
that cannot be bundled into workflow code.
|
|
32
|
+
- Process, filesystem, network, clock, randomness, or environment access unless
|
|
33
|
+
the behavior is known to be safe and deterministic in the workflow sandbox.
|
|
34
|
+
- Any import chain that reaches `runtime`.
|
|
35
|
+
|
|
36
|
+
If a helper cannot satisfy these rules, put it in `runtime` instead.
|
|
37
|
+
|
|
38
|
+
### `runtime`
|
|
39
|
+
|
|
40
|
+
Runtime modules are for helpers that need Output or Temporal runtime state.
|
|
41
|
+
|
|
42
|
+
Use `runtime` for activity-only integrations such as context lookup, event
|
|
43
|
+
emission, tracing, async storage, worker state, sinks, and other behavior that is
|
|
44
|
+
not safe inside workflow-bundled code.
|
|
45
|
+
|
|
46
|
+
Runtime modules may depend on activity runtime APIs and internal state, but they
|
|
47
|
+
must not be imported by workflow-safe code.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools to extract data from Outputai Core Components metadata storage
|
|
3
|
+
*/
|
|
4
|
+
export declare const ComponentMetadata: {
|
|
5
|
+
/**
|
|
6
|
+
* Extract the name from a workflow(), step() or evaluator() return
|
|
7
|
+
* @param fn
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
getName( fn: Function ): string | undefined;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if a function has metadata from workflow(), step() or evaluator()
|
|
14
|
+
* @param fn
|
|
15
|
+
*/
|
|
16
|
+
has( fn: Function ): boolean;
|
|
17
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { METADATA_ACCESS_SYMBOL } from '#consts';
|
|
3
|
+
import { ComponentMetadata } from './component_metadata.js';
|
|
4
|
+
|
|
5
|
+
describe( 'ComponentMetadata', () => {
|
|
6
|
+
it( 'detects functions tagged with component metadata', () => {
|
|
7
|
+
const component = () => {};
|
|
8
|
+
Object.defineProperty( component, METADATA_ACCESS_SYMBOL, {
|
|
9
|
+
value: { name: 'my_component' }
|
|
10
|
+
} );
|
|
11
|
+
|
|
12
|
+
expect( ComponentMetadata.has( component ) ).toBe( true );
|
|
13
|
+
} );
|
|
14
|
+
|
|
15
|
+
it( 'returns the component metadata name', () => {
|
|
16
|
+
const component = () => {};
|
|
17
|
+
Object.defineProperty( component, METADATA_ACCESS_SYMBOL, {
|
|
18
|
+
value: { name: 'my_component' }
|
|
19
|
+
} );
|
|
20
|
+
|
|
21
|
+
expect( ComponentMetadata.getName( component ) ).toBe( 'my_component' );
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
it( 'returns false and undefined for untagged functions', () => {
|
|
25
|
+
const fn = () => {};
|
|
26
|
+
|
|
27
|
+
expect( ComponentMetadata.has( fn ) ).toBe( false );
|
|
28
|
+
expect( ComponentMetadata.getName( fn ) ).toBeUndefined();
|
|
29
|
+
} );
|
|
30
|
+
} );
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* > [!WARNING]
|
|
3
|
+
* > **Internal use only.** Not part of the public API; may change without notice.
|
|
4
|
+
*
|
|
5
|
+
* These are stateless helpers for other SDK modules integration.
|
|
6
|
+
* They can be used while Temporal workflows are bundled.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export * from './component_metadata.js';
|
|
11
|
+
export * from './path.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools to interact with filesystem paths
|
|
3
|
+
*/
|
|
4
|
+
export declare const Path: {
|
|
5
|
+
/**
|
|
6
|
+
* Return the first immediate directory of the file invoking the code that called this function.
|
|
7
|
+
*
|
|
8
|
+
* Excludes `@outputai/core`, node, other internal paths, and any additional ignore paths.
|
|
9
|
+
*/
|
|
10
|
+
resolveInvocationDir( additionalIgnorePaths?: string[] ): string
|
|
11
|
+
};
|