@outputai/core 0.7.1-next.de30052.0 → 0.8.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 (77) hide show
  1. package/bin/worker.sh +6 -0
  2. package/package.json +1 -1
  3. package/src/consts.js +0 -4
  4. package/src/errors.js +6 -2
  5. package/src/interface/evaluator.js +7 -20
  6. package/src/interface/evaluator.spec.js +117 -1
  7. package/src/interface/step.js +8 -9
  8. package/src/interface/step.spec.js +124 -0
  9. package/src/interface/validations/index.js +108 -0
  10. package/src/interface/validations/index.spec.js +182 -0
  11. package/src/interface/validations/schemas.js +113 -0
  12. package/src/interface/validations/schemas.spec.js +209 -0
  13. package/src/interface/webhook.js +1 -1
  14. package/src/interface/webhook.spec.js +1 -1
  15. package/src/interface/workflow.d.ts +10 -9
  16. package/src/interface/workflow.js +76 -164
  17. package/src/interface/workflow.spec.js +637 -521
  18. package/src/interface/workflow_activity_options.js +16 -0
  19. package/src/interface/workflow_utils.js +1 -1
  20. package/src/interface/zod_integration.spec.js +2 -2
  21. package/src/internal_utils/aggregations.js +0 -10
  22. package/src/internal_utils/aggregations.spec.js +1 -48
  23. package/src/internal_utils/errors.js +14 -8
  24. package/src/internal_utils/errors.spec.js +73 -27
  25. package/src/utils/index.d.ts +19 -0
  26. package/src/utils/utils.js +53 -0
  27. package/src/utils/utils.spec.js +105 -1
  28. package/src/worker/bundle.js +26 -0
  29. package/src/worker/bundle.spec.js +53 -0
  30. package/src/worker/bundler_options.js +1 -1
  31. package/src/worker/bundler_options.spec.js +1 -1
  32. package/src/worker/catalog_workflow/catalog_job.js +148 -0
  33. package/src/worker/catalog_workflow/catalog_job.spec.js +232 -0
  34. package/src/worker/check.js +24 -0
  35. package/src/worker/connection_monitor.js +112 -0
  36. package/src/worker/connection_monitor.spec.js +199 -0
  37. package/src/worker/index.js +146 -41
  38. package/src/worker/index.spec.js +281 -109
  39. package/src/worker/interceptors/activity.js +7 -24
  40. package/src/worker/interceptors/activity.spec.js +97 -66
  41. package/src/worker/interceptors/index.js +4 -7
  42. package/src/worker/interceptors/modules.js +15 -0
  43. package/src/worker/interceptors/workflow.js +6 -8
  44. package/src/worker/interceptors/workflow.spec.js +49 -42
  45. package/src/worker/interruption.js +33 -0
  46. package/src/worker/interruption.spec.js +98 -0
  47. package/src/worker/loader/activities.js +75 -0
  48. package/src/worker/loader/activities.spec.js +213 -0
  49. package/src/worker/loader/hooks.js +28 -0
  50. package/src/worker/loader/hooks.spec.js +64 -0
  51. package/src/worker/loader/matchers.js +46 -0
  52. package/src/worker/loader/matchers.spec.js +140 -0
  53. package/src/worker/{loader_tools.js → loader/tools.js} +19 -67
  54. package/src/worker/{loader_tools.spec.js → loader/tools.spec.js} +53 -85
  55. package/src/worker/loader/workflows.js +82 -0
  56. package/src/worker/loader/workflows.spec.js +256 -0
  57. package/src/worker/{setup_telemetry.js → telemetry.js} +9 -4
  58. package/src/worker/{setup_telemetry.spec.js → telemetry.spec.js} +3 -3
  59. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +5 -109
  60. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +31 -103
  61. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -6
  62. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +11 -83
  63. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +8 -11
  64. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +9 -9
  65. package/src/interface/validations/runtime.js +0 -20
  66. package/src/interface/validations/runtime.spec.js +0 -29
  67. package/src/interface/validations/schema_utils.js +0 -8
  68. package/src/interface/validations/schema_utils.spec.js +0 -67
  69. package/src/interface/validations/static.js +0 -137
  70. package/src/interface/validations/static.spec.js +0 -397
  71. package/src/interface/workflow.replay_compatibility.spec.js +0 -254
  72. package/src/worker/loader.js +0 -202
  73. package/src/worker/loader.spec.js +0 -498
  74. package/src/worker/shutdown.js +0 -26
  75. package/src/worker/shutdown.spec.js +0 -82
  76. package/src/worker/start_catalog.js +0 -96
  77. package/src/worker/start_catalog.spec.js +0 -179
@@ -1,31 +1,20 @@
1
- import { join } from 'node:path';
2
1
  import traverseModule from '@babel/traverse';
