@outputai/core 0.9.3-next.c318502.0 → 0.10.1-dev.b7b2fbe.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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/consts.js +2 -1
  3. package/src/helpers/object.js +45 -36
  4. package/src/helpers/object.spec.js +118 -75
  5. package/src/interface/workflow.js +92 -85
  6. package/src/interface/workflow.spec.js +135 -142
  7. package/src/sdk/helpers/objects.d.ts +24 -19
  8. package/src/worker/configs.js +4 -0
  9. package/src/worker/configs.spec.js +21 -0
  10. package/src/worker/configs_tuner_schema.js +81 -0
  11. package/src/worker/configs_tuner_schema.spec.js +127 -0
  12. package/src/worker/index.js +12 -3
  13. package/src/worker/index.spec.js +28 -0
  14. package/src/worker/interceptors/workflow.js +5 -5
  15. package/src/worker/interceptors/workflow.spec.js +7 -7
  16. package/src/worker/loader/activities.js +56 -40
  17. package/src/worker/loader/activities.spec.js +21 -22
  18. package/src/worker/loader/workflows.spec.js +1 -2
  19. package/src/worker/webpack_loaders/consts.js +6 -0
  20. package/src/worker/webpack_loaders/tools.js +1 -146
  21. package/src/worker/webpack_loaders/tools.spec.js +0 -23
  22. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +24 -32
  23. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +82 -279
  24. package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +6 -7
  25. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +57 -119
  26. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.js +88 -0
  27. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +165 -0
  28. package/src/worker/webpack_loaders/workflow_validator/index.mjs +96 -29
  29. package/src/worker/webpack_loaders/workflow_validator/index.spec.js +110 -583
  30. package/src/worker/webpack_loaders/{npm_workflow_export_resolve.js → workflow_validator/npm_workflow_export_resolve.js} +1 -1
  31. package/src/worker/webpack_loaders/{npm_workflow_export_resolve.spec.js → workflow_validator/npm_workflow_export_resolve.spec.js} +1 -1
  32. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +0 -193
  33. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +0 -123
@@ -9,26 +9,13 @@ import {
9
9
  isWorkflowPath,
10
10
  isAbsoluteWorkflowJsResource
11
11
  } from '../tools.js';
