@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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
143
|
+
it( 'steps.js: warns when importing evaluators/workflow variants', async () => {
|
|
134
144
|
const dir = mkdtempSync( join( tmpdir(), 'steps-reject2-' ) );
|
|
135
|
-
await
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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:
|
|
154
|
+
it( 'evaluators.js: warns when importing steps/workflow variants', async () => {
|
|
143
155
|
const dir = mkdtempSync( join( tmpdir(), 'evals-reject2-' ) );
|
|
144
|
-
await
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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:
|
|
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
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
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:
|
|
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
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
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:
|
|
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
|
|
327
|
-
|
|
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:
|
|
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
|
|
338
|
-
|
|
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:
|
|
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
|
|
349
|
-
|
|
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:
|
|
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
|
|
360
|
-
|
|
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
|
} );
|