3
2
  import {
4
- buildWorkflowNameMap,
5
3
  buildEvaluatorsNameMap,
6
4
  buildSharedEvaluatorsNameMap,
7
5
  buildSharedStepsNameMap,
8
6
  buildStepsNameMap,
9
7
  getLocalNameFromDestructuredProperty,
10
- isAbsoluteWorkflowJsResource,
11
8
  isEvaluatorsPath,
12
9
  isSharedEvaluatorsPath,
13
10
  isSharedStepsPath,
14
11
  isStepsPath,
15
- isWorkflowPath,
16
12
  toAbsolutePath
17
13
  } from '../tools.js';
18
- import {
19
- isBareNpmSpecifier,
20
- resolveBareImportSpecifiersAsWorkflows,
21
- resolveBareDestructuredRequireAsWorkflows,
22
- resolveBareDefaultRequireAsWorkflow
23
- } from '../npm_workflow_export_resolve.js';
24
14
 
25
15
  import {
26
16
  isCallExpression,
27
17
  isIdentifier,
28
- isImportDefaultSpecifier,
29
18
  isImportSpecifier,
30
19
  isObjectPattern,
31
20
  isObjectProperty,
@@ -44,12 +33,6 @@ const unresolvedImportError = ( name, fileLabel, filePath ) =>
44
33
  '(e.g. step() in steps files, evaluator() in evaluators files, workflow() in workflow files).'
45
34
  );
46
35
 