12
-
13
- /**
14
- * Files where a bare npm import may bind to child workflows (catalog packages, etc.).
15
- * Matches {@link collectTargetImports} for `workflow.js`; steps/evaluators need the same
16
- * binding knowledge for fn-body validation only.
17
- * @param {string} filename - Absolute resource path.
18
- * @returns {boolean}
19
- */
20
- const fileMayBindBareNpmWorkflowImports = filename => {
21
- const n = filename.replace( /\\/g, '/' );
22
- return isAbsoluteWorkflowJsResource( filename ) ||
23
- isAnyStepsPath( n ) || isAnyEvaluatorsPath( n );
24
- };
25
12
  import {
26
13
  isBareNpmSpecifier,
27
14
  resolveBareImportSpecifiersAsWorkflows,
28
15
  resolveBareDestructuredRequireAsWorkflows,
29
16
  resolveBareDefaultRequireAsWorkflow
30
- } from '../npm_workflow_export_resolve.js';
31
- import { ComponentFile } from '../consts.js';
17
+ } from './npm_workflow_export_resolve.js';
18
+ import { ComponentFactory, ComponentFile } from '../consts.js';
32
19
  import {
33
20
  isCallExpression,
34
21
  isFunctionExpression,
@@ -41,6 +28,19 @@ import {
41
28
  isStringLiteral
42
29
  } from '@babel/types';
43
30
 
31
+ /**
32
+ * Files where a bare npm import may bind to child workflows (catalog packages, etc.).
33
+ * Matches {@link collectTargetImports} for `workflow.js`; steps/evaluators need the same
34
+ * binding knowledge for fn-body validation only.
35
+ * @param {string} filename - Absolute resource path.
36
+ * @returns {boolean}
37
+ */
38
+ const fileMayBindBareNpmWorkflowImports = filename => {
39
+ const n = filename.replace( /\\/g, '/' );
40
+ return isAbsoluteWorkflowJsResource( filename ) ||
41
+ isAnyStepsPath( n ) || isAnyEvaluatorsPath( n );
42
+ };
43
+
44
44
  // Handle CJS/ESM interop for Babel packages when executed as a webpack loader
45
45
  const traverse = traverseModule.default ?? traverseModule;
46
46
 
@@ -72,28 +72,77 @@ const getFileKindLabel = filename => {
72
72
  * @param {string} filename - The file path where the call occurs
73
73
  */
74
74
  const validateInstantiationLocation = ( calleeName, filename ) => {
75
- if ( calleeName === 'step' && !isAnyStepsPath( filename ) ) {
75
+ if ( calleeName === ComponentFactory.STEP && !isAnyStepsPath( filename ) ) {
76
76
  throw new Error( `Invalid instantiation location: step() can only be called in files with 'steps' in the path. Found in: ${filename}` );
77
77
  }
78
- if ( calleeName === 'evaluator' && !isAnyEvaluatorsPath( filename ) ) {
78
+ if ( calleeName === ComponentFactory.EVALUATOR && !isAnyEvaluatorsPath( filename ) ) {
79
79
  throw new Error( `Invalid instantiation location: evaluator() can only be called in files with 'evaluators' in the path. Found in: ${filename}` );
80
80
  }
81
- if ( calleeName === 'workflow' && !isWorkflowPath( filename ) ) {
81
+ if ( calleeName === ComponentFactory.WORKFLOW && !isWorkflowPath( filename ) ) {
82
82
  throw new Error( `Invalid instantiation location: workflow() can only be called in files with 'workflow' in the path. Found in: ${filename}` );
83
83
  }
84
84
  };
85
85
 
86
+ const validateActivityCallsAreInsideFunctions = ( {
87
+ ast, filename, localStepIds, localEvaluatorIds, importedStepIds, importedEvaluatorIds
88
+ } ) => {
89
+ traverse( ast, {
90
+ CallExpression: path => {
91
+ const callee = path.node.callee;
92
+ if ( !isIdentifier( callee ) || path.getFunctionParent() ) {
93
+ return;
94
+ }
95
+
96
+ const { name } = callee;
97
+ const violation = [
98
+ [ ComponentFactory.STEP, localStepIds.has( name ) || importedStepIds.has( name ) ],
99
+ [ ComponentFactory.EVALUATOR, localEvaluatorIds.has( name ) || importedEvaluatorIds.has( name ) ]
100
+ ].find( v => v[1] )?.[0];
101
+
102
+ if ( violation ) {
103
+ throw new Error( `Invalid top-level ${violation} call '${name}' in ${filename}. Activities can only be invoked inside functions.` );
104
+ }
105
+ }
106
+ } );
107
+ };
108
+
109
+ const isActivityPath = specifier => isAnyStepsPath( specifier ) || isAnyEvaluatorsPath( specifier );
110
+
111
+ const validateActivityImportShape = ( specifier, specifiers ) => {
112
+ if ( !isActivityPath( specifier ) ) {
113
+ return;
114
+ }
115
+
116
+ if ( specifiers.some( s => !isImportSpecifier( s ) ) ) {
117
+ throw new Error(
118
+ `Invalid activity import from "${specifier}": use named imports only. ` +
119
+ 'Default and namespace imports from steps/evaluators files cannot be rewritten.'
120
+ );
121
+ }
122
+ };
123
+
124
+ const validateActivityExportShape = ( fileKind, node ) => {
125
+ if ( ![ ComponentFile.STEPS, ComponentFile.EVALUATORS ].includes( fileKind ) ) {
126
+ return;
127
+ }
128
+
129
+ if ( node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration' ) {
130
+ throw new Error( 'Invalid activity export: steps/evaluators files must use named exports only.' );
131
+ }
132
+ };
133
+
86
134
  /**
87
135
  * Webpack loader that validates component instantiation and fn body calls.
88
136
  * Returns the source unchanged unless a validation error is found.
89
137
  *
90
138
  * Rules enforced:
91
139
  * - Instantiation location: step() must be in steps path, evaluator() in evaluators path, workflow() in workflow path
140
+ * - Activity files must use named exports only
141
+ * - Activity imports/requires must use named bindings so the rewriter can target each call
142
+ * - Top-level activity calls are rejected
92
143
  * - steps.js `fn`: calling any step, evaluator, or workflow inside fn body emits warning
93
144
  * - evaluators.js `fn`: calling any step, evaluator, or workflow inside fn body emits warning
94
145
  *
95
- * NOTE: Import restrictions have been removed - any file can import any other file.
96
- *
97
146
  * @param {string|Buffer} source
98
147
  * @param {any} inputMap
99
148
  * @this {import('webpack').LoaderContext<{}>}
@@ -122,6 +171,8 @@ export default function workflowValidatorLoader( source, inputMap ) {
122
171
  ImportDeclaration: path => {
123
172
  const specifier = path.node.source.value;
124
173
 
174
+ validateActivityImportShape( specifier, path.node.specifiers );
175
+
125
176
  if ( isBareNpmSpecifier( specifier ) && fileMayBindBareNpmWorkflowImports( filename ) ) {
126
177
  const outcome = resolveBareImportSpecifiersAsWorkflows( {
127
178
  fromAbsoluteFile: filename,
@@ -152,6 +203,12 @@ export default function workflowValidatorLoader( source, inputMap ) {
152
203
  }
153
204
  }
154
205
  },
206
+ ExportDefaultDeclaration: path => {
207
+ validateActivityExportShape( fileKind, path.node );
208
+ },
209
+ ExportAllDeclaration: path => {
210
+ validateActivityExportShape( fileKind, path.node );
211
+ },
155
212
  VariableDeclarator: path => {
156
213
  const init = path.node.init;
157
214
  if ( !isCallExpression( init ) ) {
@@ -159,20 +216,20 @@ export default function workflowValidatorLoader( source, inputMap ) {
159
216
  }
160
217
 
161
218
  // Validate instantiation location for step/evaluator/workflow calls
162
- if ( isIdentifier( init.callee, { name: 'step' } ) ) {
163
- validateInstantiationLocation( 'step', filename );
219
+ if ( isIdentifier( init.callee, { name: ComponentFactory.STEP } ) ) {
220
+ validateInstantiationLocation( ComponentFactory.STEP, filename );
164
221
  if ( isIdentifier( path.node.id ) ) {
165
222
  localStepIds.add( path.node.id.name );
166
223
  }
167
224
  }
168
- if ( isIdentifier( init.callee, { name: 'evaluator' } ) ) {
169
- validateInstantiationLocation( 'evaluator', filename );
225
+ if ( isIdentifier( init.callee, { name: ComponentFactory.EVALUATOR } ) ) {
226
+ validateInstantiationLocation( ComponentFactory.EVALUATOR, filename );
170
227
  if ( isIdentifier( path.node.id ) ) {
171
228
  localEvaluatorIds.add( path.node.id.name );
172
229
  }
173
230
  }
174
- if ( isIdentifier( init.callee, { name: 'workflow' } ) ) {
175
- validateInstantiationLocation( 'workflow', filename );
231
+ if ( isIdentifier( init.callee, { name: ComponentFactory.WORKFLOW } ) ) {
232
+ validateInstantiationLocation( ComponentFactory.WORKFLOW, filename );
176
233
  }
177
234
 
178
235
  // CommonJS requires: collect identifiers for fn body call checks
@@ -208,6 +265,12 @@ export default function workflowValidatorLoader( source, inputMap ) {
208
265
 
209
266
  // Collect imported identifiers from require patterns
210
267
  const reqType = getFileKind( toAbsolutePath( fileDir, req ) );
268
+ if ( [ ComponentFile.STEPS, ComponentFile.EVALUATORS ].includes( reqType ) && !isObjectPattern( path.node.id ) ) {
269
+ throw new Error(
270
+ `Invalid activity require from "${req}": use destructured requires only. ` +
271
+ 'Default-style requires from steps/evaluators files cannot be rewritten.'
272
+ );
273
+ }
211
274
  if ( reqType === ComponentFile.STEPS && isObjectPattern( path.node.id ) ) {
212
275
  for ( const prop of path.node.id.properties ) {
213
276
  if ( isObjectProperty( prop ) && isIdentifier( prop.value ) ) {
@@ -229,6 +292,10 @@ export default function workflowValidatorLoader( source, inputMap ) {
229
292
  }
230
293
  } );
231
294
 
295
+ validateActivityCallsAreInsideFunctions( {
296
+ ast, filename, localStepIds, localEvaluatorIds, importedStepIds, importedEvaluatorIds
297
+ } );
298
+
232
299
  // Function-body call validations for steps/evaluators files
233
300
  if ( [ ComponentFile.STEPS, ComponentFile.EVALUATORS ].includes( fileKind ) ) {
234
301
  traverse( ast, {
@@ -248,9 +315,9 @@ export default function workflowValidatorLoader( source, inputMap ) {
248
315
  const { name } = callee;
249
316
  const fileLabel = getFileKindLabel( filename );
250
317
  const violation = [
251
- [ 'step', localStepIds.has( name ) || importedStepIds.has( name ) ],
252
- [ 'evaluator', localEvaluatorIds.has( name ) || importedEvaluatorIds.has( name ) ],
253
- [ 'workflow', importedWorkflowIds.has( name ) ]
318
+ [ ComponentFactory.STEP, localStepIds.has( name ) || importedStepIds.has( name ) ],
319
+ [ ComponentFactory.EVALUATOR, localEvaluatorIds.has( name ) || importedEvaluatorIds.has( name ) ],
320
+ [ ComponentFactory.WORKFLOW, importedWorkflowIds.has( name ) ]
254
321
  ].find( v => v[1] )?.[0];
255
322
 
256
323
  if ( violation ) {