@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,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
|
|
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
|
|
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
|
-
* -
|
|
117
|
-
* - evaluators.js
|
|
118
|
-
*
|
|
119
|
-
*
|
|
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:
|
|
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:
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
218
|
-
|
|
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:
|
|
58
|
-
const dir = mkdtempSync( join( tmpdir(), 'steps-
|
|
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(
|
|
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:
|
|
74
|
-
const dir = mkdtempSync( join( tmpdir(), 'evals-
|
|
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(
|
|
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:
|
|
144
|
-
const dir = mkdtempSync( join( tmpdir(), 'steps-
|
|
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(
|
|
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(
|
|
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:
|
|
155
|
-
const dir = mkdtempSync( join( tmpdir(), 'evals-
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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( '
|
|
341
|
-
it( 'steps.ts:
|
|
342
|
-
const dir = mkdtempSync( join( tmpdir(), 'steps-shared-steps-
|
|
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(
|
|
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:
|
|
354
|
-
const dir = mkdtempSync( join( tmpdir(), 'steps-shared-evals-
|
|
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(
|
|
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:
|
|
366
|
-
const dir = mkdtempSync( join( tmpdir(), 'evals-shared-steps-
|
|
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(
|
|
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:
|
|
378
|
-
const dir = mkdtempSync( join( tmpdir(), 'evals-shared-evals-
|
|
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(
|
|
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
|
} );
|