47
- const mixedBareWorkflowImportError = ( specifier, resourcePath ) => new Error(
48
- `Workflow file '${resourcePath}': import from '${specifier}' mixes workflow exports with ` +
49
- 'non-workflow exports, or could not resolve every binding to a workflow.js module. ' +
50
- 'Split npm imports so each declaration only imports workflows, or only non-workflows.'
51
- );
52
-
53
36
  const removeRequireDeclarator = path => {
54
37
  if ( isVariableDeclaration( path.parent ) && path.parent.declarations.length === 1 ) {
55
38
  path.parentPath.remove();
@@ -84,28 +67,23 @@ const collectDestructuredRequires = ( path, absolutePath, req, descriptors ) =>
84
67
 
85
68
  /**
86
69
  * Collect and strip target imports and requires from an AST, producing
87
- * step/workflow import mappings for later rewrites.
70
+ * step/evaluator import mappings for later rewrites.
88
71
  *
89
72
  * Mutates the AST by removing matching import declarations and require declarators.
90
73
  *
91
74
  * @param {import('@babel/types').File} ast - Parsed file AST.
92
75
  * @param {string} fileDir - Absolute directory of the file represented by `ast`.
93
- * @param {{ stepsNameCache: Map<string,Map<string,string>>, workflowNameCache: Map<string,{default:(string|null),named:Map<string,string>}> }} caches
76
+ * @param {{ stepsNameCache: Map<string,Map<string,string>> }} caches
94
77
  * Resolved-name caches to avoid re-reading same modules.
95
- * @param {string} [resourcePath] - Absolute path of the file being transformed; used to resolve
96
- * npm package imports from `workflow.js` via Node resolution and export following.
97
78
  * @returns {{ stepImports: Array<{localName:string,stepName:string}>,
98
- * flowImports: Array<{localName:string,workflowName:string}> }} Collected info mappings.
79
+ * evaluatorImports: Array<{localName:string,evaluatorName:string}> }} Collected info mappings.
99
80
  */
100
81
  export default function collectTargetImports(
101
82
  ast, fileDir,
102
- { stepsNameCache, workflowNameCache, evaluatorsNameCache, sharedStepsNameCache, sharedEvaluatorsNameCache },
103
- resourcePath
83
+ { stepsNameCache, evaluatorsNameCache, sharedStepsNameCache, sharedEvaluatorsNameCache }
104
84
  ) {
105
- const resolutionPath = resourcePath ?? join( fileDir, 'file.js' );
106
85
  const stepImports = [];
107
86
  const sharedStepImports = [];
108
- const flowImports = [];
109
87
  const evaluatorImports = [];
110
88
  const sharedEvaluatorImports = [];
111
89
 
@@ -113,27 +91,7 @@ export default function collectTargetImports(
113
91
  ImportDeclaration: path => {
114
92
  const src = path.node.source.value;
115
93
 
116
- if ( isBareNpmSpecifier( src ) && isAbsoluteWorkflowJsResource( resolutionPath ) ) {
117
- const outcome = resolveBareImportSpecifiersAsWorkflows( {
118
- fromAbsoluteFile: resolutionPath,
119
- specifier: src,
120
- specifiers: path.node.specifiers,
121
- workflowNameCache
122
- } );
123
- if ( outcome.type === 'partial' ) {
124
- throw mixedBareWorkflowImportError( src, resolutionPath );
125
- }
126
- if ( outcome.type === 'all' ) {
127
- for ( const { localName, workflowName } of outcome.bindings ) {
128
- flowImports.push( { localName, workflowName } );
129
- }
130
- path.remove();
131
- return;
132
- }
133
- }
134
-
135
94
  const isTargetImport = isStepsPath( src ) || isSharedStepsPath( src ) ||
136
- isWorkflowPath( src ) ||
137
95
  isEvaluatorsPath( src ) || isSharedEvaluatorsPath( src );
138
96
  if ( !isTargetImport ) {
139
97
  return;
@@ -166,24 +124,6 @@ export default function collectTargetImports(
166
124
  isSharedEvaluatorsPath( src ), buildSharedEvaluatorsNameMap,
167
125
  sharedEvaluatorsNameCache, sharedEvaluatorImports, 'evaluatorName', 'shared evaluators'
168
126
  );
169
- if ( isWorkflowPath( src ) ) {
170
- const { named, default: defName } = buildWorkflowNameMap( absolutePath, workflowNameCache );
171
- for ( const s of path.node.specifiers ) {
172
- if ( isImportDefaultSpecifier( s ) ) {
173
- const localName = s.local.name;
174
- flowImports.push( { localName, workflowName: defName ?? localName } );
175
- } else if ( isImportSpecifier( s ) ) {
176
- const importedName = s.imported.name;
177
- const localName = s.local.name;
178
- const workflowName = named.get( importedName );
179
- if ( workflowName ) {
180
- flowImports.push( { localName, workflowName } );
181
- } else {
182
- throw unresolvedImportError( importedName, 'workflow', absolutePath );
183
- }
184
- }
185
- }
186
- }
187
127
  path.remove();
188
128
  },
189
129
  VariableDeclarator: path => {
@@ -201,38 +141,7 @@ export default function collectTargetImports(
201
141
 
202
142
  const req = firstArgument.value;
203
143
 
204
- if ( isBareNpmSpecifier( req ) && isAbsoluteWorkflowJsResource( resolutionPath ) ) {
205
- if ( isObjectPattern( path.node.id ) ) {
206
- const outcome = resolveBareDestructuredRequireAsWorkflows( {
207
- fromAbsoluteFile: resolutionPath,
208
- specifier: req,
209
- properties: path.node.id.properties,
210
- workflowNameCache
211
- } );
212
- if ( outcome.type === 'partial' ) {
213
- throw mixedBareWorkflowImportError( req, resolutionPath );
214
- }
215
- if ( outcome.type === 'all' ) {
216
- for ( const { localName, workflowName } of outcome.bindings ) {
217
- flowImports.push( { localName, workflowName } );
218
- }
219
- removeRequireDeclarator( path );
220
- return;
221
- }
222
- } else if ( isIdentifier( path.node.id ) ) {
223
- const outcome = resolveBareDefaultRequireAsWorkflow(
224
- resolutionPath, req, path.node.id.name, workflowNameCache
225
- );
226
- if ( outcome.type === 'binding' ) {
227
- flowImports.push( { localName: outcome.localName, workflowName: outcome.workflowName } );
228
- removeRequireDeclarator( path );
229
- return;
230
- }
231
- }
232
- }
233
-
234
144
  const isTargetRequire = isStepsPath( req ) || isSharedStepsPath( req ) ||
235
- isWorkflowPath( req ) ||
236
145
  isEvaluatorsPath( req ) || isSharedEvaluatorsPath( req );
237
146
  if ( !isTargetRequire ) {
238
147
  return;
@@ -263,12 +172,6 @@ export default function collectTargetImports(
263
172
  cache: sharedEvaluatorsNameCache ?? evaluatorsNameCache,
264
173
  target: sharedEvaluatorImports,
265
174
  valueKey: 'evaluatorName', label: 'shared evaluators'
266
- },
267
- {
268
- match: isWorkflowPath,
269
- buildMap: ( p, c ) => buildWorkflowNameMap( p, c ).named,
270
- cache: workflowNameCache, target: flowImports,
271
- valueKey: 'workflowName', label: 'workflow'
272
175
  }
273
176
  ];
274
177
  collectDestructuredRequires(
@@ -276,15 +179,8 @@ export default function collectTargetImports(
276
179
  );
277
180
  return;
278
181
  }
279
-
280
- if ( isWorkflowPath( req ) && isIdentifier( path.node.id ) ) {
281
- const { default: defName } = buildWorkflowNameMap( absolutePath, workflowNameCache );
282
- const localName = path.node.id.name;
283
- flowImports.push( { localName, workflowName: defName ?? localName } );
284
- removeRequireDeclarator( path );
285
- }
286
182
  }
287
183
  } );
288
184
 
289
- return { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports };
185
+ return { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports };
290
186
  }
@@ -10,7 +10,7 @@ function makeAst( source, filename ) {
10
10
  }
11
11
 
12
12
  describe( 'collect_target_imports', () => {
13
- it( 'collects ESM imports for steps and workflows and flags changes', () => {
13
+ it( 'collects ESM imports for steps and evaluators and leaves workflow imports intact', () => {
14
14
  const dir = mkdtempSync( join( tmpdir(), 'collect-esm-' ) );
15
15
  writeFileSync( join( dir, 'steps.js' ), `
16
16
  export const StepA = step({ name: 'step.a' });
@@ -27,25 +27,20 @@ import WF, { FlowA } from './workflow.js';
27
27
  const x = 1;`;
28
28
 
29
29
  const ast = makeAst( source, join( dir, 'file.js' ) );
30
- const { stepImports, evaluatorImports, flowImports } = collectTargetImports(
30
+ const { stepImports, evaluatorImports } = collectTargetImports(
31
31
  ast,
32
32
  dir,
33
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
33
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
34
34
  );
35
35
  expect( evaluatorImports ).toEqual( [ { localName: 'EvalA', evaluatorName: 'eval.a' } ] );
36
36
 
37
37
  expect( stepImports ).toEqual( [ { localName: 'StepA', stepName: 'step.a' } ] );
38
- expect( flowImports ).toEqual( [
39
- { localName: 'WF', workflowName: 'flow.def' },
40
- { localName: 'FlowA', workflowName: 'flow.a' }
41
- ] );
42
- // Import declarations should have been removed
43
- expect( ast.program.body.find( n => n.type === 'ImportDeclaration' ) ).toBeUndefined();
38
+ expect( ast.program.body.find( n => n.type === 'ImportDeclaration' )?.source.value ).toBe( './workflow.js' );
44
39
 
45
40
  rmSync( dir, { recursive: true, force: true } );
46
41
  } );
47
42
 
48
- it( 'collects CJS requires and removes declarators (steps + default workflow)', () => {
43
+ it( 'collects CJS requires for steps and evaluators and leaves workflow requires intact', () => {
49
44
  const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-' ) );
50
45
  writeFileSync( join( dir, 'steps.js' ), 'export const StepB = step({ name: \'step.b\' })' );
51
46
  writeFileSync( join( dir, 'evaluators.js' ), 'export const EvalB = evaluator({ name: \'eval.b\' })' );
@@ -58,20 +53,19 @@ const WF = require( './workflow.js' );
58
53
  const obj = {};`;
59
54
 
60
55
  const ast = makeAst( source, join( dir, 'file.js' ) );
61
- const { stepImports, evaluatorImports, flowImports } = collectTargetImports(
56
+ const { stepImports, evaluatorImports } = collectTargetImports(
62
57
  ast,
63
58
  dir,
64
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
59
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
65
60
  );
66
61
  expect( evaluatorImports ).toEqual( [ { localName: 'EvalB', evaluatorName: 'eval.b' } ] );
67
62
 
68
63
  expect( stepImports ).toEqual( [ { localName: 'StepB', stepName: 'step.b' } ] );
69
- expect( flowImports ).toEqual( [ { localName: 'WF', workflowName: 'flow.c' } ] );
70
- // All require-based declarators should have been removed (only non-require decls may remain)
64
+ // Only non-target require declarators should remain.
71
65
  const hasRequireDecl = ast.program.body.some( n =>
72
66
  n.type === 'VariableDeclaration' && n.declarations.some( d => d.init && d.init.type === 'CallExpression' )
73
67
  );
74
- expect( hasRequireDecl ).toBe( false );
68
+ expect( hasRequireDecl ).toBe( true );
75
69
 
76
70
  rmSync( dir, { recursive: true, force: true } );
77
71
  } );
@@ -86,7 +80,7 @@ const obj = {};`;
86
80
  const { evaluatorImports } = collectTargetImports(
87
81
  ast,
88
82
  dir,
89
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
83
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
90
84
  );
91
85
  expect( evaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
92
86
 
@@ -103,7 +97,7 @@ const obj = {};`;
103
97
  const { stepImports } = collectTargetImports(
104
98
  ast,
105
99
  dir,
106
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
100
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
107
101
  );
108
102
  expect( stepImports ).toEqual( [ { localName: 'MyExport', stepName: 'bad' } ] );
109
103
 
@@ -120,7 +114,7 @@ const obj = {};`;
120
114
  const { evaluatorImports } = collectTargetImports(
121
115
  ast,
122
116
  dir,
123
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
117
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
124
118
  );
125
119
  expect( evaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
126
120
 
@@ -137,14 +131,14 @@ const obj = {};`;
137
131
  const { stepImports } = collectTargetImports(
138
132
  ast,
139
133
  dir,
140
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
134
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
141
135
  );
142
136
  expect( stepImports ).toEqual( [ { localName: 'MyExport', stepName: 'bad' } ] );
143
137
 
144
138
  rmSync( dir, { recursive: true, force: true } );
145
139
  } );
146
140
 
147
- it( 'throws when ESM import from workflow.js has non-workflow export', () => {
141
+ it( 'leaves ESM imports from workflow.js alone', () => {
148
142
  const dir = mkdtempSync( join( tmpdir(), 'collect-esm-mismatch-wf-' ) );
149
143
  writeFileSync( join( dir, 'workflow.js' ), 'export const helper = () => 42;' );
150
144
 
@@ -154,13 +148,14 @@ const obj = {};`;
154
148
  expect( () => collectTargetImports(
155
149
  ast,
156
150
  dir,
157
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
158
- ) ).toThrow( /Unresolved import 'helper' from workflow file/ );
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' );
159
154
 
160
155
  rmSync( dir, { recursive: true, force: true } );
161
156
  } );
162
157
 
163
- it( 'throws when CJS destructured require from workflow.js has non-workflow export', () => {
158
+ it( 'leaves CJS requires from workflow.js alone', () => {
164
159
  const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-mismatch-wf-' ) );
165
160
  writeFileSync( join( dir, 'workflow.js' ), 'export const helper = () => 42;' );
166
161
 
@@ -170,25 +165,26 @@ const obj = {};`;
170
165
  expect( () => collectTargetImports(
171
166
  ast,
172
167
  dir,
173
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
174
- ) ).toThrow( /Unresolved import 'helper' from workflow file/ );
168
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
169
+ ) ).not.toThrow();
170
+ expect( ast.program.body.some( n => n.type === 'VariableDeclaration' ) ).toBe( true );
175
171
 
176
172
  rmSync( dir, { recursive: true, force: true } );
177
173
  } );
178
174
 
179
- it( 'collects CJS destructured require from workflow.js', () => {
175
+ it( 'does not collect CJS destructured require from workflow.js', () => {
180
176
  const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-wf-destruct-' ) );
181
177
  writeFileSync( join( dir, 'workflow.js' ), 'export const FlowX = workflow({ name: \'flow.x\' });' );
182
178
 
183
179
  const source = 'const { FlowX } = require( \'./workflow.js\' );\nconst obj = {};';
184
180
  const ast = makeAst( source, join( dir, 'file.js' ) );
185
181
 
186
- const { flowImports } = collectTargetImports(
182
+ collectTargetImports(
187
183
  ast,
188
184
  dir,
189
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
185
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map() }
190
186
  );
191
- expect( flowImports ).toEqual( [ { localName: 'FlowX', workflowName: 'flow.x' } ] );
187
+ expect( ast.program.body.some( n => n.type === 'VariableDeclaration' ) ).toBe( true );
192
188
 
193
189
  rmSync( dir, { recursive: true, force: true } );
194
190
  } );
@@ -208,7 +204,7 @@ const obj = {};`;
208
204
  const { sharedEvaluatorImports } = collectTargetImports(
209
205
  ast,
210
206
  join( dir, 'workflows', 'my_workflow' ),
211
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
207
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map() }
212
208
  );
213
209
  expect( sharedEvaluatorImports ).toEqual( [ { localName: 'SharedEval', evaluatorName: 'shared.eval' } ] );
214
210
 
@@ -230,7 +226,7 @@ const obj = {};`;
230
226
  const { sharedEvaluatorImports } = collectTargetImports(
231
227
  ast,
232
228
  join( dir, 'workflows', 'my_workflow' ),
233
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
229
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map() }
234
230
  );
235
231
  expect( sharedEvaluatorImports ).toEqual( [ { localName: 'SharedEval', evaluatorName: 'shared.eval' } ] );
236
232
 
@@ -254,8 +250,7 @@ const obj = {};`;
254
250
  join( dir, 'workflows', 'my_workflow' ),
255
251
  {
256
252
  stepsNameCache: new Map(), sharedStepsNameCache: new Map(),
257
- evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(),
258
- workflowNameCache: new Map()
253
+ evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map()
259
254
  }
260
255
  );
261
256
  expect( sharedStepImports ).toEqual( [ { localName: 'SharedA', stepName: 'shared.a' } ] );
@@ -280,8 +275,7 @@ const obj = {};`;
280
275
  join( dir, 'workflows', 'my_workflow' ),
281
276
  {
282
277
  stepsNameCache: new Map(), sharedStepsNameCache: new Map(),
283
- evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(),
284
- workflowNameCache: new Map()
278
+ evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map()
285
279
  }
286
280
  );
287
281
  expect( sharedStepImports ).toEqual( [ { localName: 'MyExport', stepName: 'bad' } ] );
@@ -304,7 +298,7 @@ const obj = {};`;
304
298
  const { sharedEvaluatorImports } = collectTargetImports(
305
299
  ast,
306
300
  join( dir, 'workflows', 'my_workflow' ),
307
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
301
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map() }
308
302
  );
309
303
  expect( sharedEvaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
310
304
 
@@ -326,78 +320,12 @@ const obj = {};`;
326
320
  const { sharedEvaluatorImports } = collectTargetImports(
327
321
  ast,
328
322
  join( dir, 'workflows', 'my_workflow' ),
329
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map(), workflowNameCache: new Map() }
323
+ { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), sharedEvaluatorsNameCache: new Map() }
330
324
  );
331
325
  expect( sharedEvaluatorImports ).toEqual( [ { localName: 'MyExport', evaluatorName: 'bad' } ] );
332
326
 
333
327
  rmSync( dir, { recursive: true, force: true } );
334
328
  } );
335
329
 
336
- it( 'collects ESM imports from @growthxlabs/workflows_catalog', () => {
337
- const dir = mkdtempSync( join( tmpdir(), 'collect-cat-esm-' ) );
338
- const pkgRoot = join( dir, 'node_modules', '@growthxlabs', 'workflows_catalog' );
339
- const srcDir = join( pkgRoot, 'src' );
340
- mkdirSync( join( srcDir, 'workflows', 'wf' ), { recursive: true } );
341
- writeFileSync( join( pkgRoot, 'package.json' ), JSON.stringify( {
342
- name: '@growthxlabs/workflows_catalog',
343
- type: 'module',
344
- main: './src/index.js',
345
- dependencies: { '@outputai/core': '1.0.0' }
346
- } ) );
347
- writeFileSync( join( srcDir, 'index.js' ), 'export { default as sumNumbers } from \'./workflows/wf/workflow.js\';\n' );
348
- writeFileSync( join( srcDir, 'workflows', 'wf', 'workflow.js' ), 'export default workflow({ name: \'cat.flow\' });\n' );
349
-
350
- const fileDir = join( dir, 'consumer' );
351
- mkdirSync( fileDir, { recursive: true } );
352
- const resourcePath = join( fileDir, 'workflow.js' );
353
-
354
- const source = `
355
- import { sumNumbers as SN } from '@growthxlabs/workflows_catalog';
356
- const x = 1;`;
357
- const ast = makeAst( source, resourcePath );
358
-
359
- const { flowImports } = collectTargetImports(
360
- ast,
361
- fileDir,
362
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() },
363
- resourcePath
364
- );
365
- expect( flowImports ).toEqual( [ { localName: 'SN', workflowName: 'cat.flow' } ] );
366
- expect( ast.program.body.find( n => n.type === 'ImportDeclaration' ) ).toBeUndefined();
367
-
368
- rmSync( dir, { recursive: true, force: true } );
369
- } );
370
-
371
- it( 'collects CJS destructured require from @growthxlabs/workflows_catalog', () => {
372
- const dir = mkdtempSync( join( tmpdir(), 'collect-cat-cjs-' ) );
373
- const pkgRoot = join( dir, 'node_modules', '@growthxlabs', 'workflows_catalog' );
374
- const srcDir = join( pkgRoot, 'src' );
375
- mkdirSync( join( srcDir, 'workflows', 'wf' ), { recursive: true } );
376
- writeFileSync( join( pkgRoot, 'package.json' ), JSON.stringify( {
377
- name: '@growthxlabs/workflows_catalog',
378
- type: 'module',
379
- main: './src/index.js',
380
- dependencies: { '@outputai/core': '1.0.0' }
381
- } ) );
382
- writeFileSync( join( srcDir, 'index.js' ), 'export { default as sumNumbers } from \'./workflows/wf/workflow.js\';\n' );
383
- writeFileSync( join( srcDir, 'workflows', 'wf', 'workflow.js' ), 'export default workflow({ name: \'cat.flow2\' });\n' );
384
-
385
- const fileDir = join( dir, 'consumer' );
386
- mkdirSync( fileDir, { recursive: true } );
387
- const resourcePath = join( fileDir, 'workflow.js' );
388
-
389
- const source = 'const { sumNumbers } = require( \'@growthxlabs/workflows_catalog\' );\nconst x = 1;';
390
- const ast = makeAst( source, resourcePath );
391
-
392
- const { flowImports } = collectTargetImports(
393
- ast,
394
- fileDir,
395
- { stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() },
396
- resourcePath
397
- );
398
- expect( flowImports ).toEqual( [ { localName: 'sumNumbers', workflowName: 'cat.flow2' } ] );
399
-
400
- rmSync( dir, { recursive: true, force: true } );
401
- } );
402
330
  } );
403
331
 
@@ -13,10 +13,9 @@ 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
15
  const sharedEvaluatorsNameCache = new Map(); // path -> Map<exported, evaluatorName> (shared)
16
- const workflowNameCache = new Map(); // path -> { default?: name, named: Map<exported, flowName> }
17
16
 
18
17
  /**
19
- * Webpack loader that rewrites step/workflow calls by reading names from
18
+ * Webpack loader that rewrites step/evaluator calls by reading names from
20
19
  * the respective modules and transforming `fn` bodies accordingly.
21
20
  * Preserves sourcemaps.
22
21
  *
@@ -28,21 +27,21 @@ const workflowNameCache = new Map(); // path -> { default?: name, named: Map<exp
28
27
  export default function stepImportRewriterAstLoader( source, inputMap ) {
29
28
  this.cacheable?.( true );
30
29
  const callback = this.async?.() ?? this.callback;
31
- const cache = { stepsNameCache, sharedStepsNameCache, evaluatorsNameCache, sharedEvaluatorsNameCache, workflowNameCache };
30
+ const cache = { stepsNameCache, sharedStepsNameCache, evaluatorsNameCache, sharedEvaluatorsNameCache };
32
31
 
33
32
  try {
34
33
  const filename = this.resourcePath;
35
34
  const ast = parse( String( source ), filename );
36
35
  const fileDir = dirname( filename );
37
- const { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports } =
36
+ const { stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports } =
38
37
  collectTargetImports( ast, fileDir, cache, filename );
39
38
 
40
39
  // No imports
41
- if ( [].concat( stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports ).length === 0 ) {
40
+ if ( [].concat( stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports ).length === 0 ) {
42
41
  return callback( null, source, inputMap );
43
42
  }
44
43
 
45
- const rewrote = rewriteFnBodies( { ast, stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports, flowImports } );
44
+ const rewrote = rewriteFnBodies( { ast, stepImports, sharedStepImports, evaluatorImports, sharedEvaluatorImports } );
46
45
  // No edits performed
47
46
  if ( !rewrote ) {
48
47
  return callback( null, source, inputMap );
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
- import { dirname, join } from 'node:path';
4
+ import { join } from 'node:path';
5
5
  import loader from './index.mjs';
6
6
 
7
7
  function runLoader( source, resourcePath ) {
@@ -17,7 +17,7 @@ function runLoader( source, resourcePath ) {
17
17
  }
18
18
 
19
19
  describe( 'workflows_rewriter Webpack loader spec', () => {
20
- it( 'rewrites ESM imports and converts fn arrow to function', async () => {
20
+ it( 'rewrites ESM step imports and leaves workflow calls intact', async () => {
21
21
  const dir = mkdtempSync( join( tmpdir(), 'ast-loader-esm-' ) );
22
22
  writeFileSync( join( dir, 'steps.js' ), 'export const StepA = step({ name: \'step.a\' });' );
23
23
  writeFileSync( join( dir, 'workflow.js' ), `
@@ -39,11 +39,11 @@ const obj = {
39
39
  const { code } = await runLoader( source, join( dir, 'file.js' ) );
40
40
 
41
41
  expect( code ).not.toMatch( /from '\.\/steps\.js'/ );
42
- expect( code ).not.toMatch( /from '\.\/workflow\.js'/ );
42
+ expect( code ).toMatch( /from '\.\/workflow\.js'/ );
43
43
  expect( code ).toMatch( /fn:\s*async function \(x\)/ );
44
44
  expect( code ).toMatch( /this\.invokeStep\('step\.a',\s*1\)/ );
45
- expect( code ).toMatch( /this\.startWorkflow\('flow\.a',\s*2\)/ );
46
- expect( code ).toMatch( /this\.startWorkflow\('flow\.def',\s*3\)/ );
45
+ expect( code ).toMatch( /FlowA\(2\)/ );
46
+ expect( code ).toMatch( /FlowDef\(3\)/ );
47
47
 
48
48
  rmSync( dir, { recursive: true, force: true } );
49
49
  } );
@@ -98,7 +98,7 @@ const obj = {
98
98
  rmSync( dir, { recursive: true, force: true } );
99
99
  } );
100
100
 
101
- it( 'rewrites CJS requires and converts fn arrow to function', async () => {
101
+ it( 'rewrites CJS step requires and leaves workflow calls intact', async () => {
102
102
  const dir = mkdtempSync( join( tmpdir(), 'ast-loader-cjs-' ) );
103
103
  writeFileSync( join( dir, 'steps.js' ), 'export const StepB = step({ name: \'step.b\' });' );
104
104
  writeFileSync( join( dir, 'workflow.js' ), 'export default workflow({ name: \'flow.c\' });' );
@@ -117,10 +117,10 @@ const obj = {
117
117
  const { code } = await runLoader( source, join( dir, 'file.js' ) );
118
118
 
119
119
  expect( code ).not.toMatch( /require\('\.\/steps\.js'\)/ );
120
- expect( code ).not.toMatch( /require\('\.\/workflow\.js'\)/ );
120
+ expect( code ).toMatch( /require\('\.\/workflow\.js'\)/ );
121
121
  expect( code ).toMatch( /fn:\s*async function \(y\)/ );
122
122
  expect( code ).toMatch( /this\.invokeStep\('step\.b'\)/ );
123
- expect( code ).toMatch( /this\.startWorkflow\('flow\.c'\)/ );
123
+ expect( code ).toMatch( /FlowDefault\(\)/ );
124
124
 
125
125
  rmSync( dir, { recursive: true, force: true } );
126
126
  } );
@@ -143,8 +143,8 @@ const obj = { fn: async () => { StepC(); FlowC(); FlowDef(); } }`;
143
143
 
144
144
  const { code } = await runLoader( source, join( dir, 'file.js' ) );
145
145
  expect( code ).toMatch( /this\.invokeStep\('step\.const'\)/ );
146
- expect( code ).toMatch( /this\.startWorkflow\('wf\.const'\)/ );
147
- expect( code ).toMatch( /this\.startWorkflow\('wf\.def'\)/ );
146
+ expect( code ).toMatch( /FlowC\(\)/ );
147
+ expect( code ).toMatch( /FlowDef\(\)/ );
148
148
  rmSync( dir, { recursive: true, force: true } );
149
149
  } );
150
150
 
@@ -196,78 +196,6 @@ const obj = {
196
196
  rmSync( dir, { recursive: true, force: true } );
197
197
  } );
198
198
 
199
- it( 'rewrites ESM imports from @growthxlabs/workflows_catalog to startWorkflow', async () => {
200
- const dir = mkdtempSync( join( tmpdir(), 'ast-loader-catalog-' ) );
201
- const pkgRoot = join( dir, 'node_modules', '@growthxlabs', 'workflows_catalog' );
202
- const srcDir = join( pkgRoot, 'src' );
203
- mkdirSync( join( srcDir, 'workflows', 'wf' ), { recursive: true } );
204
- writeFileSync( join( pkgRoot, 'package.json' ), JSON.stringify( {
205
- name: '@growthxlabs/workflows_catalog',
206
- type: 'module',
207
- main: './src/index.js',
208
- dependencies: { '@outputai/core': '1.0.0' }
209
- } ) );
210
- writeFileSync( join( srcDir, 'index.js' ), 'export { default as sumNumbers } from \'./workflows/wf/workflow.js\';\n' );
211
- writeFileSync( join( srcDir, 'workflows', 'wf', 'workflow.js' ), 'export default workflow({ name: \'nest.cat\' });\n' );
212
-
213
- const resourcePath = join( dir, 'workflows', 'mine', 'workflow.js' );
214
- mkdirSync( dirname( resourcePath ), { recursive: true } );
215
-
216
- const source = `
217
- import { sumNumbers } from '@growthxlabs/workflows_catalog';
218
-
219
- const obj = {
220
- fn: async () => {
221
- sumNumbers( 1 );
222
- }
223
- }`;
224
-
225
- const { code } = await runLoader( source, resourcePath );
226
-
227
- expect( code ).not.toMatch( /@growthxlabs\/workflows_catalog/ );
228
- expect( code ).toMatch( /this\.startWorkflow\('nest\.cat',\s*1\)/ );
229
-
230
- rmSync( dir, { recursive: true, force: true } );
231
- } );
232
-
233
- it( 'rewrites imports through the output workflow bundle export condition', async () => {
234
- const dir = mkdtempSync( join( tmpdir(), 'ast-loader-catalog-condition-' ) );
235
- const pkgRoot = join( dir, 'node_modules', '@test', 'conditional_catalog' );
236
- mkdirSync( join( pkgRoot, 'bundle' ), { recursive: true } );
237
- writeFileSync( join( pkgRoot, 'package.json' ), JSON.stringify( {
238
- name: '@test/conditional_catalog',
239
- type: 'module',
240
- main: './node-entry.js',
241
- exports: {
242
- '.': {
243
- 'output-workflow-bundle': './bundle/workflow.js',
244
- default: './node-entry.js'
245
- }
246
- }
247
- } ) );
248
- writeFileSync( join( pkgRoot, 'node-entry.js' ), 'export const helper = () => 1;\n' );
249
- writeFileSync( join( pkgRoot, 'bundle', 'workflow.js' ), 'export default workflow({ name: \'bundle.cat\' });\n' );
250
-
251
- const resourcePath = join( dir, 'workflows', 'mine', 'workflow.js' );
252
- mkdirSync( dirname( resourcePath ), { recursive: true } );
253
-
254
- const source = `
255
- import BundleCatalog from '@test/conditional_catalog';
256
-
257
- const obj = {
258
- fn: async () => {
259
- BundleCatalog( 1 );
260
- }
261
- }`;
262
-
263
- const { code } = await runLoader( source, resourcePath );
264
-
265
- expect( code ).not.toMatch( /@test\/conditional_catalog/ );
266
- expect( code ).toMatch( /this\.startWorkflow\('bundle\.cat',\s*1\)/ );
267
-
268
- rmSync( dir, { recursive: true, force: true } );
269
- } );
270
-
271
199
  it( 'throws on non-static name', async () => {
272
200
  const dir = mkdtempSync( join( tmpdir(), 'ast-loader-error-' ) );
273
201
  writeFileSync( join( dir, 'steps.js' ), `
@@ -282,7 +210,7 @@ import { StepX } from './steps.js';
282
210
  import WF from './workflow.js';
283
211
  const obj = { fn: async () => { StepX(); WF(); } }`;
284
212
 
285
- await expect( runLoader( source, join( dir, 'file.js' ) ) ).rejects.toThrow( /Invalid (step|default workflow) name/ );
213
+ await expect( runLoader( source, join( dir, 'file.js' ) ) ).rejects.toThrow( /Invalid step name/ );
286
214
  rmSync( dir, { recursive: true, force: true } );
287
215
  } );
288
216
  } );