@output.ai/core 0.3.0-dev.pr263-7879ff1 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/core",
3
- "version": "0.3.0-dev.pr263-7879ff1",
3
+ "version": "0.3.0",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,7 +1,7 @@
1
1
  import traverseModule from '@babel/traverse';
2
2
  import { dirname } from 'node:path';
3
3
  import { parse, toAbsolutePath, getFileKind, isAnyStepsPath, isEvaluatorsPath, isWorkflowPath } from '../tools.js';
4
- import { ComponentFile, CoreModule } from '../consts.js';
4
+ import { ComponentFile } from '../consts.js';
5
5
  import {
6
6
  isCallExpression,
7
7
  isFunctionExpression,
@@ -36,57 +36,6 @@ const getFileKindLabel = filename => {
36
36
  return filename;
37
37
  };
38
38
 
39
- /**
40
- * Check if workflow dependencies are valid.
41
- * Workflows can import:
42
- * - Components (steps, evaluators, workflow)
43
- * - Core modules (@output.ai/core, local_core)
44
- * - ANY file that is NOT a component file (flexible utility imports)
45
- */
46
- const validateWorkflowImports = ( { specifier, filename, emitWarning } ) => {
47
- const isCore = Object.values( CoreModule ).includes( specifier );
48
- const fileKind = getFileKind( specifier );
49
- const isComponent = Object.values( ComponentFile ).includes( fileKind );
50
- const isNonComponentFile = fileKind === null;
51
-
52
- if ( !isCore && !isComponent && !isNonComponentFile ) {
53
- emitWarning( new Error( `Invalid dependency in workflow.js: '${specifier}'. \
54
- Only components (${Object.values( ComponentFile ) } ), @output.ai/core, or non-component files are allowed in ${filename}` ) );
55
- }
56
- };
57
-
58
- /**
59
- * Check if evaluators or steps import invalid dependencies.
60
- * Steps and evaluators CANNOT import:
61
- * - Other steps (local or shared) - activity isolation
62
- * - Other evaluators (local or shared) - activity isolation
63
- * - Workflows
64
- *
65
- * Steps and evaluators CAN import:
66
- * - ANY file that is NOT a component file (flexible utility imports)
67
- */
68
- const validateStepEvaluatorImports = ( { specifier, filename, emitWarning } ) => {
69
- const importedFileKind = getFileKind( specifier );
70
-
71
- // Activity isolation: steps/evaluators cannot import other steps, evaluators, or workflows
72
- if ( Object.values( ComponentFile ).includes( importedFileKind ) ) {
73
- const fileLabel = getFileKindLabel( filename );
74
- emitWarning( new Error( `Invalid dependency in ${fileLabel}: '${specifier}'. \
75
- Steps, evaluators or workflows are not allowed dependencies in ${filename}` ) );
76
- }
77
- };
78
-
79
- /**
80
- * Validate import for evaluators, steps, workflow
81
- */
82
- const executeImportValidations = ( { fileKind, specifier, filename, emitWarning } ) => {
83
- if ( fileKind === ComponentFile.WORKFLOW ) {
84
- validateWorkflowImports( { specifier, filename, emitWarning } );
85
- } else if ( Object.values( ComponentFile ).includes( fileKind ) ) {
86
- validateStepEvaluatorImports( { specifier, filename, emitWarning } );
87
- }
88
- };
89
-
90
39
  /**
91
40
  * Validate that component instantiation calls occur in the correct file locations.
92
41
  * - step() must be called in a file whose path contains 'steps'
@@ -108,17 +57,15 @@ const validateInstantiationLocation = ( calleeName, filename ) => {
108
57
  };
109
58
 
110
59
  /**
111
- * Webpack loader that validates imports and disallowed calls across modules.
60
+ * Webpack loader that validates component instantiation and fn body calls.
112
61
  * Returns the source unchanged unless a validation error is found.
113
62
  *
114
63
  * Rules enforced:
115
64
  * - Instantiation location: step() must be in steps path, evaluator() in evaluators path, workflow() in workflow path
116
- * - evaluators.js `fn`: at each evaluator().fn body: calling any evaluator, step, or workflow is forbidden
117
- * - evaluators.js: may not import evaluators.js, steps.js, workflow.js, or any shared steps/evaluators
118
- * - steps.js: at each step().fn body: calling any evaluator, step, or workflow is forbidden
119
- * - steps.js: may not import evaluators.js, steps.js, workflow.js, or any shared steps/evaluators
120
- * - workflow.js: may import components (evaluators.js, steps.js, workflow.js including shared);
121
- * and any non-component file, or `@output.ai/core`
65
+ * - steps.js `fn`: calling any step, evaluator, or workflow inside fn body emits warning
66
+ * - evaluators.js `fn`: calling any step, evaluator, or workflow inside fn body emits warning
67
+ *
68
+ * NOTE: Import restrictions have been removed - any file can import any other file.
122
69
  *
123
70
  * @param {string|Buffer} source
124
71
  * @param {any} inputMap
@@ -143,13 +90,11 @@ export default function workflowValidatorLoader( source, inputMap ) {
143
90
  const importedEvaluatorIds = new Set();
144
91
  const importedWorkflowIds = new Set();
145
92
 
146
- // First pass: module-level import validation + collect imported ids
93
+ // First pass: collect imported identifiers for fn body call checks
147
94
  traverse( ast, {
148
95
  ImportDeclaration: path => {
149
96
  const specifier = path.node.source.value;
150
97
 
151
- executeImportValidations( { fileKind, specifier, filename, emitWarning } );
152
-
153
98
  // Collect imported identifiers for later call checks
154
99
  const importedKind = getFileKind( specifier );
155
100
  const accumulator = ( {
@@ -188,35 +133,32 @@ export default function workflowValidatorLoader( source, inputMap ) {
188
133
  validateInstantiationLocation( 'workflow', filename );
189
134
  }
190
135
 
191
- // CommonJS requires: validate source and collect identifiers
136
+ // CommonJS requires: collect identifiers for fn body call checks
192
137
  if ( isIdentifier( init.callee, { name: 'require' } ) ) {
193
138
  const firstArg = init.arguments[0];
194
139
  if ( !isStringLiteral( firstArg ) ) {
195
140
  return;
196
141
  }
197
142
  const req = firstArg.value;
198
- executeImportValidations( { fileKind, specifier: req, filename, emitWarning } );
199
143
 
200
144
  // Collect imported identifiers from require patterns
201
- if ( isStringLiteral( firstArg ) ) {
202
- const reqType = getFileKind( toAbsolutePath( fileDir, req ) );
203
- if ( reqType === ComponentFile.STEPS && isObjectPattern( path.node.id ) ) {
204
- for ( const prop of path.node.id.properties ) {
205
- if ( isObjectProperty( prop ) && isIdentifier( prop.value ) ) {
206
- importedStepIds.add( prop.value.name );
207
- }
145
+ const reqType = getFileKind( toAbsolutePath( fileDir, req ) );
146
+ if ( reqType === ComponentFile.STEPS && isObjectPattern( path.node.id ) ) {
147
+ for ( const prop of path.node.id.properties ) {
148
+ if ( isObjectProperty( prop ) && isIdentifier( prop.value ) ) {
149
+ importedStepIds.add( prop.value.name );
208
150
  }
209
151
  }
210
- if ( reqType === ComponentFile.EVALUATORS && isObjectPattern( path.node.id ) ) {
211
- for ( const prop of path.node.id.properties ) {
212
- if ( isObjectProperty( prop ) && isIdentifier( prop.value ) ) {
213
- importedEvaluatorIds.add( prop.value.name );
214
- }
152
+ }
153
+ if ( reqType === ComponentFile.EVALUATORS && isObjectPattern( path.node.id ) ) {
154
+ for ( const prop of path.node.id.properties ) {
155
+ if ( isObjectProperty( prop ) && isIdentifier( prop.value ) ) {
156
+ importedEvaluatorIds.add( prop.value.name );
215
157
  }
216
158
  }
217
- if ( reqType === ComponentFile.WORKFLOW && isIdentifier( path.node.id ) ) {
218
- importedWorkflowIds.add( path.node.id.name );
219
- }
159
+ }
160
+ if ( reqType === ComponentFile.WORKFLOW && isIdentifier( path.node.id ) ) {
161
+ importedWorkflowIds.add( path.node.id.name );
220
162
  }
221
163
  }
222
164
  }
@@ -54,12 +54,11 @@ describe( 'workflow_validator loader', () => {
54
54
  rmSync( dir, { recursive: true, force: true } );
55
55
  } );
56
56
 
57
- it( 'steps.js: warns when importing steps/evaluators/workflow', async () => {
58
- const dir = mkdtempSync( join( tmpdir(), 'steps-reject-' ) );
57
+ it( 'steps.js: allows importing steps/evaluators/workflow (no import restrictions)', async () => {
58
+ const dir = mkdtempSync( join( tmpdir(), 'steps-allow-import-' ) );
59
59
  const src = 'import { S } from "./steps.js";';
60
60
  const result = await runLoader( join( dir, 'steps.js' ), src );
61
- expect( result.warnings ).toHaveLength( 1 );
62
- expect( result.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in steps\.js/ );
61
+ expect( result.warnings ).toHaveLength( 0 );
63
62
  rmSync( dir, { recursive: true, force: true } );
64
63
  } );
65
64
 
@@ -70,12 +69,11 @@ describe( 'workflow_validator loader', () => {
70
69
  rmSync( dir, { recursive: true, force: true } );
71
70
  } );
72
71
 
73
- it( 'evaluators.js: warns when importing evaluators/steps/workflow', async () => {
74
- const dir = mkdtempSync( join( tmpdir(), 'evals-reject-' ) );
72
+ it( 'evaluators.js: allows importing evaluators/steps/workflow (no import restrictions)', async () => {
73
+ const dir = mkdtempSync( join( tmpdir(), 'evals-allow-import-' ) );
75
74
  const src = 'import { E } from "./evaluators.js";';
76
75
  const result = await runLoader( join( dir, 'evaluators.js' ), src );
77
- expect( result.warnings ).toHaveLength( 1 );
78
- expect( result.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in evaluators\.js/ );
76
+ expect( result.warnings ).toHaveLength( 0 );
79
77
  rmSync( dir, { recursive: true, force: true } );
80
78
  } );
81
79
 
@@ -140,25 +138,21 @@ describe( 'workflow_validator loader', () => {
140
138
  rmSync( dir, { recursive: true, force: true } );
141
139
  } );
142
140
 
143
- it( 'steps.js: warns when importing evaluators/workflow variants', async () => {
144
- const dir = mkdtempSync( join( tmpdir(), 'steps-reject2-' ) );
141
+ it( 'steps.js: allows importing evaluators/workflow variants (no import restrictions)', async () => {
142
+ const dir = mkdtempSync( join( tmpdir(), 'steps-allow-import2-' ) );
145
143
  const result1 = await runLoader( join( dir, 'steps.js' ), 'import { E } from "./evaluators.js";' );
146
- expect( result1.warnings ).toHaveLength( 1 );
147
- expect( result1.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in steps\.js/ );
144
+ expect( result1.warnings ).toHaveLength( 0 );
148
145
  const result2 = await runLoader( join( dir, 'steps.js' ), 'import WF from "./workflow.js";' );
149
- expect( result2.warnings ).toHaveLength( 1 );
150
- expect( result2.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in steps\.js/ );
146
+ expect( result2.warnings ).toHaveLength( 0 );
151
147
  rmSync( dir, { recursive: true, force: true } );
152
148
  } );
153
149
 
154
- it( 'evaluators.js: warns when importing steps/workflow variants', async () => {
155
- const dir = mkdtempSync( join( tmpdir(), 'evals-reject2-' ) );
150
+ it( 'evaluators.js: allows importing steps/workflow variants (no import restrictions)', async () => {
151
+ const dir = mkdtempSync( join( tmpdir(), 'evals-allow-import2-' ) );
156
152
  const result1 = await runLoader( join( dir, 'evaluators.js' ), 'import { S } from "./steps.js";' );
157
- expect( result1.warnings ).toHaveLength( 1 );
158
- expect( result1.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in evaluators\.js/ );
153
+ expect( result1.warnings ).toHaveLength( 0 );
159
154
  const result2 = await runLoader( join( dir, 'evaluators.js' ), 'import WF from "./workflow.js";' );
160
- expect( result2.warnings ).toHaveLength( 1 );
161
- expect( result2.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in evaluators\.js/ );
155
+ expect( result2.warnings ).toHaveLength( 0 );
162
156
  rmSync( dir, { recursive: true, force: true } );
163
157
  } );
164
158
 
@@ -190,31 +184,26 @@ describe( 'workflow_validator loader', () => {
190
184
  rmSync( dir, { recursive: true, force: true } );
191
185
  } );
192
186
 
193
- it( 'steps.js: warns on require of steps/evaluators/workflow; allows other require', async () => {
194
- const dir = mkdtempSync( join( tmpdir(), 'steps-require-' ) );
187
+ it( 'steps.js: allows require of steps/evaluators/workflow (no import restrictions)', async () => {
188
+ const dir = mkdtempSync( join( tmpdir(), 'steps-require-allow-' ) );
195
189
  const result1 = await runLoader( join( dir, 'steps.js' ), 'const { S } = require("./steps.js");' );
196
- expect( result1.warnings ).toHaveLength( 1 );
197
- expect( result1.warnings[0].message ).toMatch( /Invalid (require|dependency) in steps\.js/ );
190
+ expect( result1.warnings ).toHaveLength( 0 );
198
191
  const result2 = await runLoader( join( dir, 'steps.js' ), 'const { E } = require("./evaluators.js");' );
199
- expect( result2.warnings ).toHaveLength( 1 );
200
- expect( result2.warnings[0].message ).toMatch( /Invalid (require|dependency) in steps\.js/ );
192
+ expect( result2.warnings ).toHaveLength( 0 );
201
193
  const result3 = await runLoader( join( dir, 'steps.js' ), 'const W = require("./workflow.js");' );
202
- expect( result3.warnings ).toHaveLength( 1 );
203
- expect( result3.warnings[0].message ).toMatch( /Invalid (require|dependency) in steps\.js/ );
194
+ expect( result3.warnings ).toHaveLength( 0 );
204
195
  const ok = 'const util = require("./util.js");';
205
196
  const resultOk = await runLoader( join( dir, 'steps.js' ), ok );
206
197
  expect( resultOk.warnings ).toHaveLength( 0 );
207
198
  rmSync( dir, { recursive: true, force: true } );
208
199
  } );
209
200
 
210
- it( 'evaluators.js: warns on require of steps/workflow; allows other require', async () => {
211
- const dir = mkdtempSync( join( tmpdir(), 'evals-require-' ) );
201
+ it( 'evaluators.js: allows require of steps/workflow (no import restrictions)', async () => {
202
+ const dir = mkdtempSync( join( tmpdir(), 'evals-require-allow-' ) );
212
203
  const result1 = await runLoader( join( dir, 'evaluators.js' ), 'const { S } = require("./steps.js");' );
213
- expect( result1.warnings ).toHaveLength( 1 );
214
- expect( result1.warnings[0].message ).toMatch( /Invalid (require|dependency) in evaluators\.js/ );
204
+ expect( result1.warnings ).toHaveLength( 0 );
215
205
  const result2 = await runLoader( join( dir, 'evaluators.js' ), 'const W = require("./workflow.js");' );
216
- expect( result2.warnings ).toHaveLength( 1 );
217
- expect( result2.warnings[0].message ).toMatch( /Invalid (require|dependency) in evaluators\.js/ );
206
+ expect( result2.warnings ).toHaveLength( 0 );
218
207
  const ok = 'const util = require("./util.js");';
219
208
  const resultOk = await runLoader( join( dir, 'evaluators.js' ), ok );
220
209
  expect( resultOk.warnings ).toHaveLength( 0 );
@@ -337,52 +326,48 @@ describe( 'workflow_validator loader', () => {
337
326
  } );
338
327
  } );
339
328
 
340
- describe( 'activity isolation - shared imports', () => {
341
- it( 'steps.ts: warns on imports from ../../shared/steps/common.js (activity isolation)', async () => {
342
- const dir = mkdtempSync( join( tmpdir(), 'steps-shared-steps-reject-' ) );
329
+ describe( 'cross-component imports - now allowed (no import restrictions)', () => {
330
+ it( 'steps.ts: allows imports from ../../shared/steps/common.js', async () => {
331
+ const dir = mkdtempSync( join( tmpdir(), 'steps-shared-steps-allow-' ) );
343
332
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
344
333
  mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
345
334
  writeFileSync( join( dir, 'shared', 'steps', 'common.js' ), 'export const commonStep = step({ name: "common" });\n' );
346
335
  const src = 'import { commonStep } from "../../shared/steps/common.js";';
347
336
  const result = await runLoader( join( dir, 'workflows', 'my_workflow', 'steps.js' ), src );
348
- expect( result.warnings ).toHaveLength( 1 );
349
- expect( result.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in steps\.js/ );
337
+ expect( result.warnings ).toHaveLength( 0 );
350
338
  rmSync( dir, { recursive: true, force: true } );
351
339
  } );
352
340
 
353
- it( 'steps.ts: warns on imports from ../../shared/evaluators/quality.js (activity isolation)', async () => {
354
- const dir = mkdtempSync( join( tmpdir(), 'steps-shared-evals-reject-' ) );
341
+ it( 'steps.ts: allows imports from ../../shared/evaluators/quality.js', async () => {
342
+ const dir = mkdtempSync( join( tmpdir(), 'steps-shared-evals-allow-' ) );
355
343
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
356
344
  mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
357
345
  writeFileSync( join( dir, 'shared', 'evaluators', 'quality.js' ), 'export const qualityEval = evaluator({ name: "quality" });\n' );
358
346
  const src = 'import { qualityEval } from "../../shared/evaluators/quality.js";';
359
347
  const result = await runLoader( join( dir, 'workflows', 'my_workflow', 'steps.js' ), src );
360
- expect( result.warnings ).toHaveLength( 1 );
361
- expect( result.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in steps\.js/ );
348
+ expect( result.warnings ).toHaveLength( 0 );
362
349
  rmSync( dir, { recursive: true, force: true } );
363
350
  } );
364
351
 
365
- it( 'evaluators.ts: warns on imports from ../../shared/steps/common.js (activity isolation)', async () => {
366
- const dir = mkdtempSync( join( tmpdir(), 'evals-shared-steps-reject-' ) );
352
+ it( 'evaluators.ts: allows imports from ../../shared/steps/common.js', async () => {
353
+ const dir = mkdtempSync( join( tmpdir(), 'evals-shared-steps-allow-' ) );
367
354
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
368
355
  mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
369
356
  writeFileSync( join( dir, 'shared', 'steps', 'common.js' ), 'export const commonStep = step({ name: "common" });\n' );
370
357
  const src = 'import { commonStep } from "../../shared/steps/common.js";';
371
358
  const result = await runLoader( join( dir, 'workflows', 'my_workflow', 'evaluators.js' ), src );
372
- expect( result.warnings ).toHaveLength( 1 );
373
- expect( result.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in evaluators\.js/ );
359
+ expect( result.warnings ).toHaveLength( 0 );
374
360
  rmSync( dir, { recursive: true, force: true } );
375
361
  } );
376
362
 
377
- it( 'evaluators.ts: warns on imports from ../../shared/evaluators/other.js (activity isolation)', async () => {
378
- const dir = mkdtempSync( join( tmpdir(), 'evals-shared-evals-reject-' ) );
363
+ it( 'evaluators.ts: allows imports from ../../shared/evaluators/other.js', async () => {
364
+ const dir = mkdtempSync( join( tmpdir(), 'evals-shared-evals-allow-' ) );
379
365
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
380
366
  mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
381
367
  writeFileSync( join( dir, 'shared', 'evaluators', 'other.js' ), 'export const otherEval = evaluator({ name: "other" });\n' );
382
368
  const src = 'import { otherEval } from "../../shared/evaluators/other.js";';
383
369
  const result = await runLoader( join( dir, 'workflows', 'my_workflow', 'evaluators.js' ), src );
384
- expect( result.warnings ).toHaveLength( 1 );
385
- expect( result.warnings[0].message ).toMatch( /Invalid (import|imports|dependency) in evaluators\.js/ );
370
+ expect( result.warnings ).toHaveLength( 0 );
386
371
  rmSync( dir, { recursive: true, force: true } );
387
372
  } );
388
373
  } );