@outputai/core 0.10.0 → 0.10.1-next.09ed166.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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/bus.js +58 -20
  3. package/src/bus.spec.js +135 -77
  4. package/src/consts.js +2 -1
  5. package/src/helpers/object.js +48 -35
  6. package/src/helpers/object.spec.js +123 -75
  7. package/src/hooks/index.d.ts +48 -45
  8. package/src/hooks/index.js +49 -44
  9. package/src/hooks/index.spec.js +74 -102
  10. package/src/interface/workflow.js +92 -85
  11. package/src/interface/workflow.spec.js +135 -142
  12. package/src/sdk/helpers/objects.d.ts +24 -19
  13. package/src/sdk/runtime/events.d.ts +4 -6
  14. package/src/sdk/runtime/events.js +2 -15
  15. package/src/sdk/runtime/events.spec.js +14 -92
  16. package/src/worker/global_functions.js +2 -2
  17. package/src/worker/global_functions.spec.js +3 -3
  18. package/src/worker/index.js +3 -3
  19. package/src/worker/index.spec.js +6 -6
  20. package/src/worker/interceptors/activity.js +4 -4
  21. package/src/worker/interceptors/activity.spec.js +9 -9
  22. package/src/worker/interceptors/workflow.js +5 -5
  23. package/src/worker/interceptors/workflow.spec.js +7 -7
  24. package/src/worker/loader/activities.js +56 -40
  25. package/src/worker/loader/activities.spec.js +21 -22
  26. package/src/worker/loader/workflows.spec.js +1 -2
  27. package/src/worker/log_hooks.js +9 -9
  28. package/src/worker/log_hooks.spec.js +2 -2
  29. package/src/worker/sinks.js +8 -8
  30. package/src/worker/sinks.spec.js +9 -9
  31. package/src/worker/webpack_loaders/consts.js +6 -0
  32. package/src/worker/webpack_loaders/tools.js +1 -146
  33. package/src/worker/webpack_loaders/tools.spec.js +0 -23
  34. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +24 -32
  35. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +82 -279
  36. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +6 -7
  37. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +57 -119
  38. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.js +88 -0
  39. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +165 -0
  40. package/src/worker/webpack_loaders/workflow_validator/index.mjs +96 -29
  41. package/src/worker/webpack_loaders/workflow_validator/index.spec.js +110 -583
  42. package/src/worker/webpack_loaders/{npm_workflow_export_resolve.js → workflow_validator/npm_workflow_export_resolve.js} +1 -1
  43. package/src/worker/webpack_loaders/{npm_workflow_export_resolve.spec.js → workflow_validator/npm_workflow_export_resolve.spec.js} +1 -1
  44. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +0 -193
  45. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +0 -123
