@output.ai/core 0.3.0-dev.pr263-8f8e94a → 0.3.0-dev.pr263-7879ff1

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-8f8e94a",
3
+ "version": "0.3.0-dev.pr263-7879ff1",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -43,15 +43,15 @@ const getFileKindLabel = filename => {
43
43
  * - Core modules (@output.ai/core, local_core)
44
44
  * - ANY file that is NOT a component file (flexible utility imports)
45
45
  */
46
- const validateWorkflowImports = ( { specifier, filename } ) => {
46
+ const validateWorkflowImports = ( { specifier, filename, emitWarning } ) => {
47
47
  const isCore = Object.values( CoreModule ).includes( specifier );
48
48
  const fileKind = getFileKind( specifier );
49
49
  const isComponent = Object.values( ComponentFile ).includes( fileKind );
50
50
  const isNonComponentFile = fileKind === null;
51
51
 
52
52
  if ( !isCore && !isComponent && !isNonComponentFile ) {
53
- throw 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}` );
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
55
  }
56
56
  };
57
57
 
@@ -65,25 +65,25 @@ Only components (${Object.values( ComponentFile ) } ), @output.ai/core, or non-c
65
65
  * Steps and evaluators CAN import:
66
66
  * - ANY file that is NOT a component file (flexible utility imports)
67
67
  */
68
- const validateStepEvaluatorImports = ( { specifier, filename } ) => {
68
+ const validateStepEvaluatorImports = ( { specifier, filename, emitWarning } ) => {
69
69
  const importedFileKind = getFileKind( specifier );
70
70
 
71
71
  // Activity isolation: steps/evaluators cannot import other steps, evaluators, or workflows
72
72
  if ( Object.values( ComponentFile ).includes( importedFileKind ) ) {
73
73
  const fileLabel = getFileKindLabel( filename );
74
- throw new Error( `Invalid dependency in ${fileLabel}: '${specifier}'. \
75
- Steps, evaluators or workflows are not allowed dependencies in ${filename}` );
74
+ emitWarning( new Error( `Invalid dependency in ${fileLabel}: '${specifier}'. \
75
+ Steps, evaluators or workflows are not allowed dependencies in ${filename}` ) );
76
76
  }
77
77
  };
78
78
 
79
79
  /**
80
80
  * Validate import for evaluators, steps, workflow
81
81
  */
82
- const executeImportValidations = ( { fileKind, specifier, filename } ) => {
82
+ const executeImportValidations = ( { fileKind, specifier, filename, emitWarning } ) => {
83
83
  if ( fileKind === ComponentFile.WORKFLOW ) {
84
- validateWorkflowImports( { specifier, filename } );
84
+ validateWorkflowImports( { specifier, filename, emitWarning } );
85
85
  } else if ( Object.values( ComponentFile ).includes( fileKind ) ) {
86
- validateStepEvaluatorImports( { specifier, filename } );
86
+ validateStepEvaluatorImports( { specifier, filename, emitWarning } );
87
87
  }
88
88
  };
89
89
 
@@ -127,6 +127,7 @@ const validateInstantiationLocation = ( calleeName, filename ) => {
127
127
  export default function workflowValidatorLoader( source, inputMap ) {
128
128
  this.cacheable?.( true );
129
129
  const callback = this.async?.() ?? this.callback;
130
+ const emitWarning = this.emitWarning?.bind( this ) ?? ( () => {} );
130
131
 
131
132
  try {
132
133
  const filename = this.resourcePath;
@@ -147,7 +148,7 @@ export default function workflowValidatorLoader( source, inputMap ) {
147
148
  ImportDeclaration: path => {
148
149
  const specifier = path.node.source.value;
149
150
 
150
- executeImportValidations( { fileKind, specifier, filename } );
151
+ executeImportValidations( { fileKind, specifier, filename, emitWarning } );
151
152
 
152
153
  // Collect imported identifiers for later call checks
153
154
  const importedKind = getFileKind( specifier );
@@ -194,7 +195,7 @@ export default function workflowValidatorLoader( source, inputMap ) {
194
195
  return;
195
196
  }
196
197
  const req = firstArg.value;
197
- executeImportValidations( { fileKind, specifier: req, filename } );
198
+ executeImportValidations( { fileKind, specifier: req, filename, emitWarning } );
198
199
 
199
200
  // Collect imported identifiers from require patterns
200
201
  if ( isStringLiteral( firstArg ) ) {
@@ -246,7 +247,7 @@ export default function workflowValidatorLoader( source, inputMap ) {
246
247
  ].find( v => v[1] )?.[0];
247
248
 
248
249
  if ( violation ) {
249
- throw new Error( `Invalid call in ${fileLabel} fn: calling a ${violation} ('${name}') is not allowed in ${filename}` );
250
+ emitWarning( new Error( `Invalid call in ${fileLabel} fn: calling a ${violation} ('${name}') is not allowed in ${filename}` ) );
250
251
  }
251
252
  }
252
253
  }
@@ -6,11 +6,13 @@ import validatorLoader from './index.mjs';
6
6
 
7
7
  function runLoader( filename, source ) {
8
8
  return new Promise( ( resolve, reject ) => {
9
+ const warnings = [];
9
10
  const ctx = {
10
11
  resourcePath: filename,
11
12
  cacheable: () => {},
12
- async: () => ( err, code, map ) => ( err ? reject( err ) : resolve( { code, map } ) ),
13
- callback: ( err, code, map ) => ( err ? reject( err ) : resolve( { code, map } ) )
13
+ emitWarning: err => warnings.push( err ),
14
+ async: () => ( err, code, map ) => ( err ? reject( err ) : resolve( { code, map, warnings } ) ),
15
+ callback: ( err, code, map ) => ( err ? reject( err ) : resolve( { code, map, warnings } ) )
14
16
  };
15
17
  validatorLoader.call( ctx, source, null );
16
18
  } );
@@ -52,10 +54,12 @@ describe( 'workflow_validator loader', () => {
52
54
  rmSync( dir, { recursive: true, force: true } );
53
55
  } );
54
56
 
55
- it( 'steps.js: rejects importing steps/evaluators/workflow', async () => {
57
+ it( 'steps.js: warns when importing steps/evaluators/workflow', async () => {
56
58
  const dir = mkdtempSync( join( tmpdir(), 'steps-reject-' ) );
57
59
  const src = 'import { S } from "./steps.js";';
58
- await expect( runLoader( join( dir, 'steps.js' ), src ) ).rejects.toThrow( /Invalid (import|imports|dependency) in steps\.js/ );
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/ );
59
63
  rmSync( dir, { recursive: true, force: true } );
60
64
  } );
61
65
 
@@ -66,14 +70,16 @@ describe( 'workflow_validator loader', () => {
66
70
  rmSync( dir, { recursive: true, force: true } );
67
71
  } );
68
72
 
69
- it( 'evaluators.js: rejects importing evaluators/steps/workflow', async () => {
73
+ it( 'evaluators.js: warns when importing evaluators/steps/workflow', async () => {
70
74
  const dir = mkdtempSync( join( tmpdir(), 'evals-reject-' ) );
71
75
  const src = 'import { E } from "./evaluators.js";';
72
- await expect( runLoader( join( dir, 'evaluators.js' ), src ) ).rejects.toThrow( /Invalid (import|imports|dependency) in evaluators\.js/ );
76
+ 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/ );
73
79
  rmSync( dir, { recursive: true, force: true } );
74
80
  } );
75
81
 
76
- it( 'steps.js: rejects calling another step inside fn', async () => {
82
+ it( 'steps.js: warns when calling another step inside fn', async () => {
77
83
  const dir = mkdtempSync( join( tmpdir(), 'steps-call-reject-' ) );
78
84
  // Can only test same-type components since cross-type declarations are now blocked by instantiation validation
79
85
  const src = [
@@ -81,11 +87,13 @@ describe( 'workflow_validator loader', () => {
81
87
  'const B = step({ name: "b" });',
82
88
  'const obj = { fn: function() { B(); } };'
83
89
  ].join( '\n' );
84
- await expect( runLoader( join( dir, 'steps.js' ), src ) ).rejects.toThrow( /Invalid call in .*\.js fn/ );
90
+ const result = await runLoader( join( dir, 'steps.js' ), src );
91
+ expect( result.warnings ).toHaveLength( 1 );
92
+ expect( result.warnings[0].message ).toMatch( /Invalid call in .*\.js fn/ );
85
93
  rmSync( dir, { recursive: true, force: true } );
86
94
  } );
87
95
 
88
- it( 'evaluators.js: rejects calling another evaluator inside fn', async () => {
96
+ it( 'evaluators.js: warns when calling another evaluator inside fn', async () => {
89
97
  const dir = mkdtempSync( join( tmpdir(), 'evals-call-reject-' ) );
90
98
  // Can only test same-type components since cross-type declarations are now blocked by instantiation validation
91
99
  const src = [
@@ -93,7 +101,9 @@ describe( 'workflow_validator loader', () => {
93
101
  'const E2 = evaluator({ name: "e2" });',
94
102
  'const obj = { fn: function() { E2(); } };'
95
103
  ].join( '\n' );
96
- await expect( runLoader( join( dir, 'evaluators.js' ), src ) ).rejects.toThrow( /Invalid call in .*\.js fn/ );
104
+ const result = await runLoader( join( dir, 'evaluators.js' ), src );
105
+ expect( result.warnings ).toHaveLength( 1 );
106
+ expect( result.warnings[0].message ).toMatch( /Invalid call in .*\.js fn/ );
97
107
  rmSync( dir, { recursive: true, force: true } );
98
108
  } );
99
109
 
@@ -130,21 +140,25 @@ describe( 'workflow_validator loader', () => {
130
140
  rmSync( dir, { recursive: true, force: true } );
131
141
  } );
132
142
 
133
- it( 'steps.js: rejects importing evaluators/workflow variants', async () => {
143
+ it( 'steps.js: warns when importing evaluators/workflow variants', async () => {
134
144
  const dir = mkdtempSync( join( tmpdir(), 'steps-reject2-' ) );
135
- await expect( runLoader( join( dir, 'steps.js' ), 'import { E } from "./evaluators.js";' ) )
136
- .rejects.toThrow( /Invalid (import|imports|dependency) in steps\.js/ );
137
- await expect( runLoader( join( dir, 'steps.js' ), 'import WF from "./workflow.js";' ) )
138
- .rejects.toThrow( /Invalid (import|imports|dependency) in steps\.js/ );
145
+ 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/ );
148
+ 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/ );
139
151
  rmSync( dir, { recursive: true, force: true } );
140
152
  } );
141
153
 
142
- it( 'evaluators.js: rejects importing steps/workflow variants', async () => {
154
+ it( 'evaluators.js: warns when importing steps/workflow variants', async () => {
143
155
  const dir = mkdtempSync( join( tmpdir(), 'evals-reject2-' ) );
144
- await expect( runLoader( join( dir, 'evaluators.js' ), 'import { S } from "./steps.js";' ) )
145
- .rejects.toThrow( /Invalid (import|imports|dependency) in evaluators\.js/ );
146
- await expect( runLoader( join( dir, 'evaluators.js' ), 'import WF from "./workflow.js";' ) )
147
- .rejects.toThrow( /Invalid (import|imports|dependency) in evaluators\.js/ );
156
+ 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/ );
159
+ 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/ );
148
162
  rmSync( dir, { recursive: true, force: true } );
149
163
  } );
150
164
 
@@ -176,27 +190,34 @@ describe( 'workflow_validator loader', () => {
176
190
  rmSync( dir, { recursive: true, force: true } );
177
191
  } );
178
192
 
179
- it( 'steps.js: rejects require of steps/evaluators/workflow; allows other require', async () => {
193
+ it( 'steps.js: warns on require of steps/evaluators/workflow; allows other require', async () => {
180
194
  const dir = mkdtempSync( join( tmpdir(), 'steps-require-' ) );
181
- await expect( runLoader( join( dir, 'steps.js' ), 'const { S } = require("./steps.js");' ) )
182
- .rejects.toThrow( /Invalid (require|dependency) in steps\.js/ );
183
- await expect( runLoader( join( dir, 'steps.js' ), 'const { E } = require("./evaluators.js");' ) )
184
- .rejects.toThrow( /Invalid (require|dependency) in steps\.js/ );
185
- await expect( runLoader( join( dir, 'steps.js' ), 'const W = require("./workflow.js");' ) )
186
- .rejects.toThrow( /Invalid (require|dependency) in steps\.js/ );
195
+ 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/ );
198
+ 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/ );
201
+ 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/ );
187
204
  const ok = 'const util = require("./util.js");';
188
- await expect( runLoader( join( dir, 'steps.js' ), ok ) ).resolves.toBeTruthy();
205
+ const resultOk = await runLoader( join( dir, 'steps.js' ), ok );
206
+ expect( resultOk.warnings ).toHaveLength( 0 );
189
207
  rmSync( dir, { recursive: true, force: true } );
190
208
  } );
191
209
 
192
- it( 'evaluators.js: rejects require of steps/workflow; allows other require', async () => {
210
+ it( 'evaluators.js: warns on require of steps/workflow; allows other require', async () => {
193
211
  const dir = mkdtempSync( join( tmpdir(), 'evals-require-' ) );
194
- await expect( runLoader( join( dir, 'evaluators.js' ), 'const { S } = require("./steps.js");' ) )
195
- .rejects.toThrow( /Invalid (require|dependency) in evaluators\.js/ );
196
- await expect( runLoader( join( dir, 'evaluators.js' ), 'const W = require("./workflow.js");' ) )
197
- .rejects.toThrow( /Invalid (require|dependency) in evaluators\.js/ );
212
+ 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/ );
215
+ 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/ );
198
218
  const ok = 'const util = require("./util.js");';
199
- await expect( runLoader( join( dir, 'evaluators.js' ), ok ) ).resolves.toBeTruthy();
219
+ const resultOk = await runLoader( join( dir, 'evaluators.js' ), ok );
220
+ expect( resultOk.warnings ).toHaveLength( 0 );
200
221
  rmSync( dir, { recursive: true, force: true } );
201
222
  } );
202
223
 
@@ -317,47 +338,51 @@ describe( 'workflow_validator loader', () => {
317
338
  } );
318
339
 
319
340
  describe( 'activity isolation - shared imports', () => {
320
- it( 'steps.ts: rejects imports from ../../shared/steps/common.js (activity isolation)', async () => {
341
+ it( 'steps.ts: warns on imports from ../../shared/steps/common.js (activity isolation)', async () => {
321
342
  const dir = mkdtempSync( join( tmpdir(), 'steps-shared-steps-reject-' ) );
322
343
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
323
344
  mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
324
345
  writeFileSync( join( dir, 'shared', 'steps', 'common.js' ), 'export const commonStep = step({ name: "common" });\n' );
325
346
  const src = 'import { commonStep } from "../../shared/steps/common.js";';
326
- await expect( runLoader( join( dir, 'workflows', 'my_workflow', 'steps.js' ), src ) )
327
- .rejects.toThrow( /Invalid (import|imports|dependency) in steps\.js/ );
347
+ 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/ );
328
350
  rmSync( dir, { recursive: true, force: true } );
329
351
  } );
330
352
 
331
- it( 'steps.ts: rejects imports from ../../shared/evaluators/quality.js (activity isolation)', async () => {
353
+ it( 'steps.ts: warns on imports from ../../shared/evaluators/quality.js (activity isolation)', async () => {
332
354
  const dir = mkdtempSync( join( tmpdir(), 'steps-shared-evals-reject-' ) );
333
355
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
334
356
  mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
335
357
  writeFileSync( join( dir, 'shared', 'evaluators', 'quality.js' ), 'export const qualityEval = evaluator({ name: "quality" });\n' );
336
358
  const src = 'import { qualityEval } from "../../shared/evaluators/quality.js";';
337
- await expect( runLoader( join( dir, 'workflows', 'my_workflow', 'steps.js' ), src ) )
338
- .rejects.toThrow( /Invalid (import|imports|dependency) in steps\.js/ );
359
+ 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/ );
339
362
  rmSync( dir, { recursive: true, force: true } );
340
363
  } );
341
364
 
342
- it( 'evaluators.ts: rejects imports from ../../shared/steps/common.js (activity isolation)', async () => {
365
+ it( 'evaluators.ts: warns on imports from ../../shared/steps/common.js (activity isolation)', async () => {
343
366
  const dir = mkdtempSync( join( tmpdir(), 'evals-shared-steps-reject-' ) );
344
367
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
345
368
  mkdirSync( join( dir, 'shared', 'steps' ), { recursive: true } );
346
369
  writeFileSync( join( dir, 'shared', 'steps', 'common.js' ), 'export const commonStep = step({ name: "common" });\n' );
347
370
  const src = 'import { commonStep } from "../../shared/steps/common.js";';
348
- await expect( runLoader( join( dir, 'workflows', 'my_workflow', 'evaluators.js' ), src ) )
349
- .rejects.toThrow( /Invalid (import|imports|dependency) in evaluators\.js/ );
371
+ 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/ );
350
374
  rmSync( dir, { recursive: true, force: true } );
351
375
  } );
352
376
 
353
- it( 'evaluators.ts: rejects imports from ../../shared/evaluators/other.js (activity isolation)', async () => {
377
+ it( 'evaluators.ts: warns on imports from ../../shared/evaluators/other.js (activity isolation)', async () => {
354
378
  const dir = mkdtempSync( join( tmpdir(), 'evals-shared-evals-reject-' ) );
355
379
  mkdirSync( join( dir, 'workflows', 'my_workflow' ), { recursive: true } );
356
380
  mkdirSync( join( dir, 'shared', 'evaluators' ), { recursive: true } );
357
381
  writeFileSync( join( dir, 'shared', 'evaluators', 'other.js' ), 'export const otherEval = evaluator({ name: "other" });\n' );
358
382
  const src = 'import { otherEval } from "../../shared/evaluators/other.js";';
359
- await expect( runLoader( join( dir, 'workflows', 'my_workflow', 'evaluators.js' ), src ) )
360
- .rejects.toThrow( /Invalid (import|imports|dependency) in evaluators\.js/ );
383
+ 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/ );
361
386
  rmSync( dir, { recursive: true, force: true } );
362
387
  } );
363
388
  } );