@output.ai/core 0.1.2 → 0.1.3
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 -2
- package/src/interface/workflow.js +2 -3
- package/src/utils/index.d.ts +35 -0
- package/src/utils/index.js +2 -0
- package/src/utils/resolve_invocation_dir.js +27 -0
- package/src/utils/resolve_invocation_dir.spec.js +102 -0
- package/src/interface/utils.js +0 -19
- package/src/interface/utils.spec.js +0 -35
- /package/src/{utils.js → utils/utils.js} +0 -0
- /package/src/{utils.spec.js → utils/utils.spec.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@output.ai/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The core module of the output framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"./tracing": {
|
|
12
12
|
"types": "./src/tracing/index.d.ts",
|
|
13
13
|
"import": "./src/tracing/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./utils": {
|
|
16
|
+
"types": "./src/utils/index.d.ts",
|
|
17
|
+
"import": "./src/utils/index.js"
|
|
14
18
|
}
|
|
15
19
|
},
|
|
16
20
|
"files": [
|
|
@@ -33,13 +37,14 @@
|
|
|
33
37
|
"@temporalio/worker": "1.13.1",
|
|
34
38
|
"@temporalio/workflow": "1.13.1",
|
|
35
39
|
"redis": "5.8.3",
|
|
40
|
+
"stacktrace-parser": "0.1.11",
|
|
36
41
|
"zod": "4.1.12"
|
|
37
42
|
},
|
|
38
43
|
"license": "UNLICENSED",
|
|
39
44
|
"imports": {
|
|
40
45
|
"#consts": "./src/consts.js",
|
|
41
46
|
"#errors": "./src/errors.js",
|
|
42
|
-
"#utils": "./src/utils.js",
|
|
47
|
+
"#utils": "./src/utils/index.js",
|
|
43
48
|
"#tracing": "./src/tracing/internal_interface.js",
|
|
44
49
|
"#async_storage": "./src/async_storage.js",
|
|
45
50
|
"#temporal_options": "./src/temporal_options.js",
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
|
|
2
2
|
import { proxyActivities, inWorkflowContext, executeChild, workflowInfo } from '@temporalio/workflow';
|
|
3
|
-
import { getInvocationDir } from './utils.js';
|
|
4
3
|
import { validateWorkflow } from './validations/static.js';
|
|
5
4
|
import { validateWithSchema } from './validations/runtime.js';
|
|
6
5
|
import { SHARED_STEP_PREFIX } from '#consts';
|
|
7
|
-
import { mergeActivityOptions, setMetadata } from '#utils';
|
|
6
|
+
import { mergeActivityOptions, resolveInvocationDir, setMetadata } from '#utils';
|
|
8
7
|
import { FatalError, ValidationError } from '#errors';
|
|
9
8
|
|
|
10
9
|
const defaultActivityOptions = {
|
|
@@ -20,7 +19,7 @@ const defaultActivityOptions = {
|
|
|
20
19
|
|
|
21
20
|
export function workflow( { name, description, inputSchema, outputSchema, fn, options } ) {
|
|
22
21
|
validateWorkflow( { name, description, inputSchema, outputSchema, fn, options } );
|
|
23
|
-
const workflowPath =
|
|
22
|
+
const workflowPath = resolveInvocationDir();
|
|
24
23
|
|
|
25
24
|
const activityOptions = mergeActivityOptions( defaultActivityOptions, options );
|
|
26
25
|
const steps = proxyActivities( activityOptions );
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the directory of the file invoking the code that called this function
|
|
3
|
+
* Excludes `@output.ai/core`, node, and other internal paths
|
|
4
|
+
*/
|
|
5
|
+
export function resolveInvocationDir(): string;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Node safe clone implementation that doesn't use global structuredClone()
|
|
9
|
+
* @param {object} v
|
|
10
|
+
* @returns {object}
|
|
11
|
+
*/
|
|
12
|
+
export function clone( v: object ): object;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Throw given error
|
|
16
|
+
* @param {Error} e
|
|
17
|
+
* @throws {e}
|
|
18
|
+
*/
|
|
19
|
+
export function throws( e: Error ): void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Add metadata "values" property to a given object
|
|
23
|
+
* @param {object} target
|
|
24
|
+
* @param {object} values
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
export function setMetadata( target: object, values: object ): void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Merge two temporal activity options
|
|
31
|
+
*/
|
|
32
|
+
export function mergeActivityOptions(
|
|
33
|
+
base?: import( '@temporalio/workflow' ).ActivityOptions,
|
|
34
|
+
ext?: import( '@temporalio/workflow' ).ActivityOptions
|
|
35
|
+
): import( '@temporalio/workflow' ).ActivityOptions;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as stackTraceParser from 'stacktrace-parser';
|
|
2
|
+
|
|
3
|
+
// OS separator, but in a deterministic way, allowing this to work in Temporal's sandbox
|
|
4
|
+
// This avoids importing from node:path
|
|
5
|
+
const SEP = new Error().stack.includes( '/' ) ? '/' : '\\';
|
|
6
|
+
const ignorePaths = [
|
|
7
|
+
`${SEP}node_modules${SEP}`,
|
|
8
|
+
`${SEP}app${SEP}sdk${SEP}`,
|
|
9
|
+
`node:internal${SEP}`,
|
|
10
|
+
'evalmachine.',
|
|
11
|
+
`webpack${SEP}bootstrap`
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Return the directory of the file invoking the code that called this function
|
|
16
|
+
* Excludes some internal paths and the sdk itself
|
|
17
|
+
*/
|
|
18
|
+
export default () => {
|
|
19
|
+
const stack = new Error().stack;
|
|
20
|
+
const lines = stackTraceParser.parse( stack );
|
|
21
|
+
|
|
22
|
+
const frame = lines.find( l => !ignorePaths.some( p => l.file.includes( p ) ) );
|
|
23
|
+
if ( !frame ) {
|
|
24
|
+
throw new Error( `Invocation dir resolution via stack trace failed. Stack: ${stack}` );
|
|
25
|
+
}
|
|
26
|
+
return frame.file.replace( 'file://', '' ).split( SEP ).slice( 0, -1 ).join( SEP );
|
|
27
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import resolveInvocationDir from './resolve_invocation_dir';
|
|
3
|
+
|
|
4
|
+
const OriginalError = Error;
|
|
5
|
+
|
|
6
|
+
describe( 'Resolve Invocation Dir', () => {
|
|
7
|
+
afterEach( () => {
|
|
8
|
+
Error = OriginalError;
|
|
9
|
+
} );
|
|
10
|
+
|
|
11
|
+
it( 'Should detect the invocation dir from the tests workflow', () => {
|
|
12
|
+
const stack = `Error
|
|
13
|
+
at resolveInvocationDir (file:///app/sdk/core/src/utils/resolve_invocation_dir.js)
|
|
14
|
+
at fn (file:///app/test_workflows/dist/simple/steps.js:8:21)
|
|
15
|
+
at wrapper (file:///app/sdk/core/src/interface/step.js:12:26)
|
|
16
|
+
at executeNextHandler (/app/node_modules/@temporalio/worker/lib/activity.js:99:54)
|
|
17
|
+
at Storage.runWithContext.parentId (file:///app/sdk/core/src/worker/interceptors/activity.js:31:63)
|
|
18
|
+
at AsyncLocalStorage.run (node:internal/async_local_storage/async_context_frame:63:14)
|
|
19
|
+
at Object.runWithContext (file:///app/sdk/core/src/async_storage.js:12:44)
|
|
20
|
+
at ActivityExecutionInterceptor.execute (file:///app/sdk/core/src/worker/interceptors/activity.js:31:36)
|
|
21
|
+
at next (/app/node_modules/@temporalio/common/lib/interceptors.js:22:51)
|
|
22
|
+
at Activity.execute (/app/node_modules/@temporalio/worker/lib/activity.js:101:26)
|
|
23
|
+
at /app/node_modules/@temporalio/worker/lib/activity.js:149:29`;
|
|
24
|
+
|
|
25
|
+
Error = class extends OriginalError {
|
|
26
|
+
constructor( ...args ) {
|
|
27
|
+
super( ...args );
|
|
28
|
+
this.stack = stack;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
expect( resolveInvocationDir() ).toBe( '/app/test_workflows/dist/simple' );
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
it( 'Should detect the invocation dir from the sandbox environment at sdk/core', () => {
|
|
36
|
+
const stack = `Error
|
|
37
|
+
at resolveInvocationDir (file:///app/sdk/core/src/utils/resolve_invocation_dir.js)
|
|
38
|
+
at workflow (file:///app/sdk/core/src/interface/workflow.js:25:16)
|
|
39
|
+
at file:///app/test_workflows/dist/nested/workflow.js:4:16
|
|
40
|
+
at ModuleJob.run (node:internal/modules/esm/module_job:365:25)
|
|
41
|
+
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)
|
|
42
|
+
at async importComponents (file:///app/sdk/core/src/worker/loader_tools.js:54:22)
|
|
43
|
+
at async loadWorkflows (file:///app/sdk/core/src/worker/loader.js:64:38)
|
|
44
|
+
at async file:///app/sdk/core/src/worker/index.js:21:21`;
|
|
45
|
+
|
|
46
|
+
Error = class extends OriginalError {
|
|
47
|
+
constructor( ...args ) {
|
|
48
|
+
super( ...args );
|
|
49
|
+
this.stack = stack;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
expect( resolveInvocationDir() ).toBe( '/app/test_workflows/dist/nested' );
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
it( 'Should detect the invocation dir from workflow loading at core', () => {
|
|
57
|
+
const stack = `Error
|
|
58
|
+
at __WEBPACK_DEFAULT_EXPORT__ (/app/sdk/core/src/utils/resolve_invocation_dir.js:13:0)
|
|
59
|
+
at workflow (/app/sdk/core/src/interface/workflow.js:22:43)
|
|
60
|
+
at ../../test_workflows/dist/nested/workflow.js (/app/test_workflows/dist/nested/workflow.js:4:24)
|
|
61
|
+
at __webpack_require__ (webpack/bootstrap:19:0)
|
|
62
|
+
at ./src/worker/temp/__workflows_entrypoint.js (null:null:null)
|
|
63
|
+
at __webpack_require__ (webpack/bootstrap:19:0)
|
|
64
|
+
at importWorkflows (/app/sdk/core/src/worker/temp/__workflows_entrypoint-autogenerated-entrypoint.cjs:9:9)
|
|
65
|
+
at Object.initRuntime (/app/node_modules/@temporalio/workflow/src/worker-interface.ts:78:16)
|
|
66
|
+
at __TEMPORAL_CALL_INTO_SCOPE (evalmachine.<anonymous>:30:40)
|
|
67
|
+
at evalmachine.<anonymous>:1:1`;
|
|
68
|
+
|
|
69
|
+
Error = class extends OriginalError {
|
|
70
|
+
constructor( ...args ) {
|
|
71
|
+
super( ...args );
|
|
72
|
+
this.stack = stack;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
expect( resolveInvocationDir() ).toBe( '/app/test_workflows/dist/nested' );
|
|
77
|
+
} );
|
|
78
|
+
|
|
79
|
+
it( 'Should detect the invocation dir from a workflow using core installed via NPM', () => {
|
|
80
|
+
const stack = `Error
|
|
81
|
+
at resolveInvocationDir (file:///app/node_modules/@output.ai/core/src/utils/resolve_invocation_dir.js)
|
|
82
|
+
at fn (file:///app/dist/simple/steps.js:8:21)
|
|
83
|
+
at wrapper (file:///app/node_modules/@output.ai/core/src/interface/step.js:12:26)
|
|
84
|
+
at executeNextHandler (/app/node_modules/@temporalio/worker/lib/activity.js:99:54)
|
|
85
|
+
at Storage.runWithContext.parentId (file:///app/node_modules/@output.ai/core/src/worker/interceptors/activity.js:31:63)
|
|
86
|
+
at AsyncLocalStorage.run (node:internal/async_local_storage/async_context_frame:63:14)
|
|
87
|
+
at Object.runWithContext (file:///app/node_modules/@output.ai/core/src/async_storage.js:12:44)
|
|
88
|
+
at ActivityExecutionInterceptor.execute (file:///app/node_modules/@output.ai/core/src/worker/interceptors/activity.js:31:36)
|
|
89
|
+
at next (/app/node_modules/@temporalio/common/lib/interceptors.js:22:51)
|
|
90
|
+
at Activity.execute (/app/node_modules/@temporalio/worker/lib/activity.js:101:26)
|
|
91
|
+
at /app/node_modules/@temporalio/worker/lib/activity.js:149:29`;
|
|
92
|
+
|
|
93
|
+
Error = class extends OriginalError {
|
|
94
|
+
constructor( ...args ) {
|
|
95
|
+
super( ...args );
|
|
96
|
+
this.stack = stack;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
expect( resolveInvocationDir() ).toBe( '/app/dist/simple' );
|
|
101
|
+
} );
|
|
102
|
+
} );
|
package/src/interface/utils.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Function rigged to return the folder path of the source of calls for the interface methods (step/workflow)
|
|
3
|
-
*
|
|
4
|
-
* IMPORTANT!!!
|
|
5
|
-
* If to refactor this, pay attention to the depth in the stack trace to extract the info.
|
|
6
|
-
* Currently it is 3:
|
|
7
|
-
* - 1st line is the name of the function;
|
|
8
|
-
* - 2nd line is this function;
|
|
9
|
-
* - 3rd line is step/workflow;
|
|
10
|
-
* - 4th line is caller;
|
|
11
|
-
*
|
|
12
|
-
* @returns {string} The folder path of the caller
|
|
13
|
-
*/
|
|
14
|
-
export const getInvocationDir = () => new Error()
|
|
15
|
-
.stack.split( '\n' )[3]
|
|
16
|
-
.split( ' ' )
|
|
17
|
-
.at( -1 )
|
|
18
|
-
.replace( /\((.+):\d+:\d+\)/, '$1' )
|
|
19
|
-
.split( '/' ).slice( 0, -1 ).join( '/' );
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { getInvocationDir } from './utils.js';
|
|
3
|
-
|
|
4
|
-
describe( 'interface/utils', () => {
|
|
5
|
-
describe( 'getInvocationDir', () => {
|
|
6
|
-
it( 'returns the caller directory from stack trace', () => {
|
|
7
|
-
const fakeCaller = '/tmp/project/src/caller/file.js';
|
|
8
|
-
const OriginalError = Error;
|
|
9
|
-
// Provide a deterministic stack shape for the function under test
|
|
10
|
-
// Lines: 1) Error, 2) getInvocationDir, 3) step/workflow, 4) actual caller
|
|
11
|
-
// Include typical V8 formatting with leading spaces and without function name
|
|
12
|
-
// for the caller line
|
|
13
|
-
|
|
14
|
-
Error = class extends OriginalError {
|
|
15
|
-
constructor( ...args ) {
|
|
16
|
-
super( ...args );
|
|
17
|
-
this.stack = [
|
|
18
|
-
'Error',
|
|
19
|
-
' at getInvocationDir (a:1:1)',
|
|
20
|
-
' at step (b:1:1)',
|
|
21
|
-
` at ${fakeCaller}:10:20`
|
|
22
|
-
].join( '\n' );
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
try {
|
|
26
|
-
const dir = getInvocationDir();
|
|
27
|
-
expect( dir ).toBe( '/tmp/project/src/caller' );
|
|
28
|
-
} finally {
|
|
29
|
-
|
|
30
|
-
Error = OriginalError;
|
|
31
|
-
}
|
|
32
|
-
} );
|
|
33
|
-
} );
|
|
34
|
-
} );
|
|
35
|
-
|
|
File without changes
|
|
File without changes
|