@output.ai/core 0.5.1 → 0.5.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 +1 -1
- package/src/errors.d.ts +15 -0
- package/src/index.d.ts +17 -864
- package/src/index.js +3 -28
- package/src/interface/evaluation_result.d.ts +160 -0
- package/src/interface/evaluation_result.js +202 -0
- package/src/interface/evaluator.d.ts +70 -0
- package/src/interface/evaluator.js +1 -202
- package/src/interface/evaluator.spec.js +1 -1
- package/src/interface/index.d.ts +9 -0
- package/src/interface/index.js +19 -0
- package/src/interface/step.d.ts +138 -0
- package/src/interface/step.js +1 -0
- package/src/interface/types.d.ts +27 -0
- package/src/interface/webhook.d.ts +84 -0
- package/src/interface/workflow.d.ts +273 -0
- package/src/interface/workflow.js +2 -32
- package/src/interface/workflow.spec.js +462 -46
- package/src/interface/workflow_context.js +31 -0
- package/src/interface/workflow_utils.d.ts +53 -0
- package/src/interface/workflow_utils.js +1 -0
- package/src/tracing/tools/build_trace_tree.js +5 -1
- package/src/tracing/tools/build_trace_tree.spec.js +11 -0
- package/src/utils/index.d.ts +1 -1
- package/src/worker/catalog_workflow/workflow.js +9 -3
- package/src/worker/index.js +7 -39
- package/src/worker/index.spec.js +162 -0
- package/src/worker/interceptors/workflow.js +1 -1
- package/src/worker/shutdown.js +26 -0
- package/src/worker/shutdown.spec.js +82 -0
- package/src/worker/start_catalog.js +36 -0
- package/src/worker/start_catalog.spec.js +116 -0
- package/src/worker/webpack_loaders/tools.js +34 -4
- package/src/worker/webpack_loaders/tools.spec.js +4 -1
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +107 -68
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +251 -1
- package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -4
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +48 -0
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +2 -1
- package/src/worker/webpack_loaders/workflow_validator/index.mjs +3 -3
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +22 -0
|
@@ -3,12 +3,14 @@ import {
|
|
|
3
3
|
buildWorkflowNameMap,
|
|
4
4
|
getLocalNameFromDestructuredProperty,
|
|
5
5
|
isEvaluatorsPath,
|
|
6
|
+
isSharedEvaluatorsPath,
|
|
6
7
|
isSharedStepsPath,
|
|
7
8
|
isStepsPath,
|
|
8
9
|
isWorkflowPath,
|
|
9
10
|
buildStepsNameMap,
|
|
10
11
|
buildSharedStepsNameMap,
|
|
11
12
|
buildEvaluatorsNameMap,
|
|
13
|
+
buildSharedEvaluatorsNameMap,
|
|
12
14
|
toAbsolutePath
|
|
13
15
|
} from '../tools.js';
|
|
14
16
|
import {
|
|
@@ -25,6 +27,46 @@ import {
|
|
|
25
27
|
// Handle CJS/ESM interop for Babel packages when executed as a webpack loader
|
|
26
28
|
const traverse = traverseModule.default ?? traverseModule;
|
|
27
29
|
|
|
30
|
+
const unresolvedImportError = ( name, fileLabel, filePath ) =>
|
|
31
|
+
new Error(
|
|
32
|
+
`Unresolved import '${name}' from ${fileLabel} file '${filePath}'. ` +
|
|
33
|
+
'This export may have been defined with the wrong component type. ' +
|
|
34
|
+
'Use the matching factory function for the file ' +
|
|
35
|
+
'(e.g. step() in steps files, evaluator() in evaluators files, workflow() in workflow files).'
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const removeRequireDeclarator = path => {
|
|
39
|
+
if ( isVariableDeclaration( path.parent ) && path.parent.declarations.length === 1 ) {
|
|
40
|
+
path.parentPath.remove();
|
|
41
|
+
} else {
|
|
42
|
+
path.remove();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const collectDestructuredRequires = ( path, absolutePath, req, descriptors ) => {
|
|
47
|
+
const propFilter = p => isObjectProperty( p ) && isIdentifier( p.key );
|
|
48
|
+
for ( const { match, buildMap, cache, target, valueKey, label } of descriptors ) {
|
|
49
|
+
if ( !match( req ) ) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const nameMap = buildMap( absolutePath, cache );
|
|
53
|
+
for ( const prop of path.node.id.properties.filter( propFilter ) ) {
|
|
54
|
+
const importedName = prop.key.name;
|
|
55
|
+
const localName = getLocalNameFromDestructuredProperty( prop );
|
|
56
|
+
if ( localName ) {
|
|
57
|
+
const resolved = nameMap.get( importedName );
|
|
58
|
+
if ( resolved ) {
|
|
59
|
+
target.push( { localName, [valueKey]: resolved } );
|
|
60
|
+
} else {
|
|
61
|
+
throw unresolvedImportError( importedName, label, absolutePath );
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
removeRequireDeclarator( path );
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
28
70
|
/**
|
|
29
71
|
* Collect and strip target imports and requires from an AST, producing
|
|
30
72
|
* step/workflow import mappings for later rewrites.
|
|
@@ -38,22 +80,28 @@ const traverse = traverseModule.default ?? traverseModule;
|
|
|
38
80
|
* @returns {{ stepImports: Array<{localName:string,stepName:string}>,
|
|
39
81
|
* flowImports: Array<{localName:string,workflowName:string}> }} Collected info mappings.
|
|
40
82
|
*/
|
|
41
|
-
export default function collectTargetImports(
|
|
83
|
+
export default function collectTargetImports(
|
|
84
|
+
ast, fileDir,
|
|
85
|
+
{ stepsNameCache, workflowNameCache, evaluatorsNameCache, sharedStepsNameCache, sharedEvaluatorsNameCache }
|
|
86
|
+
) {
|
|
42
87
|
const stepImports = [];
|
|
43
88
|
const sharedStepImports = [];
|
|
44
89
|
const flowImports = [];
|
|
45
90
|
const evaluatorImports = [];
|
|
91
|
+
const sharedEvaluatorImports = [];
|
|
46
92
|
|
|
47
93
|
traverse( ast, {
|
|
48
94
|
ImportDeclaration: path => {
|
|
49
95
|
const src = path.node.source.value;
|
|
50
96
|
// Ignore other imports
|
|
51
|
-
|
|
97
|
+
const isTargetImport = isStepsPath( src ) || isSharedStepsPath( src ) ||
|
|
98
|
+
isWorkflowPath( src ) || isEvaluatorsPath( src ) || isSharedEvaluatorsPath( src );
|
|
99
|
+
if ( !isTargetImport ) {
|
|
52
100
|
return;
|
|
53
101
|
}
|
|
54
102
|
|
|
55
103
|
const absolutePath = toAbsolutePath( fileDir, src );
|
|
56
|
-
const collectNamedImports = ( match, buildMapFn, cache, targetArr, valueKey ) => {
|
|
104
|
+
const collectNamedImports = ( match, buildMapFn, cache, targetArr, valueKey, fileLabel ) => {
|
|
57
105
|
if ( !match ) {
|
|
58
106
|
return;
|
|
59
107
|
}
|
|
@@ -66,13 +114,19 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
66
114
|
const entry = { localName };
|
|
67
115
|
entry[valueKey] = value;
|
|
68
116
|
targetArr.push( entry );
|
|
117
|
+
} else {
|
|
118
|
+
throw unresolvedImportError( importedName, fileLabel, absolutePath );
|
|
69
119
|
}
|
|
70
120
|
}
|
|
71
121
|
};
|
|
72
122
|
|
|
73
|
-
collectNamedImports( isStepsPath( src ), buildStepsNameMap, stepsNameCache, stepImports, 'stepName' );
|
|
74
|
-
collectNamedImports( isSharedStepsPath( src ), buildSharedStepsNameMap, sharedStepsNameCache, sharedStepImports, 'stepName' );
|
|
75
|
-
collectNamedImports( isEvaluatorsPath( src ), buildEvaluatorsNameMap, evaluatorsNameCache, evaluatorImports, 'evaluatorName' );
|
|
123
|
+
collectNamedImports( isStepsPath( src ), buildStepsNameMap, stepsNameCache, stepImports, 'stepName', 'steps' );
|
|
124
|
+
collectNamedImports( isSharedStepsPath( src ), buildSharedStepsNameMap, sharedStepsNameCache, sharedStepImports, 'stepName', 'shared steps' );
|
|
125
|
+
collectNamedImports( isEvaluatorsPath( src ), buildEvaluatorsNameMap, evaluatorsNameCache, evaluatorImports, 'evaluatorName', 'evaluators' );
|
|
126
|
+
collectNamedImports(
|
|
127
|
+
isSharedEvaluatorsPath( src ), buildSharedEvaluatorsNameMap,
|
|
128
|
+
sharedEvaluatorsNameCache, sharedEvaluatorImports, 'evaluatorName', 'shared evaluators'
|
|
129
|
+
);
|
|
76
130
|
if ( isWorkflowPath( src ) ) {
|
|
77
131
|
const { named, default: defName } = buildWorkflowNameMap( absolutePath, workflowNameCache );
|
|
78
132
|
for ( const s of path.node.specifiers ) {
|
|
@@ -85,6 +139,8 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
85
139
|
const workflowName = named.get( importedName );
|
|
86
140
|
if ( workflowName ) {
|
|
87
141
|
flowImports.push( { localName, workflowName } );
|
|
142
|
+
} else {
|
|
143
|
+
throw unresolvedImportError( importedName, 'workflow', absolutePath );
|
|
88
144
|
}
|
|
89
145
|
}
|
|
90
146
|
}
|
|
@@ -93,90 +149,73 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
93
149
|
},
|
|
94
150
|
VariableDeclarator: path => {
|
|
95
151
|
const init = path.node.init;
|
|
96
|
-
// Not a require call
|
|
97
152
|
if ( !isCallExpression( init ) ) {
|
|
98
153
|
return;
|
|
99
154
|
}
|
|
100
|
-
// Different callee
|
|
101
155
|
if ( !isIdentifier( init.callee, { name: 'require' } ) ) {
|
|
102
156
|
return;
|
|
103
157
|
}
|
|
104
158
|
const firstArgument = init.arguments[0];
|
|
105
|
-
// Dynamic require is not supported
|
|
106
159
|
if ( !isStringLiteral( firstArgument ) ) {
|
|
107
160
|
return;
|
|
108
161
|
}
|
|
109
162
|
|
|
110
163
|
const req = firstArgument.value;
|
|
111
|
-
|
|
112
|
-
|
|
164
|
+
const isTargetRequire = isStepsPath( req ) || isSharedStepsPath( req ) ||
|
|
165
|
+
isWorkflowPath( req ) || isEvaluatorsPath( req ) || isSharedEvaluatorsPath( req );
|
|
166
|
+
if ( !isTargetRequire ) {
|
|
113
167
|
return;
|
|
114
168
|
}
|
|
115
169
|
|
|
116
170
|
const absolutePath = toAbsolutePath( fileDir, req );
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
} else {
|
|
149
|
-
path.remove();
|
|
150
|
-
}
|
|
151
|
-
} else if ( isEvaluatorsPath( req ) && isObjectPattern( path.node.id ) ) {
|
|
152
|
-
const nameMap = buildEvaluatorsNameMap( absolutePath, evaluatorsNameCache );
|
|
153
|
-
for ( const prop of path.node.id.properties.filter( prop => isObjectProperty( prop ) && isIdentifier( prop.key ) ) ) {
|
|
154
|
-
const importedName = prop.key.name;
|
|
155
|
-
const localName = getLocalNameFromDestructuredProperty( prop );
|
|
156
|
-
if ( localName ) {
|
|
157
|
-
const evaluatorName = nameMap.get( importedName );
|
|
158
|
-
if ( evaluatorName ) {
|
|
159
|
-
evaluatorImports.push( { localName, evaluatorName } );
|
|
160
|
-
}
|
|
171
|
+
|
|
172
|
+
// Destructured requires: const { X } = require('./steps.js')
|
|
173
|
+
if ( isObjectPattern( path.node.id ) ) {
|
|
174
|
+
const cjsDescriptors = [
|
|
175
|
+
{
|
|
176
|
+
match: isStepsPath, buildMap: buildStepsNameMap,
|
|
177
|
+
cache: stepsNameCache, target: stepImports,
|
|
178
|
+
valueKey: 'stepName', label: 'steps'
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
match: isSharedStepsPath, buildMap: buildSharedStepsNameMap,
|
|
182
|
+
cache: sharedStepsNameCache ?? stepsNameCache,
|
|
183
|
+
target: sharedStepImports,
|
|
184
|
+
valueKey: 'stepName', label: 'shared steps'
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
match: isEvaluatorsPath, buildMap: buildEvaluatorsNameMap,
|
|
188
|
+
cache: evaluatorsNameCache, target: evaluatorImports,
|
|
189
|
+
valueKey: 'evaluatorName', label: 'evaluators'
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
match: isSharedEvaluatorsPath, buildMap: buildSharedEvaluatorsNameMap,
|
|
193
|
+
cache: sharedEvaluatorsNameCache ?? evaluatorsNameCache,
|
|
194
|
+
target: sharedEvaluatorImports,
|
|
195
|
+
valueKey: 'evaluatorName', label: 'shared evaluators'
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
match: isWorkflowPath,
|
|
199
|
+
buildMap: ( p, c ) => buildWorkflowNameMap( p, c ).named,
|
|
200
|
+
cache: workflowNameCache, target: flowImports,
|
|
201
|
+
valueKey: 'workflowName', label: 'workflow'
|
|
161
202
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
path
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
203
|
+
];
|
|
204
|
+
collectDestructuredRequires(
|
|
205
|
+
path, absolutePath, req, cjsDescriptors
|
|
206
|
+
);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Default workflow require: const WF = require('./workflow.js')
|
|
211
|
+
if ( isWorkflowPath( req ) && isIdentifier( path.node.id ) ) {
|
|
169
212
|
const { default: defName } = buildWorkflowNameMap( absolutePath, workflowNameCache );
|
|
170
213
|
const localName = path.node.id.name;
|
|
171
214
|
flowImports.push( { localName, workflowName: defName ?? localName } );
|
|
172
|
-
|
|
173
|
-
path.parentPath.remove();
|
|
174
|
-
} else {
|
|
175
|
-
path.remove();
|
|
176
|
-
}
|
|
215
|
+
removeRequireDeclarator( path );
|
|
177
216
|
}
|
|
178
217
|
}
|
|
179
218
|
} );
|
|
180
219
|
|
|
181
|
-
return { stepImports, sharedStepImports, evaluatorImports, flowImports };
|
|
220
|
+
return { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports };
|
|
182
221
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { parse } from '../tools.js';
|
|
@@ -75,5 +75,255 @@ const obj = {};`;
|
|
|
75
75
|
|
|
76
76
|
rmSync( dir, { recursive: true, force: true } );
|
|
77
77
|
} );
|
|
78
|
+
|
|
79
|
+
it( 'throws when ESM import from evaluators.js uses step() instead of evaluator()', () => {
|
|
80
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-eval-' ) );
|
|
81
|
+
writeFileSync( join( dir, 'evaluators.js' ), 'export const BadExport = step({ name: \'bad\' });' );
|
|
82
|
+
|
|
83
|
+
const source = 'import { BadExport } from \'./evaluators.js\';';
|
|
84
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
85
|
+
|
|
86
|
+
expect( () => collectTargetImports(
|
|
87
|
+
ast,
|
|
88
|
+
dir,
|
|
89
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
90
|
+
) ).toThrow( /Unresolved import 'BadExport' from evaluators file/ );
|
|
91
|
+
|
|
92
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
93
|
+
} );
|
|
94
|
+
|
|
95
|
+
it( 'throws when ESM import from steps.js uses evaluator() instead of step()', () => {
|
|
96
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-step-' ) );
|
|
97
|
+
writeFileSync( join( dir, 'steps.js' ), 'export const BadExport = evaluator({ name: \'bad\' });' );
|
|
98
|
+
|
|
99
|
+
const source = 'import { BadExport } from \'./steps.js\';';
|
|
100
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
101
|
+
|
|
102
|
+
expect( () => collectTargetImports(
|
|
103
|
+
ast,
|
|
104
|
+
dir,
|
|
105
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
106
|
+
) ).toThrow( /Unresolved import 'BadExport' from steps file/ );
|
|
107
|
+
|
|
108
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
109
|
+
} );
|
|
110
|
+
|
|
111
|
+
it( 'throws when CJS require from evaluators.js uses step() instead of evaluator()', () => {
|
|
112
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-eval-' ) );
|
|
113
|
+
writeFileSync( join( dir, 'evaluators.js' ), 'export const BadExport = step({ name: \'bad\' });' );
|
|
114
|
+
|
|
115
|
+
const source = 'const { BadExport } = require( \'./evaluators.js\' );';
|
|
116
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
117
|
+
|
|
118
|
+
expect( () => collectTargetImports(
|
|
119
|
+
ast,
|
|
120
|
+
dir,
|
|
121
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
122
|
+
) ).toThrow( /Unresolved import 'BadExport' from evaluators file/ );
|
|
123
|
+
|
|
124
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
125
|
+
} );
|
|
126
|
+
|
|
127
|
+
it( 'throws when CJS require from steps.js uses evaluator() instead of step()', () => {
|
|
128
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-step-' ) );
|
|
129
|
+
writeFileSync( join( dir, 'steps.js' ), 'export const BadExport = evaluator({ name: \'bad\' });' );
|
|
130
|
+
|
|
131
|
+
const source = 'const { BadExport } = require( \'./steps.js\' );';
|
|
132
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
133
|
+
|
|
134
|
+
expect( () => collectTargetImports(
|
|
135
|
+
ast,
|
|
136
|
+
dir,
|
|
137
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
138
|
+
) ).toThrow( /Unresolved import 'BadExport' from steps file/ );
|
|
139
|
+
|
|
140
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
141
|
+
} );
|
|
142
|
+
|
|
143
|
+
it( 'throws when ESM import from workflow.js has non-workflow export', () => {
|
|
144
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-wf-' ) );
|
|
145
|
+
writeFileSync( join( dir, 'workflow.js' ), 'export const helper = () => 42;' );
|
|
146
|
+
|
|
147
|
+
const source = 'import { helper } from \'./workflow.js\';';
|
|
148
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
149
|
+
|
|
150
|
+
expect( () => collectTargetImports(
|
|
151
|
+
ast,
|
|
152
|
+
dir,
|
|
153
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
154
|
+
) ).toThrow( /Unresolved import 'helper' from workflow file/ );
|
|
155
|
+
|
|
156
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
157
|
+
} );
|
|
158
|
+
|
|
159
|
+
it( 'throws when CJS destructured require from workflow.js has non-workflow export', () => {
|
|
160
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-wf-' ) );
|
|
161
|
+
writeFileSync( join( dir, 'workflow.js' ), 'export const helper = () => 42;' );
|
|
162
|
+
|
|
163
|
+
const source = 'const { helper } = require( \'./workflow.js\' );';
|
|
164
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
165
|
+
|
|
166
|
+
expect( () => collectTargetImports(
|
|
167
|
+
ast,
|
|
168
|
+
dir,
|
|
169
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
170
|
+
) ).toThrow( /Unresolved import 'helper' from workflow file/ );
|
|
171
|
+
|
|
172
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
173
|
+
} );
|
|
174
|
+
|
|
175
|
+
it( 'collects CJS destructured require from workflow.js', () => {
|
|
176
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-wf-destruct-' ) );
|
|
177
|
+
writeFileSync( join( dir, 'workflow.js' ), 'export const FlowX = workflow({ name: \'flow.x\' });' );
|
|
178
|
+
|
|
179
|
+
const source = 'const { FlowX } = require( \'./workflow.js\' );\nconst obj = {};';
|
|
180
|
+
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
181
|
+
|
|
182
|
+
const { flowImports } = collectTargetImports(
|
|
183
|
+
ast,
|
|
184
|
+
dir,
|
|
185
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
186
|
+
);
|
|
187
|
+
expect( flowImports ).toEqual( [ { localName: 'FlowX', workflowName: 'flow.x' } ] );
|
|
188
|
+
|
|
189
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
190
|
+
} );
|
|
191
|
+
|
|
192
|
+
it( 'collects ESM shared evaluator imports', () => {
|
|
193
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-esm-shared-eval-' ) );
|
|
194
|
+
mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
|
|
195
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
196
|
+
writeFileSync(
|
|
197
|
+
join( dir, 'shared', 'evaluators', 'common.js' ),
|
|
198
|
+
'export const SharedEval = evaluator({ name: \'shared.eval\' });'
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const source = 'import { SharedEval } from \'../../shared/evaluators/common.js\';';
|
|
202
|
+
const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
203
|
+
|
|
204
|
+
const { sharedEvaluatorImports } = collectTargetImports(
|
|
205
|
+
ast,
|
|
206
|
+
join( dir, 'workflows', 'my_workflow' ),
|
|
207
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
208
|
+
);
|
|
209
|
+
expect( sharedEvaluatorImports ).toEqual( [ { localName: 'SharedEval', evaluatorName: 'shared.eval' } ] );
|
|
210
|
+
|
|
211
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
212
|
+
} );
|
|
213
|
+
|
|
214
|
+
it( 'collects CJS shared evaluator requires', () => {
|
|
215
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-eval-' ) );
|
|
216
|
+
mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
|
|
217
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
218
|
+
writeFileSync(
|
|
219
|
+
join( dir, 'shared', 'evaluators', 'common.js' ),
|
|
220
|
+
'export const SharedEval = evaluator({ name: \'shared.eval\' });'
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const source = 'const { SharedEval } = require( \'../../shared/evaluators/common.js\' );';
|
|
224
|
+
const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
225
|
+
|
|
226
|
+
const { sharedEvaluatorImports } = collectTargetImports(
|
|
227
|
+
ast,
|
|
228
|
+
join( dir, 'workflows', 'my_workflow' ),
|
|
229
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
230
|
+
);
|
|
231
|
+
expect( sharedEvaluatorImports ).toEqual( [ { localName: 'SharedEval', evaluatorName: 'shared.eval' } ] );
|
|
232
|
+
|
|
233
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
234
|
+
} );
|
|
235
|
+
|
|
236
|
+
it( 'collects CJS shared steps requires', () => {
|
|
237
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-step-' ) );
|
|
238
|
+
mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
|
|
239
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
240
|
+
writeFileSync(
|
|
241
|
+
join( dir, 'shared', 'steps', 'common.js' ),
|
|
242
|
+
'export const SharedA = step({ name: \'shared.a\' });'
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const source = 'const { SharedA } = require( \'../../shared/steps/common.js\' );';
|
|
246
|
+
const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
247
|
+
|
|
248
|
+
const { sharedStepImports } = collectTargetImports(
|
|
249
|
+
ast,
|
|
250
|
+
join( dir, 'workflows', 'my_workflow' ),
|
|
251
|
+
{
|
|
252
|
+
stepsNameCache: new Map(), sharedStepsNameCache: new Map(),
|
|
253
|
+
evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(),
|
|
254
|
+
workflowNameCache: new Map()
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
expect( sharedStepImports ).toEqual( [ { localName: 'SharedA', stepName: 'shared.a' } ] );
|
|
258
|
+
|
|
259
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
260
|
+
} );
|
|
261
|
+
|
|
262
|
+
it( 'throws when CJS shared steps require uses evaluator() instead of step()', () => {
|
|
263
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-step-mismatch-' ) );
|
|
264
|
+
mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
|
|
265
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
266
|
+
writeFileSync(
|
|
267
|
+
join( dir, 'shared', 'steps', 'common.js' ),
|
|
268
|
+
'export const BadExport = evaluator({ name: \'bad\' });'
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const source = 'const { BadExport } = require( \'../../shared/steps/common.js\' );';
|
|
272
|
+
const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
273
|
+
|
|
274
|
+
expect( () => collectTargetImports(
|
|
275
|
+
ast,
|
|
276
|
+
join( dir, 'workflows', 'my_workflow' ),
|
|
277
|
+
{
|
|
278
|
+
stepsNameCache: new Map(), sharedStepsNameCache: new Map(),
|
|
279
|
+
evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(),
|
|
280
|
+
workflowNameCache: new Map()
|
|
281
|
+
}
|
|
282
|
+
) ).toThrow( /Unresolved import 'BadExport' from shared steps file/ );
|
|
283
|
+
|
|
284
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
285
|
+
} );
|
|
286
|
+
|
|
287
|
+
it( 'throws when CJS shared evaluator require uses step() instead of evaluator()', () => {
|
|
288
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-eval-mismatch-' ) );
|
|
289
|
+
mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
|
|
290
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
291
|
+
writeFileSync(
|
|
292
|
+
join( dir, 'shared', 'evaluators', 'common.js' ),
|
|
293
|
+
'export const BadExport = step({ name: \'bad\' });'
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const source = 'const { BadExport } = require( \'../../shared/evaluators/common.js\' );';
|
|
297
|
+
const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
298
|
+
|
|
299
|
+
expect( () => collectTargetImports(
|
|
300
|
+
ast,
|
|
301
|
+
join( dir, 'workflows', 'my_workflow' ),
|
|
302
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
303
|
+
) ).toThrow( /Unresolved import 'BadExport' from shared evaluators file/ );
|
|
304
|
+
|
|
305
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
306
|
+
} );
|
|
307
|
+
|
|
308
|
+
it( 'throws when ESM shared evaluator import uses step() instead of evaluator()', () => {
|
|
309
|
+
const dir = mkdtempSync( join( tmpdir(), 'collect-esm-shared-eval-mismatch-' ) );
|
|
310
|
+
mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
|
|
311
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
312
|
+
writeFileSync(
|
|
313
|
+
join( dir, 'shared', 'evaluators', 'common.js' ),
|
|
314
|
+
'export const BadExport = step({ name: \'bad\' });'
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const source = 'import { BadExport } from \'../../shared/evaluators/common.js\';';
|
|
318
|
+
const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
319
|
+
|
|
320
|
+
expect( () => collectTargetImports(
|
|
321
|
+
ast,
|
|
322
|
+
join( dir, 'workflows', 'my_workflow' ),
|
|
323
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
324
|
+
) ).toThrow( /Unresolved import 'BadExport' from shared evaluators file/ );
|
|
325
|
+
|
|
326
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
327
|
+
} );
|
|
78
328
|
} );
|
|
79
329
|
|
|
@@ -12,6 +12,7 @@ const generate = generatorModule.default ?? generatorModule;
|
|
|
12
12
|
const stepsNameCache = new Map(); // path -> Map<exported, stepName>
|
|
13
13
|
const sharedStepsNameCache = new Map(); // path -> Map<exported, stepName> (shared)
|
|
14
14
|
const evaluatorsNameCache = new Map(); // path -> Map<exported, evaluatorName>
|
|
15
|
+
const sharedEvaluatorsNameCache = new Map(); // path -> Map<exported, evaluatorName> (shared)
|
|
15
16
|
const workflowNameCache = new Map(); // path -> { default?: name, named: Map<exported, flowName> }
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -27,20 +28,20 @@ const workflowNameCache = new Map(); // path -> { default?: name, named: Map<exp
|
|
|
27
28
|
export default function stepImportRewriterAstLoader( source, inputMap ) {
|
|
28
29
|
this.cacheable?.( true );
|
|
29
30
|
const callback = this.async?.() ?? this.callback;
|
|
30
|
-
const cache = { stepsNameCache, sharedStepsNameCache, evaluatorsNameCache, workflowNameCache };
|
|
31
|
+
const cache = { stepsNameCache, sharedStepsNameCache, evaluatorsNameCache, sharedEvaluatorsNameCache, workflowNameCache };
|
|
31
32
|
|
|
32
33
|
try {
|
|
33
34
|
const filename = this.resourcePath;
|
|
34
35
|
const ast = parse( String( source ), filename );
|
|
35
36
|
const fileDir = dirname( filename );
|
|
36
|
-
const { stepImports, sharedStepImports, evaluatorImports, flowImports } = collectTargetImports( ast, fileDir, cache );
|
|
37
|
+
const { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports } = collectTargetImports( ast, fileDir, cache );
|
|
37
38
|
|
|
38
39
|
// No imports
|
|
39
|
-
if ( [].concat( stepImports, sharedStepImports, evaluatorImports, flowImports ).length === 0 ) {
|
|
40
|
+
if ( [].concat( stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports ).length === 0 ) {
|
|
40
41
|
return callback( null, source, inputMap );
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
const rewrote = rewriteFnBodies( { ast, stepImports, sharedStepImports, evaluatorImports, flowImports } );
|
|
44
|
+
const rewrote = rewriteFnBodies( { ast, stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports } );
|
|
44
45
|
// No edits performed
|
|
45
46
|
if ( !rewrote ) {
|
|
46
47
|
return callback( null, source, inputMap );
|
|
@@ -148,6 +148,54 @@ const obj = { fn: async () => { StepC(); FlowC(); FlowDef(); } }`;
|
|
|
148
148
|
rmSync( dir, { recursive: true, force: true } );
|
|
149
149
|
} );
|
|
150
150
|
|
|
151
|
+
it( 'rewrites ESM shared evaluator imports to invokeSharedEvaluator', async () => {
|
|
152
|
+
const dir = mkdtempSync( join( tmpdir(), 'ast-loader-esm-shared-eval-' ) );
|
|
153
|
+
mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
|
|
154
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
155
|
+
writeFileSync( join( dir, 'shared', 'evaluators', 'common.js' ), 'export const SharedEval = evaluator({ name: \'shared.eval\' });' );
|
|
156
|
+
|
|
157
|
+
const source = `
|
|
158
|
+
import { SharedEval } from '../../shared/evaluators/common.js';
|
|
159
|
+
|
|
160
|
+
const obj = {
|
|
161
|
+
fn: async (x) => {
|
|
162
|
+
SharedEval(1);
|
|
163
|
+
}
|
|
164
|
+
}`;
|
|
165
|
+
|
|
166
|
+
const { code } = await runLoader( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
167
|
+
|
|
168
|
+
expect( code ).not.toMatch( /from '\.\.\/\.\.\/shared\/evaluators\/common\.js'/ );
|
|
169
|
+
expect( code ).toMatch( /fn:\s*async function \(x\)/ );
|
|
170
|
+
expect( code ).toMatch( /this\.invokeSharedEvaluator\('shared\.eval',\s*1\)/ );
|
|
171
|
+
|
|
172
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
173
|
+
} );
|
|
174
|
+
|
|
175
|
+
it( 'rewrites CJS shared evaluator requires to invokeSharedEvaluator', async () => {
|
|
176
|
+
const dir = mkdtempSync( join( tmpdir(), 'ast-loader-cjs-shared-eval-' ) );
|
|
177
|
+
mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
|
|
178
|
+
mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
|
|
179
|
+
writeFileSync( join( dir, 'shared', 'evaluators', 'common.js' ), 'export const SharedEval = evaluator({ name: \'shared.eval\' });' );
|
|
180
|
+
|
|
181
|
+
const source = `
|
|
182
|
+
const { SharedEval } = require( '../../shared/evaluators/common.js' );
|
|
183
|
+
|
|
184
|
+
const obj = {
|
|
185
|
+
fn: async (y) => {
|
|
186
|
+
SharedEval();
|
|
187
|
+
}
|
|
188
|
+
}`;
|
|
189
|
+
|
|
190
|
+
const { code } = await runLoader( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
|
|
191
|
+
|
|
192
|
+
expect( code ).not.toMatch( /require\('\.\.\/\.\.\/shared\/evaluators\/common\.js'\)/ );
|
|
193
|
+
expect( code ).toMatch( /fn:\s*async function \(y\)/ );
|
|
194
|
+
expect( code ).toMatch( /this\.invokeSharedEvaluator\('shared\.eval'\)/ );
|
|
195
|
+
|
|
196
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
197
|
+
} );
|
|
198
|
+
|
|
151
199
|
it( 'throws on non-static name', async () => {
|
|
152
200
|
const dir = mkdtempSync( join( tmpdir(), 'ast-loader-error-' ) );
|
|
153
201
|
writeFileSync( join( dir, 'steps.js' ), `
|
|
@@ -156,13 +156,14 @@ const processFunction = ( { name, bindingPath, state, descriptors, processedFns
|
|
|
156
156
|
* @param {Array<{localName:string,workflowName:string}>} params.flowImports - Workflow imports.
|
|
157
157
|
* @returns {boolean} True if the AST was modified; false otherwise.
|
|
158
158
|
*/
|
|
159
|
-
export default function rewriteFnBodies( { ast, stepImports, sharedStepImports = [], evaluatorImports, flowImports } ) {
|
|
159
|
+
export default function rewriteFnBodies( { ast, stepImports, sharedStepImports = [], evaluatorImports, sharedEvaluatorImports = [], flowImports } ) {
|
|
160
160
|
const state = { rewrote: false };
|
|
161
161
|
// Build rewrite descriptors once per traversal
|
|
162
162
|
const descriptors = [
|
|
163
163
|
{ list: stepImports, method: 'invokeStep', key: 'stepName' },
|
|
164
164
|
{ list: sharedStepImports, method: 'invokeSharedStep', key: 'stepName' },
|
|
165
165
|
{ list: evaluatorImports, method: 'invokeEvaluator', key: 'evaluatorName' },
|
|
166
|
+
{ list: sharedEvaluatorImports, method: 'invokeSharedEvaluator', key: 'evaluatorName' },
|
|
166
167
|
{ list: flowImports, method: 'startWorkflow', key: 'workflowName' }
|
|
167
168
|
];
|
|
168
169
|
traverse( ast, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import traverseModule from '@babel/traverse';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
-
import { parse, toAbsolutePath, getFileKind, isAnyStepsPath,
|
|
3
|
+
import { parse, toAbsolutePath, getFileKind, isAnyStepsPath, isAnyEvaluatorsPath, isWorkflowPath } from '../tools.js';
|
|
4
4
|
import { ComponentFile } from '../consts.js';
|
|
5
5
|
import {
|
|
6
6
|
isCallExpression,
|
|
@@ -27,7 +27,7 @@ const getFileKindLabel = filename => {
|
|
|
27
27
|
if ( isAnyStepsPath( filename ) ) {
|
|
28
28
|
return 'steps.js';
|
|
29
29
|
}
|
|
30
|
-
if (
|
|
30
|
+
if ( isAnyEvaluatorsPath( filename ) ) {
|
|
31
31
|
return 'evaluators.js';
|
|
32
32
|
}
|
|
33
33
|
if ( /workflow\.js$/.test( filename ) ) {
|
|
@@ -48,7 +48,7 @@ const validateInstantiationLocation = ( calleeName, filename ) => {
|
|
|
48
48
|
if ( calleeName === 'step' && !isAnyStepsPath( filename ) ) {
|
|
49
49
|
throw new Error( `Invalid instantiation location: step() can only be called in files with 'steps' in the path. Found in: ${filename}` );
|
|
50
50
|
}
|
|
51
|
-
if ( calleeName === 'evaluator' && !
|
|
51
|
+
if ( calleeName === 'evaluator' && !isAnyEvaluatorsPath( filename ) ) {
|
|
52
52
|
throw new Error( `Invalid instantiation location: evaluator() can only be called in files with 'evaluators' in the path. Found in: ${filename}` );
|
|
53
53
|
}
|
|
54
54
|
if ( calleeName === 'workflow' && !isWorkflowPath( filename ) ) {
|