@@ -43,7 +43,7 @@ const removeRequireDeclarator = path => {
43
43
 
44
44
  const collectDestructuredRequires = ( path, absolutePath, req, descriptors ) => {
45
45
  const propFilter = p => isObjectProperty( p ) && isIdentifier( p.key );
46
- for ( const { match, buildMap, cache, target, valueKey, label } of descriptors ) {
46
+ for ( const { match, buildMap, cache, label, target } of descriptors ) {
47
47
  if ( !match( req ) ) {
48
48
  continue;
49
49
  }
@@ -54,7 +54,7 @@ const collectDestructuredRequires = ( path, absolutePath, req, descriptors ) =>
54
54
  if ( localName ) {
55
55
  const resolved = nameMap.get( importedName );
56
56
  if ( resolved ) {
57
- target.push( { localName, [valueKey]: resolved } );
57
+ target.push( { localName, activityName: resolved } );
58
58
  } else {
59
59
  throw unresolvedImportError( importedName, label, absolutePath );
60
60
  }
@@ -67,25 +67,26 @@ const collectDestructuredRequires = ( path, absolutePath, req, descriptors ) =>
67
67
 
68
68
  /**
69
69
  * Collect and strip target imports and requires from an AST, producing
70
- * step/evaluator import mappings for later rewrites.
70
+ * activity import mappings for later rewrites.
71
71
  *
72
72
  * Mutates the AST by removing matching import declarations and require declarators.
73
73
  *
74
74
  * @param {import('@babel/types').File} ast - Parsed file AST.
75
75
  * @param {string} fileDir - Absolute directory of the file represented by `ast`.
76
- * @param {{ stepsNameCache: Map<string,Map<string,string>> }} caches
76
+ * @param {{
77
+ * stepsNameCache: Map<string,Map<string,string>>,
78
+ * evaluatorsNameCache: Map<string,Map<string,string>>,
79
+ * sharedStepsNameCache: Map<string,Map<string,string>>,
80
+ * sharedEvaluatorsNameCache: Map<string,Map<string,string>>
81
+ * }} caches
77
82
  * Resolved-name caches to avoid re-reading same modules.
78
- * @returns {{ stepImports: Array<{localName:string,stepName:string}>,
79
- * evaluatorImports: Array<{localName:string,evaluatorName:string}> }} Collected info mappings.
83
+ * @returns {{ activityImports: Array<{localName:string,activityName:string}> }} Collected import mappings.
80
84
  */
81
85
  export default function collectTargetImports(
82
86
  ast, fileDir,
83
87
  { stepsNameCache, evaluatorsNameCache, sharedStepsNameCache, sharedEvaluatorsNameCache }
84
88
  ) {
85
- const stepImports = [];
86
- const sharedStepImports = [];
87
- const evaluatorImports = [];
88
- const sharedEvaluatorImports = [];
89
+ const activityImports = [];
89
90
 
90
91
  traverse( ast, {
91
92
  ImportDeclaration: path => {
@@ -98,7 +99,7 @@ export default function collectTargetImports(
98
99
  }
99
100
 
100
101
  const absolutePath = toAbsolutePath( fileDir, src );
101
- const collectNamedImports = ( match, buildMapFn, cache, targetArr, valueKey, fileLabel ) => {
102
+ const collectNamedImports = ( match, buildMapFn, cache, fileLabel ) => {
102
103
  if ( !match ) {
103
104
  return;
104
105
  }
@@ -108,22 +109,17 @@ export default function collectTargetImports(
108
109
  const localName = s.local.name;
109
110
  const value = nameMap.get( importedName );
110
111
  if ( value ) {
111
- const entry = { localName };
112
- entry[valueKey] = value;
113
- targetArr.push( entry );
112
+ activityImports.push( { localName, activityName: value } );
114
113
  } else {
115
114
  throw unresolvedImportError( importedName, fileLabel, absolutePath );
116
115
  }
117
116
  }
118
117
  };
119
118
 
120
- collectNamedImports( isStepsPath( src ), buildStepsNameMap, stepsNameCache, stepImports, 'stepName', 'steps' );
121
- collectNamedImports( isSharedStepsPath( src ), buildSharedStepsNameMap, sharedStepsNameCache, sharedStepImports, 'stepName', 'shared steps' );
122
- collectNamedImports( isEvaluatorsPath( src ), buildEvaluatorsNameMap, evaluatorsNameCache, evaluatorImports, 'evaluatorName', 'evaluators' );
123
- collectNamedImports(
124
- isSharedEvaluatorsPath( src ), buildSharedEvaluatorsNameMap,
125
- sharedEvaluatorsNameCache, sharedEvaluatorImports, 'evaluatorName', 'shared evaluators'
126
- );
119
+ collectNamedImports( isStepsPath( src ), buildStepsNameMap, stepsNameCache, 'steps' );
120
+ collectNamedImports( isSharedStepsPath( src ), buildSharedStepsNameMap, sharedStepsNameCache, 'shared steps' );
121
+ collectNamedImports( isEvaluatorsPath( src ), buildEvaluatorsNameMap, evaluatorsNameCache, 'evaluators' );
122
+ collectNamedImports( isSharedEvaluatorsPath( src ), buildSharedEvaluatorsNameMap, sharedEvaluatorsNameCache, 'shared evaluators' );
127
123
  path.remove();
128
124
  },
129
125
  VariableDeclarator: path => {
@@ -153,25 +149,21 @@ export default function collectTargetImports(
153
149
  const cjsDescriptors = [
154
150
  {
155
151
  match: isStepsPath, buildMap: buildStepsNameMap,
156
- cache: stepsNameCache, target: stepImports,
157
- valueKey: 'stepName', label: 'steps'
152
+ cache: stepsNameCache, target: activityImports, label: 'steps'
158
153
  },
159
154
  {
160
155
  match: isSharedStepsPath, buildMap: buildSharedStepsNameMap,
161
- cache: sharedStepsNameCache ?? stepsNameCache,
162
- target: sharedStepImports,
163
- valueKey: 'stepName', label: 'shared steps'
156
+ cache: sharedStepsNameCache,
157
+ target: activityImports, label: 'shared steps'
164
158
  },
165
159
  {
166
160
  match: isEvaluatorsPath, buildMap: buildEvaluatorsNameMap,
167
- cache: evaluatorsNameCache, target: evaluatorImports,
168
- valueKey: 'evaluatorName', label: 'evaluators'
161
+ cache: evaluatorsNameCache, target: activityImports, label: 'evaluators'
169
162
  },
170
163
  {
171
164
  match: isSharedEvaluatorsPath, buildMap: buildSharedEvaluatorsNameMap,
172
- cache: sharedEvaluatorsNameCache ?? evaluatorsNameCache,
173
- target: sharedEvaluatorImports,
174
- valueKey: 'evaluatorName', label: 'shared evaluators'
165
+ cache: sharedEvaluatorsNameCache,
166
+ target: activityImports, label: 'shared evaluators'
175
167
  }
176
168
  ];
177
169
  collectDestructuredRequires(
@@ -182,5 +174,5 @@ export default function collectTargetImports(
182
174
  }
183
175
  } );
184
176
 
185
- return { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports };
177
+ return { activityImports };
186
178
  }
@@ -5,327 +5,130 @@ import { join } from 'node:path';
5
5
  import { parse } from '../tools.js';
6
6
  import collectTargetImports from './collect_target_imports.js';
7
7
 
8
- function makeAst( source, filename ) {
9
- return parse( source, filename );
10
- }
8
+ const caches = () => ( {
9
+ stepsNameCache: new Map(),
10
+ evaluatorsNameCache: new Map(),
11
+ sharedStepsNameCache: new Map(),
12
+ sharedEvaluatorsNameCache: new Map()
13
+ } );
14
+
15
+ const makeAst = ( source, filename ) => parse( source, filename );
11
16
 
12
17
  describe( 'collect_target_imports', () => {
13
- it( 'collects ESM imports for steps and evaluators and leaves workflow imports intact', () => {
18
+ it( 'collects ESM activity imports and leaves workflow imports intact', () => {
14
19
  const dir = mkdtempSync( join( tmpdir(), 'collect-esm-' ) );
15
- writeFileSync( join( dir, 'steps.js' ), `
16
- export const StepA = step({ name: 'step.a' });
17
- export const StepB = step({ name: 'step.b' });` );
20
+ writeFileSync( join( dir, 'steps.js' ), 'export const StepA = step({ name: \'step.a\' });' );
18
21
  writeFileSync( join( dir, 'evaluators.js' ), 'export const EvalA = evaluator({ name: \'eval.a\' });' );
19
- writeFileSync( join( dir, 'workflow.js' ), `
20
- export const FlowA = workflow({ name: 'flow.a' });
21
- export default workflow({ name: 'flow.def' });` );
22
+ writeFileSync( join( dir, 'workflow.js' ), 'export const FlowA = workflow({ name: \'flow.a\' });' );
22
23
 
23
- const source = `
24
+ const ast = makeAst( `
24
25
  import { StepA } from './steps.js';
25
26
  import { EvalA } from './evaluators.js';
26
- import WF, { FlowA } from './workflow.js';
27
- const x = 1;`;
27
+ import { FlowA } from './workflow.js';`, join( dir, 'file.js' ) );
28
28
 
29
- const ast = makeAst( source, join( dir, 'file.js' ) );
30
- const { stepImports, evaluatorImports } = collectTargetImports(
31
- ast,
32
- dir,
33
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
34
- );
35
- expect( evaluatorImports ).toEqual( [ { localName: 'EvalA', evaluatorName: 'eval.a' } ] );
29
+ const { activityImports } = collectTargetImports( ast, dir, caches() );
36
30
 
37
- expect( stepImports ).toEqual( [ { localName: 'StepA', stepName: 'step.a' } ] );
31
+ expect( activityImports ).toEqual( [
32
+ { localName: 'StepA', activityName: 'step.a' },
33
+ { localName: 'EvalA', activityName: 'eval.a' }
34
+ ] );
38
35
  expect( ast.program.body.find( n => n.type === 'ImportDeclaration' )?.source.value ).toBe( './workflow.js' );
39
-
40
36
  rmSync( dir, { recursive: true, force: true } );
41
37
  } );
42
38
 
43
- it( 'collects CJS requires for steps and evaluators and leaves workflow requires intact', () => {
39
+ it( 'collects CJS activity requires and leaves workflow requires intact', () => {
44
40
  const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-' ) );
45
- writeFileSync( join( dir, 'steps.js' ), 'export const StepB = step({ name: \'step.b\' })' );
46
- writeFileSync( join( dir, 'evaluators.js' ), 'export const EvalB = evaluator({ name: \'eval.b\' })' );
47
- writeFileSync( join( dir, 'workflow.js' ), 'export default workflow({ name: \'flow.c\' })' );
48
-
49
- const source = `
50
- const { StepB } = require( './steps.js' );
51
- const { EvalB } = require( './evaluators.js' );
52
- const WF = require( './workflow.js' );
53
- const obj = {};`;
54
-
55
- const ast = makeAst( source, join( dir, 'file.js' ) );
56
- const { stepImports, evaluatorImports } = collectTargetImports(
57
- ast,
58
- dir,
59
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
60
- );
61
- expect( evaluatorImports ).toEqual( [ { localName: 'EvalB', evaluatorName: 'eval.b' } ] );
62
-
63
- expect( stepImports ).toEqual( [ { localName: 'StepB', stepName: 'step.b' } ] );
64
- // Only non-target require declarators should remain.
65
- const hasRequireDecl = ast.program.body.some( n =>
66
- n.type === 'VariableDeclaration' && n.declarations.some( d => d.init && d.init.type === 'CallExpression' )
67
- );
68
- expect( hasRequireDecl ).toBe( true );
69
-
70
- rmSync( dir, { recursive: true, force: true } );
71
- } );
72
-
73
- it( 'resolves ESM import from evaluators.js regardless of callee name', () => {
74
- const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-eval-' ) );
75
- writeFileSync( join( dir, 'evaluators.js' ), 'export const MyExport = step({ name: \'bad\' });' );
76
-
77
- const source = 'import { MyExport } from \'./evaluators.js\';';
78
- const ast = makeAst( source, join( dir, 'file.js' ) );
79
-
80
- const { evaluatorImports } = collectTargetImports(
81
- ast,
82
- dir,
83
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
84
- );
85
- expect( evaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
86
-
87
- rmSync( dir, { recursive: true, force: true } );
88
- } );
89
-
90
- it( 'resolves ESM import from steps.js regardless of callee name', () => {
91
- const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-step-' ) );
92
- writeFileSync( join( dir, 'steps.js' ), 'export const MyExport = evaluator({ name: \'bad\' });' );
93
-
94
- const source = 'import { MyExport } from \'./steps.js\';';
95
- const ast = makeAst( source, join( dir, 'file.js' ) );
96
-
97
- const { stepImports } = collectTargetImports(
98
- ast,
99
- dir,
100
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
101
- );
102
- expect( stepImports ).toEqual( [ { localName: 'MyExport', stepName: 'bad' } ] );
103
-
104
- rmSync( dir, { recursive: true, force: true } );
105
- } );
106
-
107
- it( 'resolves CJS require from evaluators.js regardless of callee name', () => {
108
- const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-eval-' ) );
109
- writeFileSync( join( dir, 'evaluators.js' ), 'export const MyExport = step({ name: \'bad\' });' );
110
-
111
- const source = 'const { MyExport } = require( \'./evaluators.js\' );';
112
- const ast = makeAst( source, join( dir, 'file.js' ) );
113
-
114
- const { evaluatorImports } = collectTargetImports(
115
- ast,
116
- dir,
117
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
118
- );
119
- expect( evaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
120
-
121
- rmSync( dir, { recursive: true, force: true } );
122
- } );
123
-
124
- it( 'resolves CJS require from steps.js regardless of callee name', () => {
125
- const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-step-' ) );
126
- writeFileSync( join( dir, 'steps.js' ), 'export const MyExport = evaluator({ name: \'bad\' });' );
127
-
128
- const source = 'const { MyExport } = require( \'./steps.js\' );';
129
- const ast = makeAst( source, join( dir, 'file.js' ) );
130
-
131
- const { stepImports } = collectTargetImports(
132
- ast,
133
- dir,
134
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
135
- );
136
- expect( stepImports ).toEqual( [ { localName: 'MyExport', stepName: 'bad' } ] );
137
-
138
- rmSync( dir, { recursive: true, force: true } );
139
- } );
140
-
141
- it( 'leaves ESM imports from workflow.js alone', () => {
142
- const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-wf-' ) );
143
- writeFileSync( join( dir, 'workflow.js' ), 'export const helper = () => 42;' );
144
-
145
- const source = 'import { helper } from \'./workflow.js\';';
146
- const ast = makeAst( source, join( dir, 'file.js' ) );
147
-
148
- expect( () => collectTargetImports(
149
- ast,
150
- dir,
151
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
152
- ) ).not.toThrow();
153
- expect( ast.program.body.find( n => n.type === 'ImportDeclaration' )?.source.value ).toBe( './workflow.js' );
154
-
155
- rmSync( dir, { recursive: true, force: true } );
156
- } );
157
-
158
- it( 'leaves CJS requires from workflow.js alone', () => {
159
- const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-wf-' ) );
41
+ writeFileSync( join( dir, 'steps.js' ), 'export const StepB = step({ name: \'step.b\' });' );
42
+ writeFileSync( join( dir, 'evaluators.js' ), 'export const EvalB = evaluator({ name: \'eval.b\' });' );
160
43
  writeFileSync( join( dir, 'workflow.js' ), 'export const helper = () => 42;' );
161
44
 
162
- const source = 'const { helper } = require( \'./workflow.js\' );';
163
- const ast = makeAst( source, join( dir, 'file.js' ) );
164
-
165
- expect( () => collectTargetImports(
166
- ast,
167
- dir,
168
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
169
- ) ).not.toThrow();
170
- expect( ast.program.body.some( n => n.type === 'VariableDeclaration' ) ).toBe( true );
171
-
172
- rmSync( dir, { recursive: true, force: true } );
173
- } );
174
-
175
- it( 'does not collect 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\' });' );
45
+ const ast = makeAst( `
46
+ const { StepB } = require('./steps.js');
47
+ const { EvalB } = require('./evaluators.js');
48
+ const { helper } = require('./workflow.js');`, join( dir, 'file.js' ) );
178
49
 
179
- const source = 'const { FlowX } = require( \'./workflow.js\' );\nconst obj = {};';
180
- const ast = makeAst( source, join( dir, 'file.js' ) );
50
+ const { activityImports } = collectTargetImports( ast, dir, caches() );
181
51
 
182
- collectTargetImports(
183
- ast,
184
- dir,
185
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
186
- );
52
+ expect( activityImports ).toEqual( [
53
+ { localName: 'StepB', activityName: 'step.b' },
54
+ { localName: 'EvalB', activityName: 'eval.b' }
55
+ ] );
187
56
  expect( ast.program.body.some( n => n.type === 'VariableDeclaration' ) ).toBe( true );
188
-
189
57
  rmSync( dir, { recursive: true, force: true } );
190
58
  } );
191
59
 
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() }
208
- );
209
- expect( sharedEvaluatorImports ).toEqual( [ { localName: 'SharedEval', evaluatorName: 'shared.eval' } ] );
210
-
60
+ it( 'uses file path to choose component type instead of factory callee name', () => {
61
+ const dir = mkdtempSync( join( tmpdir(), 'collect-path-scoped-' ) );
62
+ writeFileSync( join( dir, 'steps.js' ), 'export const StepExport = evaluator({ name: \'from.steps\' });' );
63
+ writeFileSync( join( dir, 'evaluators.js' ), 'export const EvalExport = step({ name: \'from.evaluators\' });' );
64
+
65
+ const esmAst = makeAst( `
66
+ import { StepExport } from './steps.js';
67
+ import { EvalExport } from './evaluators.js';`, join( dir, 'esm.js' ) );
68
+ const cjsAst = makeAst( `
69
+ const { StepExport } = require('./steps.js');
70
+ const { EvalExport } = require('./evaluators.js');`, join( dir, 'cjs.js' ) );
71
+
72
+ expect( collectTargetImports( esmAst, dir, caches() ).activityImports ).toEqual( [
73
+ { localName: 'StepExport', activityName: 'from.steps' },
74
+ { localName: 'EvalExport', activityName: 'from.evaluators' }
75
+ ] );
76
+ expect( collectTargetImports( cjsAst, dir, caches() ).activityImports ).toEqual( [
77
+ { localName: 'StepExport', activityName: 'from.steps' },
78
+ { localName: 'EvalExport', activityName: 'from.evaluators' }
79
+ ] );
211
80
  rmSync( dir, { recursive: true, force: true } );
212
81
  } );
213
82
 
214
- it( 'collects CJS shared evaluator requires', () => {
215
- const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-eval-' ) );
83
+ it( 'collects shared steps and evaluators from ESM and CJS imports', () => {
84
+ const dir = mkdtempSync( join( tmpdir(), 'collect-shared-' ) );
85
+ mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
216
86
  mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
217
87
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
88
+ writeFileSync( join( dir, 'shared', 'steps', 'common.js' ), 'export const SharedStep = step({ name: \'shared.step\' });' );
218
89
  writeFileSync(
219
90
  join( dir, 'shared', 'evaluators', 'common.js' ),
220
91
  'export const SharedEval = evaluator({ name: \'shared.eval\' });'
221
92
  );
222
93
 
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() }
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
- }
255
- );
256
- expect( sharedStepImports ).toEqual( [ { localName: 'SharedA', stepName: 'shared.a' } ] );
257
-
258
- rmSync( dir, { recursive: true, force: true } );
259
- } );
260
-
261
- it( 'resolves CJS shared steps require regardless of callee name', () => {
262
- const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-step-mismatch-' ) );
263
- mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
264
- mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
265
- writeFileSync(
266
- join( dir, 'shared', 'steps', 'common.js' ),
267
- 'export const MyExport = evaluator({ name: \'bad\' });'
268
- );
269
-
270
- const source = 'const { MyExport } = require( \'../../shared/steps/common.js\' );';
271
- const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
272
-
273
- const { sharedStepImports } = collectTargetImports(
274
- ast,
275
- join( dir, 'workflows', 'my_workflow' ),
276
- {
277
- stepsNameCache: new Map(), sharedStepsNameCache: new Map(),
278
- evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map()
279
- }
280
- );
281
- expect( sharedStepImports ).toEqual( [ { localName: 'MyExport', stepName: 'bad' } ] );
282
-
94
+ const fileDir = join( dir, 'workflows', 'my_workflow' );
95
+ const esmAst = makeAst( `
96
+ import { SharedStep } from '../../shared/steps/common.js';
97
+ import { SharedEval } from '../../shared/evaluators/common.js';`, join( fileDir, 'workflow.js' ) );
98
+ const cjsAst = makeAst( `
99
+ const { SharedStep } = require('../../shared/steps/common.js');
100
+ const { SharedEval } = require('../../shared/evaluators/common.js');`, join( fileDir, 'helper.js' ) );
101
+
102
+ expect( collectTargetImports( esmAst, fileDir, caches() ).activityImports ).toEqual( [
103
+ { localName: 'SharedStep', activityName: 'shared.step' },
104
+ { localName: 'SharedEval', activityName: 'shared.eval' }
105
+ ] );
106
+ expect( collectTargetImports( cjsAst, fileDir, caches() ).activityImports ).toEqual( [
107
+ { localName: 'SharedStep', activityName: 'shared.step' },
108
+ { localName: 'SharedEval', activityName: 'shared.eval' }
109
+ ] );
283
110
  rmSync( dir, { recursive: true, force: true } );
284
111
  } );
285
112
 
286
- it( 'resolves CJS shared evaluator require regardless of callee name', () => {
287
- const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-shared-eval-mismatch-' ) );
288
- mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
289
- mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
290
- writeFileSync(
291
- join( dir, 'shared', 'evaluators', 'common.js' ),
292
- 'export const MyExport = step({ name: \'bad\' });'
293
- );
294
-
295
- const source = 'const { MyExport } = require( \'../../shared/evaluators/common.js\' );';
296
- const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
113
+ it( 'leaves destructured workflow requires untouched', () => {
114
+ const dir = mkdtempSync( join( tmpdir(), 'collect-workflow-require-' ) );
115
+ writeFileSync( join( dir, 'workflow.js' ), 'export const FlowX = workflow({ name: \'flow.x\' });' );
297
116
 
298
- const { sharedEvaluatorImports } = collectTargetImports(
299
- ast,
300
- join( dir, 'workflows', 'my_workflow' ),
301
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map() }
302
- );
303
- expect( sharedEvaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
117
+ const ast = makeAst( 'const { FlowX } = require(\'./workflow.js\');', join( dir, 'file.js' ) );
118
+ const { activityImports } = collectTargetImports( ast, dir, caches() );
304
119
 
120
+ expect( activityImports ).toEqual( [] );
121
+ expect( ast.program.body.some( n => n.type === 'VariableDeclaration' ) ).toBe( true );
305
122
  rmSync( dir, { recursive: true, force: true } );
306
123
  } );
307
124
 
308
- it( 'resolves ESM shared evaluator import regardless of callee name', () => {
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 MyExport = step({ name: \'bad\' });'
315
- );
316
-
317
- const source = 'import { MyExport } from \'../../shared/evaluators/common.js\';';
318
- const ast = makeAst( source, join( dir, 'workflows', 'my_workflow', 'workflow.js' ) );
125
+ it( 'throws when a named activity import cannot be resolved from the target file', () => {
126
+ const dir = mkdtempSync( join( tmpdir(), 'collect-unresolved-' ) );
127
+ writeFileSync( join( dir, 'steps.js' ), 'export const StepA = step({ name: \'step.a\' });' );
319
128
 
320
- const { sharedEvaluatorImports } = collectTargetImports(
321
- ast,
322
- join( dir, 'workflows', 'my_workflow' ),
323
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map() }
324
- );
325
- expect( sharedEvaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
129
+ const ast = makeAst( 'import { MissingStep } from \'./steps.js\';', join( dir, 'file.js' ) );
326
130
 
131
+ expect( () => collectTargetImports( ast, dir, caches() ) ).toThrow( /Unresolved import 'MissingStep'/ );
327
132
  rmSync( dir, { recursive: true, force: true } );
328
133
  } );
329
-
330
134
  } );
331
-
@@ -2,7 +2,7 @@ import { dirname } from 'node:path';
2
2
  import generatorModule from '@babel/generator';
3
3
  import { parse } from '../tools.js';
4
4
 
5
- import rewriteFnBodies from './rewrite_fn_bodies.js';
5
+ import rewriteActivityCalls from './rewrite_activity_calls.js';
6
6
  import collectTargetImports from './collect_target_imports.js';
7
7
 
8
8
  // Handle CJS/ESM interop for Babel packages when executed as a webpack loader
@@ -15,8 +15,8 @@ const evaluatorsNameCache = new Map(); // path -> Map<exported, evaluatorName>
15
15
  const sharedEvaluatorsNameCache = new Map(); // path -> Map<exported, evaluatorName> (shared)
16
16
 
17
17
  /**
18
- * Webpack loader that rewrites step/evaluator calls by reading names from
19
- * the respective modules and transforming `fn` bodies accordingly.
18
+ * Webpack loader that rewrites imported step/evaluator calls by reading
19
+ * declared activity names and transforming function-body activity calls accordingly.
20
20
  * Preserves sourcemaps.
21
21
  *
22
22
  * @param {string|Buffer} source - Module source code.
@@ -33,15 +33,14 @@ export default function stepImportRewriterAstLoader( source, inputMap ) {
33
33
  const filename = this.resourcePath;
34
34
  const ast = parse( String( source ), filename );
35
35
  const fileDir = dirname( filename );
36
- const { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports } =
37
- collectTargetImports( ast, fileDir, cache, filename );
36
+ const { activityImports } = collectTargetImports( ast, fileDir, cache );
38
37
 
39
38
  // No imports
40
- if ( [].concat( stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports ).length === 0 ) {
39
+ if ( activityImports.length === 0 ) {
41
40
  return callback( null, source, inputMap );
42
41
  }
43
42
 
44
- const rewrote = rewriteFnBodies( { ast, stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports } );
43
+ const rewrote = rewriteActivityCalls( { ast, activityImports } );
45
44
  // No edits performed
46
45
  if ( !rewrote ) {
47
46
  return callback( null, source, inputMap );