@output.ai/core 0.0.15 → 0.0.16
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/bin/worker.sh +1 -1
- package/package.json +5 -8
- package/src/consts.js +8 -1
- package/src/index.d.ts +169 -7
- package/src/index.js +18 -1
- package/src/interface/evaluator.js +146 -0
- package/src/interface/step.js +4 -9
- package/src/interface/{schema_utils.js → validations/runtime.js} +0 -14
- package/src/interface/validations/runtime.spec.js +29 -0
- package/src/interface/validations/schema_utils.js +8 -0
- package/src/interface/validations/static.js +13 -1
- package/src/interface/validations/static.spec.js +29 -1
- package/src/interface/workflow.js +3 -8
- package/src/internal_activities/index.js +6 -0
- package/src/worker/catalog_workflow/catalog.js +19 -10
- package/src/worker/index.js +1 -1
- package/src/worker/interceptors/activity.js +16 -3
- package/src/worker/loader.js +2 -2
- package/src/worker/tracer/index.js +1 -1
- package/src/worker/tracer/index.test.js +1 -2
- package/src/worker/tracer/tracer_tree.js +4 -3
- package/src/worker/tracer/tracer_tree.test.js +1 -1
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +35 -4
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +12 -4
- package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +5 -4
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.js +13 -4
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_fn_bodies.spec.js +16 -2
- package/src/worker/webpack_loaders/workflow_rewriter/tools.js +46 -13
- package/src/worker/webpack_loaders/workflow_rewriter/tools.spec.js +20 -2
- /package/src/interface/{schema_utils.spec.js → validations/schema_utils.spec.js} +0 -0
package/bin/worker.sh
CHANGED
|
@@ -18,7 +18,7 @@ fi
|
|
|
18
18
|
# Get the real script directory (should be node_modules/<pkg>/bin)
|
|
19
19
|
script_dir="$(cd "$(dirname "$real_script")" && pwd)"
|
|
20
20
|
|
|
21
|
-
# SDK dir is the parent (node_modules/
|
|
21
|
+
# SDK dir is the parent (node_modules/output-core)
|
|
22
22
|
sdk_dir="$(dirname "$script_dir")"
|
|
23
23
|
|
|
24
24
|
cd ${sdk_dir}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@output.ai/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "The core module of the output framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -9,16 +9,12 @@
|
|
|
9
9
|
"./src",
|
|
10
10
|
"./bin"
|
|
11
11
|
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"worker": "node ./src/worker/index.js"
|
|
14
|
-
},
|
|
15
12
|
"bin": {
|
|
16
|
-
"
|
|
13
|
+
"output-worker": "./bin/worker.sh",
|
|
17
14
|
"outputai": "./bin/worker.sh"
|
|
18
15
|
},
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"url": "git+https://github.com/growthxai/flow-sdk"
|
|
16
|
+
"scripts": {
|
|
17
|
+
"worker": "node ./src/worker/index.js"
|
|
22
18
|
},
|
|
23
19
|
"dependencies": {
|
|
24
20
|
"@babel/generator": "7.28.3",
|
|
@@ -29,6 +25,7 @@
|
|
|
29
25
|
"@temporalio/workflow": "1.13.0",
|
|
30
26
|
"zod": "4.1.9"
|
|
31
27
|
},
|
|
28
|
+
"license": "UNLICENSED",
|
|
32
29
|
"imports": {
|
|
33
30
|
"#consts": "./src/consts.js",
|
|
34
31
|
"#configs": "./src/configs.js",
|
package/src/consts.js
CHANGED
|
@@ -7,5 +7,12 @@ export const TraceEvent = {
|
|
|
7
7
|
WORKFLOW_START: 'workflow_start',
|
|
8
8
|
WORKFLOW_END: 'workflow_end',
|
|
9
9
|
STEP_START: 'step_start',
|
|
10
|
-
STEP_END: 'step_end'
|
|
10
|
+
STEP_END: 'step_end',
|
|
11
|
+
EVALUATOR_START: 'evaluator_start',
|
|
12
|
+
EVALUATOR_END: 'evaluator_end'
|
|
13
|
+
};
|
|
14
|
+
export const ComponentType = {
|
|
15
|
+
EVALUATOR: 'evaluator',
|
|
16
|
+
INTERNAL_STEP: 'internal_step',
|
|
17
|
+
STEP: 'step'
|
|
11
18
|
};
|
package/src/index.d.ts
CHANGED
|
@@ -19,6 +19,13 @@ export { z } from 'zod';
|
|
|
19
19
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
20
|
type AnyZodSchema = z.ZodType<any, any, any>;
|
|
21
21
|
|
|
22
|
+
/*
|
|
23
|
+
╭─────────╮
|
|
24
|
+
│ S T E P │╮
|
|
25
|
+
╰─────────╯│
|
|
26
|
+
╰─────────╯
|
|
27
|
+
*/
|
|
28
|
+
|
|
22
29
|
/*
|
|
23
30
|
* There are 4 overloads of the "step" function:
|
|
24
31
|
* - with fn receiving input and returning output;
|
|
@@ -28,7 +35,7 @@ type AnyZodSchema = z.ZodType<any, any, any>;
|
|
|
28
35
|
*/
|
|
29
36
|
|
|
30
37
|
/**
|
|
31
|
-
* Creates logical unit of work
|
|
38
|
+
* Creates logical unit of work defined schema for both input and output.
|
|
32
39
|
*
|
|
33
40
|
* @param {object} options - Step options
|
|
34
41
|
* @param {string} options.name - Human-readable step name (only letters, numbers and "_")
|
|
@@ -50,7 +57,7 @@ export async function step<
|
|
|
50
57
|
} ): ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
|
|
51
58
|
|
|
52
59
|
/**
|
|
53
|
-
* Creates logical unit of work with
|
|
60
|
+
* Creates logical unit of work with defined schema for input only.
|
|
54
61
|
*
|
|
55
62
|
* @param {object} options - Step options
|
|
56
63
|
* @param {string} options.name - Human-readable step name (only letters, numbers and "_")
|
|
@@ -69,7 +76,7 @@ export async function step<
|
|
|
69
76
|
} ): ( input: z.infer<InputSchema> ) => Promise<void>;
|
|
70
77
|
|
|
71
78
|
/**
|
|
72
|
-
* Creates logical unit of work with
|
|
79
|
+
* Creates logical unit of work with defined schema for output only.
|
|
73
80
|
*
|
|
74
81
|
* @param {object} options - Step options
|
|
75
82
|
* @param {string} options.name - Human-readable step name (only letters, numbers and "_")
|
|
@@ -102,6 +109,12 @@ export async function step( options: {
|
|
|
102
109
|
fn: () => Promise<void>;
|
|
103
110
|
} ): () => Promise<void>;
|
|
104
111
|
|
|
112
|
+
/*
|
|
113
|
+
╭─────────────────╮
|
|
114
|
+
│ W O R K F L O W │╮
|
|
115
|
+
╰─────────────────╯│
|
|
116
|
+
╰─────────────────╯
|
|
117
|
+
*/
|
|
105
118
|
/*
|
|
106
119
|
* There are 4 overloads of the "workflow" function:
|
|
107
120
|
* - with fn receiving input and returning output;
|
|
@@ -111,7 +124,7 @@ export async function step( options: {
|
|
|
111
124
|
*/
|
|
112
125
|
|
|
113
126
|
/**
|
|
114
|
-
* Creates a workflow orchestrator
|
|
127
|
+
* Creates a workflow orchestrator defined schema for both input and output.
|
|
115
128
|
*
|
|
116
129
|
* @param {object} options - Workflow options
|
|
117
130
|
* @param {string} options.name - Unique workflow name
|
|
@@ -133,7 +146,7 @@ export function workflow<
|
|
|
133
146
|
} ): ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
|
|
134
147
|
|
|
135
148
|
/**
|
|
136
|
-
* Creates a workflow orchestrator with
|
|
149
|
+
* Creates a workflow orchestrator with defined schema for input only.
|
|
137
150
|
*
|
|
138
151
|
* @param {object} options - Workflow options
|
|
139
152
|
* @param {string} options.name - Unique workflow name
|
|
@@ -152,7 +165,7 @@ export function workflow<
|
|
|
152
165
|
} ): ( input: z.infer<InputSchema> ) => Promise<void>;
|
|
153
166
|
|
|
154
167
|
/**
|
|
155
|
-
* Creates a workflow orchestrator with
|
|
168
|
+
* Creates a workflow orchestrator with defined schema for output only.
|
|
156
169
|
*
|
|
157
170
|
* @param {object} options - Workflow options
|
|
158
171
|
* @param {string} options.name - Unique workflow name
|
|
@@ -171,7 +184,7 @@ export function workflow<
|
|
|
171
184
|
} ): () => Promise<z.infer<OutputSchema>>;
|
|
172
185
|
|
|
173
186
|
/**
|
|
174
|
-
* Creates a workflow orchestrator without schemas.
|
|
187
|
+
* Creates a workflow orchestrator without defined schemas.
|
|
175
188
|
*
|
|
176
189
|
* @param {object} options - Workflow options
|
|
177
190
|
* @param {string} options.name - Unique workflow name
|
|
@@ -185,6 +198,155 @@ export function workflow( options : {
|
|
|
185
198
|
fn: () => Promise<void>
|
|
186
199
|
} ): () => Promise<void>;
|
|
187
200
|
|
|
201
|
+
/*
|
|
202
|
+
╭───────────────────╮
|
|
203
|
+
│ E V A L U A T O R │╮
|
|
204
|
+
╰───────────────────╯│
|
|
205
|
+
╰───────────────────╯
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generic EvaluationResult class, represents the result of an evaluation
|
|
210
|
+
*/
|
|
211
|
+
class EvaluationResult {
|
|
212
|
+
/**
|
|
213
|
+
* @constructor
|
|
214
|
+
* @param {object} args
|
|
215
|
+
* @param {any} args.value - The value of the evaluation
|
|
216
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
217
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
218
|
+
*/
|
|
219
|
+
constructor( args: { value: any; confidence: number; reasoning?: string } ); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @returns {any} The evaluation result value
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
get value(): any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @returns {number} The evaluation result confidence
|
|
229
|
+
*/
|
|
230
|
+
get confidence(): number;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @returns {string} The evaluation result reasoning
|
|
234
|
+
*/
|
|
235
|
+
get reasoning(): string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* An evaluation result where the value is a string
|
|
240
|
+
* @extends EvaluationResult
|
|
241
|
+
*/
|
|
242
|
+
export class EvaluationStringResult extends EvaluationResult {
|
|
243
|
+
/**
|
|
244
|
+
* @constructor
|
|
245
|
+
* @param {object} args
|
|
246
|
+
* @param {string} args.value - The value of the evaluation
|
|
247
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
248
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
249
|
+
*/
|
|
250
|
+
constructor( args: { value: string; confidence: number; reasoning?: string } );
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @returns {string} The evaluation result value
|
|
254
|
+
*/
|
|
255
|
+
get value(): string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* An evaluation result where the value is a number
|
|
260
|
+
* @extends EvaluationResult
|
|
261
|
+
*/
|
|
262
|
+
export class EvaluationNumberResult extends EvaluationResult {
|
|
263
|
+
/**
|
|
264
|
+
* @constructor
|
|
265
|
+
* @param {object} args
|
|
266
|
+
* @param {number} args.value - The value of the evaluation
|
|
267
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
268
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
269
|
+
*/
|
|
270
|
+
constructor( args: { value: number; confidence: number; reasoning?: string } );
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @returns {number} The evaluation result value
|
|
274
|
+
*/
|
|
275
|
+
get value(): number;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* An evaluation result where the value is a boolean
|
|
280
|
+
* @extends EvaluationResult
|
|
281
|
+
*/
|
|
282
|
+
export class EvaluationBooleanResult extends EvaluationResult {
|
|
283
|
+
/**
|
|
284
|
+
* @constructor
|
|
285
|
+
* @param {object} args
|
|
286
|
+
* @param {boolean} args.value - The value of the evaluation
|
|
287
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
288
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
289
|
+
*/
|
|
290
|
+
constructor( args: { value: boolean; confidence: number; reasoning?: string } );
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @returns {boolean} The evaluation result value
|
|
294
|
+
*/
|
|
295
|
+
get value(): boolean;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/*
|
|
299
|
+
* There are 2 overloads of the "evaluator" function:
|
|
300
|
+
* - with fn receiving input;
|
|
301
|
+
* - with fn receiving void;
|
|
302
|
+
*/
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Creates workflow evaluation of work defined schema for input.
|
|
306
|
+
*
|
|
307
|
+
* @param {object} options - Evaluator options
|
|
308
|
+
* @param {string} options.name - Human-readable evaluator name (only letters, numbers and "_")
|
|
309
|
+
* @param {string} [options.description] - Description of the evaluator
|
|
310
|
+
* @param {z.ZodType} options.inputSchema - Zod schema for the fn input
|
|
311
|
+
* @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
|
|
312
|
+
* @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
|
|
313
|
+
*/
|
|
314
|
+
export async function evaluator<
|
|
315
|
+
InputSchema extends AnyZodSchema,
|
|
316
|
+
Result extends EvaluationResult
|
|
317
|
+
>( options: {
|
|
318
|
+
name: string;
|
|
319
|
+
description?: string;
|
|
320
|
+
inputSchema: InputSchema;
|
|
321
|
+
fn: ( input: z.infer<InputSchema> ) => Promise<Result>;
|
|
322
|
+
} ): ( input: z.infer<InputSchema> ) => Promise<Result>;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Creates workflow evaluation without a defined input schema.
|
|
326
|
+
*
|
|
327
|
+
* @param {object} options - Evaluator options
|
|
328
|
+
* @param {string} options.name - Human-readable evaluator name (only letters, numbers and "_")
|
|
329
|
+
* @param {string} [options.description] - Description of the evaluator
|
|
330
|
+
* @param {z.ZodType} options.inputSchema - Zod schema for the fn input
|
|
331
|
+
* @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
|
|
332
|
+
* @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
|
|
333
|
+
*/
|
|
334
|
+
export async function evaluator<
|
|
335
|
+
InputSchema extends AnyZodSchema,
|
|
336
|
+
Result extends EvaluationResult
|
|
337
|
+
>( options: {
|
|
338
|
+
name: string;
|
|
339
|
+
description?: string;
|
|
340
|
+
fn: ( input: z.infer<InputSchema> ) => Promise<Result>;
|
|
341
|
+
} ): ( input: z.infer<InputSchema> ) => Promise<Result>;
|
|
342
|
+
|
|
343
|
+
/*
|
|
344
|
+
╭───────────╮
|
|
345
|
+
│ O T H E R │╮
|
|
346
|
+
╰───────────╯│
|
|
347
|
+
╰───────────╯
|
|
348
|
+
*/
|
|
349
|
+
|
|
188
350
|
/**
|
|
189
351
|
* Create a webhook call that pauses the workflow until resumed via signal.
|
|
190
352
|
*
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
+
import { evaluator, EvaluationStringResult, EvaluationNumberResult, EvaluationBooleanResult } from './interface/evaluator.js';
|
|
1
2
|
import { step } from './interface/step.js';
|
|
2
3
|
import { workflow } from './interface/workflow.js';
|
|
3
4
|
import { createWebhook } from './interface/webhook.js';
|
|
4
5
|
import { FatalError, ValidationError } from './errors.js';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
|
|
7
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
// base building blocks
|
|
10
|
+
evaluator,
|
|
11
|
+
step,
|
|
12
|
+
workflow,
|
|
13
|
+
// Evaluation results
|
|
14
|
+
EvaluationNumberResult,
|
|
15
|
+
EvaluationStringResult,
|
|
16
|
+
EvaluationBooleanResult,
|
|
17
|
+
// webhook tool
|
|
18
|
+
createWebhook,
|
|
19
|
+
// errors
|
|
20
|
+
FatalError,
|
|
21
|
+
ValidationError,
|
|
22
|
+
// zod
|
|
23
|
+
z
|
|
24
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { setMetadata } from './metadata.js';
|
|
2
|
+
import { validateEvaluator } from './validations/static.js';
|
|
3
|
+
import { validateWithSchema } from './validations/runtime.js';
|
|
4
|
+
import { ValidationError } from '#errors';
|
|
5
|
+
import { ComponentType } from '#consts';
|
|
6
|
+
import * as z from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error for when EvaluationResult are invalid
|
|
10
|
+
*/
|
|
11
|
+
class EvaluationResultValidationError extends ValidationError {};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generic EvaluationResult class, represents the result
|
|
15
|
+
* of an evaluation
|
|
16
|
+
*/
|
|
17
|
+
export class EvaluationResult {
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the evaluation result value
|
|
21
|
+
* @type {any}
|
|
22
|
+
*/
|
|
23
|
+
value = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the confidence value, between 0 and 1.
|
|
27
|
+
* @type {number}
|
|
28
|
+
*/
|
|
29
|
+
confidence = undefined;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns the reasoning value.
|
|
33
|
+
* @type {string}
|
|
34
|
+
*/
|
|
35
|
+
reasoning = undefined;
|
|
36
|
+
|
|
37
|
+
static #schema = z.object( {
|
|
38
|
+
value: z.any(),
|
|
39
|
+
confidence: z.number(),
|
|
40
|
+
reasoning: z.string().optional()
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @constructor
|
|
45
|
+
* @param {object} args
|
|
46
|
+
* @param {any} args.value - The value of the evaluation
|
|
47
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
48
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
49
|
+
*/
|
|
50
|
+
constructor( args ) {
|
|
51
|
+
if ( !EvaluationResult.#schema.safeParse( args ) ) {
|
|
52
|
+
throw new EvaluationResultValidationError( z.prettifyError( result.error ) );
|
|
53
|
+
}
|
|
54
|
+
this.value = args.value;
|
|
55
|
+
this.confidence = args.confidence;
|
|
56
|
+
this.reasoning = args.reasoning;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* An evaluation result that uses a string value
|
|
62
|
+
* @extends EvaluationResult
|
|
63
|
+
* @property {string} value - The evaluation result value
|
|
64
|
+
*/
|
|
65
|
+
export class EvaluationStringResult extends EvaluationResult {
|
|
66
|
+
static #valueSchema = z.string();
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @constructor
|
|
70
|
+
* @param {object} args
|
|
71
|
+
* @param {string} args.value - The value of the evaluation
|
|
72
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
73
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
74
|
+
*/
|
|
75
|
+
constructor( args ) {
|
|
76
|
+
if ( !EvaluationStringResult.#valueSchema.safeParse( args.value ) ) {
|
|
77
|
+
throw new EvaluationResultValidationError( z.prettifyError( result.error ) );
|
|
78
|
+
}
|
|
79
|
+
super( args );
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* An evaluation result that uses a boolean value
|
|
85
|
+
* @extends EvaluationResult
|
|
86
|
+
* @property {boolean} value - The evaluation result value
|
|
87
|
+
*/
|
|
88
|
+
export class EvaluationBooleanResult extends EvaluationResult {
|
|
89
|
+
static #valueSchema = z.boolean();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @constructor
|
|
93
|
+
* @param {object} args
|
|
94
|
+
* @param {boolean} args.value - The value of the evaluation
|
|
95
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
96
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
97
|
+
*/
|
|
98
|
+
constructor( args ) {
|
|
99
|
+
if ( !EvaluationBooleanResult.#valueSchema.safeParse( args.value ) ) {
|
|
100
|
+
throw new EvaluationResultValidationError( z.prettifyError( result.error ) );
|
|
101
|
+
}
|
|
102
|
+
super( args );
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* An evaluation result that uses a number value
|
|
108
|
+
* @extends EvaluationResult
|
|
109
|
+
* @property {number} value - The evaluation result value
|
|
110
|
+
*/
|
|
111
|
+
export class EvaluationNumberResult extends EvaluationResult {
|
|
112
|
+
static #valueSchema = z.number();
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @constructor
|
|
116
|
+
* @param {object} args
|
|
117
|
+
* @param {number} args.value - The value of the evaluation
|
|
118
|
+
* @param {number} args.confidence - The confidence on the evaluation
|
|
119
|
+
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
120
|
+
*/
|
|
121
|
+
constructor( args ) {
|
|
122
|
+
if ( !EvaluationNumberResult.#valueSchema.safeParse( args.value ) ) {
|
|
123
|
+
throw new EvaluationResultValidationError( z.prettifyError( result.error ) );
|
|
124
|
+
}
|
|
125
|
+
super( args );
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export function evaluator( { name, description, inputSchema, fn } ) {
|
|
130
|
+
validateEvaluator( { name, description, inputSchema, fn } );
|
|
131
|
+
|
|
132
|
+
const wrapper = async input => {
|
|
133
|
+
validateWithSchema( inputSchema, input, `Evaluator ${name} input` );
|
|
134
|
+
|
|
135
|
+
const output = await fn( input );
|
|
136
|
+
|
|
137
|
+
if ( !output instanceof EvaluationResult ) {
|
|
138
|
+
throw new ValidationError( 'Evaluators must return an EvaluationResult' );
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return output;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
setMetadata( wrapper, { name, description, inputSchema, type: ComponentType.EVALUATOR } );
|
|
145
|
+
return wrapper;
|
|
146
|
+
};
|
package/src/interface/step.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { setMetadata } from './metadata.js';
|
|
2
2
|
import { validateStep } from './validations/static.js';
|
|
3
|
-
import { validateWithSchema } from './
|
|
3
|
+
import { validateWithSchema } from './validations/runtime.js';
|
|
4
|
+
import { ComponentType } from '#consts';
|
|
4
5
|
|
|
5
6
|
export function step( { name, description, inputSchema, outputSchema, fn } ) {
|
|
6
|
-
validateStep( {
|
|
7
|
-
name,
|
|
8
|
-
description,
|
|
9
|
-
inputSchema,
|
|
10
|
-
outputSchema,
|
|
11
|
-
fn
|
|
12
|
-
} );
|
|
7
|
+
validateStep( { name, description, inputSchema, outputSchema, fn } );
|
|
13
8
|
|
|
14
9
|
const wrapper = async input => {
|
|
15
10
|
validateWithSchema( inputSchema, input, `Step ${name} input` );
|
|
@@ -21,6 +16,6 @@ export function step( { name, description, inputSchema, outputSchema, fn } ) {
|
|
|
21
16
|
return output;
|
|
22
17
|
};
|
|
23
18
|
|
|
24
|
-
setMetadata( wrapper, { name, description, inputSchema, outputSchema } );
|
|
19
|
+
setMetadata( wrapper, { name, description, inputSchema, outputSchema, type: ComponentType.EVALUATOR } );
|
|
25
20
|
return wrapper;
|
|
26
21
|
};
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import { ValidationError } from '#errors';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Detects if a schema is a Zod schema object
|
|
5
|
-
* @param {unknown} schema - The schema to check
|
|
6
|
-
* @returns {boolean} True if the schema is a Zod schema
|
|
7
|
-
*/
|
|
8
|
-
export function isZodSchema( schema ) {
|
|
9
|
-
return schema &&
|
|
10
|
-
typeof schema === 'object' &&
|
|
11
|
-
'_def' in schema &&
|
|
12
|
-
typeof schema.parse === 'function' &&
|
|
13
|
-
typeof schema.parseAsync === 'function';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
3
|
/**
|
|
17
4
|
* Validates data against a Zod schema using safeParse
|
|
18
5
|
* @param {unknown} schema - The Zod schema to validate against
|
|
@@ -31,4 +18,3 @@ export function validateWithSchema( schema, data, context ) {
|
|
|
31
18
|
throw new ValidationError( `${context} validation failed: ${result.error.message}` );
|
|
32
19
|
}
|
|
33
20
|
}
|
|
34
|
-
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { validateWithSchema } from './runtime.js';
|
|
4
|
+
import { ValidationError } from '#errors';
|
|
5
|
+
|
|
6
|
+
describe( 'runtime validations', () => {
|
|
7
|
+
describe( 'validateWithSchema', () => {
|
|
8
|
+
it( 'no-ops when schema is falsy', () => {
|
|
9
|
+
expect( () => validateWithSchema( undefined, { a: 1 }, 'X' ) ).not.toThrow();
|
|
10
|
+
expect( () => validateWithSchema( null, { a: 1 }, 'X' ) ).not.toThrow();
|
|
11
|
+
} );
|
|
12
|
+
|
|
13
|
+
it( 'passes on valid data', () => {
|
|
14
|
+
const schema = z.object( { a: z.string(), b: z.number().optional() } );
|
|
15
|
+
expect( () => validateWithSchema( schema, { a: 'ok' }, 'Test' ) ).not.toThrow();
|
|
16
|
+
} );
|
|
17
|
+
|
|
18
|
+
it( 'throws ValidationError on invalid data and prefixes context', () => {
|
|
19
|
+
const schema = z.object( { a: z.string() } );
|
|
20
|
+
const call = () => validateWithSchema( schema, { a: 1 }, 'MyCtx' );
|
|
21
|
+
expect( call ).toThrow( ValidationError );
|
|
22
|
+
try {
|
|
23
|
+
call();
|
|
24
|
+
} catch ( e ) {
|
|
25
|
+
expect( String( e.message ) ).toContain( 'MyCtx validation failed:' );
|
|
26
|
+
}
|
|
27
|
+
} );
|
|
28
|
+
} );
|
|
29
|
+
} );
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
|
-
import { isZodSchema } from '
|
|
2
|
+
import { isZodSchema } from './schema_utils.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Error is thrown when the definition of a step/workflow has problems
|
|
@@ -25,6 +25,8 @@ const stepAndWorkflowSchema = z.object( {
|
|
|
25
25
|
fn: z.function()
|
|
26
26
|
} );
|
|
27
27
|
|
|
28
|
+
const evaluatorSchema = stepAndWorkflowSchema.omit( { outputSchema: true } );
|
|
29
|
+
|
|
28
30
|
const webhookSchema = z.object( {
|
|
29
31
|
url: z.url( { protocol: /^https?$/ } ),
|
|
30
32
|
payload: z.any().optional()
|
|
@@ -47,6 +49,16 @@ export function validateStep( args ) {
|
|
|
47
49
|
validateAgainstSchema( stepAndWorkflowSchema, args );
|
|
48
50
|
};
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Validate evaluator payload
|
|
54
|
+
*
|
|
55
|
+
* @param {object} args - The evaluator arguments
|
|
56
|
+
* @throws {StaticValidationError} Throws if args are invalid
|
|
57
|
+
*/
|
|
58
|
+
export function validateEvaluator( args ) {
|
|
59
|
+
validateAgainstSchema( evaluatorSchema, args );
|
|
60
|
+
};
|
|
61
|
+
|
|
50
62
|
/**
|
|
51
63
|
* Validate workflow payload
|
|
52
64
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { validateStep, validateWorkflow, validateCreateWebhook, StaticValidationError } from './static.js';
|
|
3
|
+
import { validateStep, validateWorkflow, validateCreateWebhook, validateEvaluator, StaticValidationError } from './static.js';
|
|
4
4
|
|
|
5
5
|
const validArgs = Object.freeze( {
|
|
6
6
|
name: 'valid_name',
|
|
@@ -73,6 +73,34 @@ describe( 'interface/validator', () => {
|
|
|
73
73
|
} );
|
|
74
74
|
} );
|
|
75
75
|
|
|
76
|
+
describe( 'validateEvaluator', () => {
|
|
77
|
+
const base = Object.freeze( {
|
|
78
|
+
name: 'valid_name',
|
|
79
|
+
description: 'desc',
|
|
80
|
+
inputSchema: z.object( {} ),
|
|
81
|
+
fn: () => {}
|
|
82
|
+
} );
|
|
83
|
+
|
|
84
|
+
it( 'passes for valid args (no outputSchema)', () => {
|
|
85
|
+
expect( () => validateEvaluator( { ...base } ) ).not.toThrow();
|
|
86
|
+
} );
|
|
87
|
+
|
|
88
|
+
it( 'rejects invalid name pattern', () => {
|
|
89
|
+
const error = new StaticValidationError( '✖ Invalid string: must match pattern /^[a-z_][a-z0-9_]*$/i\n → at name' );
|
|
90
|
+
expect( () => validateEvaluator( { ...base, name: '-bad' } ) ).toThrow( error );
|
|
91
|
+
} );
|
|
92
|
+
|
|
93
|
+
it( 'rejects non-Zod inputSchema', () => {
|
|
94
|
+
const error = new StaticValidationError( '✖ Schema must be a Zod schema\n → at inputSchema' );
|
|
95
|
+
expect( () => validateEvaluator( { ...base, inputSchema: 'not-a-zod-schema' } ) ).toThrow( error );
|
|
96
|
+
} );
|
|
97
|
+
|
|
98
|
+
it( 'rejects missing fn', () => {
|
|
99
|
+
const error = new StaticValidationError( '✖ Invalid input: expected function, received undefined\n → at fn' );
|
|
100
|
+
expect( () => validateEvaluator( { ...base, fn: undefined } ) ).toThrow( error );
|
|
101
|
+
} );
|
|
102
|
+
} );
|
|
103
|
+
|
|
76
104
|
describe( 'validate webhook', () => {
|
|
77
105
|
it( 'passes with valid http url', () => {
|
|
78
106
|
expect( () => validateCreateWebhook( { url: 'http://example.com' } ) ).not.toThrow();
|
|
@@ -4,8 +4,8 @@ import { getInvocationDir } from './utils.js';
|
|
|
4
4
|
import { setMetadata } from './metadata.js';
|
|
5
5
|
import { FatalError, ValidationError } from '#errors';
|
|
6
6
|
import { validateWorkflow } from './validations/static.js';
|
|
7
|
+
import { validateWithSchema } from './validations/runtime.js';
|
|
7
8
|
import { READ_TRACE_FILE, TraceEvent } from '#consts';
|
|
8
|
-
import { validateWithSchema } from './schema_utils.js';
|
|
9
9
|
|
|
10
10
|
const temporalActivityConfigs = {
|
|
11
11
|
startToCloseTimeout: '20 minute',
|
|
@@ -19,13 +19,7 @@ const temporalActivityConfigs = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export function workflow( { name, description, inputSchema, outputSchema, fn } ) {
|
|
22
|
-
validateWorkflow( {
|
|
23
|
-
name,
|
|
24
|
-
description,
|
|
25
|
-
inputSchema,
|
|
26
|
-
outputSchema,
|
|
27
|
-
fn
|
|
28
|
-
} );
|
|
22
|
+
validateWorkflow( { name, description, inputSchema, outputSchema, fn } );
|
|
29
23
|
const workflowPath = getInvocationDir();
|
|
30
24
|
|
|
31
25
|
const steps = proxyActivities( temporalActivityConfigs );
|
|
@@ -53,6 +47,7 @@ export function workflow( { name, description, inputSchema, outputSchema, fn } )
|
|
|
53
47
|
// binds the methods called in the code that Webpack loader will add, they will exposed via "this"
|
|
54
48
|
const output = await fn.call( {
|
|
55
49
|
invokeStep: async ( stepName, input ) => steps[`${workflowPath}#${stepName}`]( input ),
|
|
50
|
+
invokeEvaluator: async ( evaluatorName, input ) => steps[`${workflowPath}#${evaluatorName}`]( input ),
|
|
56
51
|
|
|
57
52
|
startWorkflow: async ( childName, input ) => {
|
|
58
53
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { FatalError } from '#errors';
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { setMetadata } from '../interface/metadata.js';
|
|
5
|
+
import { ComponentType } from '#consts';
|
|
4
6
|
|
|
5
7
|
const callerDir = process.argv[2];
|
|
6
8
|
|
|
@@ -38,6 +40,8 @@ export const sendWebhookPost = async ( { url, workflowId, payload } ) => {
|
|
|
38
40
|
}
|
|
39
41
|
};
|
|
40
42
|
|
|
43
|
+
setMetadata( sendWebhookPost, { type: ComponentType.INTERNAL_STEP } );
|
|
44
|
+
|
|
41
45
|
/**
|
|
42
46
|
* Read the trace file of a given execution and returns the content
|
|
43
47
|
*
|
|
@@ -65,3 +69,5 @@ export const readTraceFile = async ( { workflowType, workflowId } ) => {
|
|
|
65
69
|
const file = join( dir, matchingFile );
|
|
66
70
|
return existsSync( file ) ? readFileSync( file, 'utf-8' ).split( '\n' ) : [];
|
|
67
71
|
};
|
|
72
|
+
|
|
73
|
+
setMetadata( readTraceFile, { type: ComponentType.INTERNAL_STEP, skipTrace: true } );
|
|
@@ -73,6 +73,18 @@ class CatalogEntry {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Describes a single activity within a workflow.
|
|
78
|
+
*
|
|
79
|
+
* @class
|
|
80
|
+
* @extends CatalogEntry
|
|
81
|
+
*/
|
|
82
|
+
export class CatalogActivity extends CatalogEntry {}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param { CatalogWorkflowOptions}
|
|
86
|
+
*/
|
|
87
|
+
|
|
76
88
|
/**
|
|
77
89
|
* Describes a single workflow within the catalog.
|
|
78
90
|
*
|
|
@@ -87,19 +99,16 @@ export class CatalogWorkflow extends CatalogEntry {
|
|
|
87
99
|
activities;
|
|
88
100
|
|
|
89
101
|
/**
|
|
90
|
-
* @
|
|
91
|
-
* @param {
|
|
102
|
+
* @param {Object} params - Entry parameters.
|
|
103
|
+
* @param {string} params.name - Name of the entry.
|
|
104
|
+
* @param {string} [params.description] - Optional description.
|
|
105
|
+
* @param {object} [params.inputSchema] - JSON schema describing the expected input.
|
|
106
|
+
* @param {object} [params.outputSchema] - JSON schema describing the produced output.
|
|
107
|
+
* @param {string} params.path - Absolute path of the entity in the file system.
|
|
108
|
+
* @param {Array<CatalogActivity>} params.activities - Each activity of this workflow
|
|
92
109
|
*/
|
|
93
110
|
constructor( { activities, ...args } ) {
|
|
94
111
|
super( args );
|
|
95
112
|
this.activities = activities;
|
|
96
113
|
};
|
|
97
114
|
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Describes a single activity within a workflow.
|
|
101
|
-
*
|
|
102
|
-
* @class
|
|
103
|
-
* @extends CatalogEntry
|
|
104
|
-
*/
|
|
105
|
-
export class CatalogActivity extends CatalogEntry {}
|
package/src/worker/index.js
CHANGED
|
@@ -47,7 +47,7 @@ const callerDir = process.argv[2];
|
|
|
47
47
|
sinks,
|
|
48
48
|
interceptors: {
|
|
49
49
|
workflowModules: [ join( __dirname, './interceptors/workflow.js' ) ],
|
|
50
|
-
activityInbound: [ () => new ActivityExecutionInterceptor() ]
|
|
50
|
+
activityInbound: [ () => new ActivityExecutionInterceptor( activities ) ]
|
|
51
51
|
},
|
|
52
52
|
maxConcurrentWorkflowTaskExecutions: maxWorkflows,
|
|
53
53
|
maxConcurrentActivityTaskExecutions: maxActivities,
|
|
@@ -2,7 +2,8 @@ import { Context } from '@temporalio/activity';
|
|
|
2
2
|
import { Storage } from '../async_storage.js';
|
|
3
3
|
import { trace } from '../tracer/index.js';
|
|
4
4
|
import { headersToObject } from '../sandboxed_utils.js';
|
|
5
|
-
import { THIS_LIB_NAME
|
|
5
|
+
import { THIS_LIB_NAME } from '#consts';
|
|
6
|
+
import { METADATA_ACCESS_SYMBOL } from '#consts';
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
This interceptor is called for every activity execution
|
|
@@ -14,14 +15,26 @@ import { THIS_LIB_NAME, TraceEvent } from '#consts';
|
|
|
14
15
|
Some information it needs for its context comes from Temporal's Activity Context others are injected in the headers
|
|
15
16
|
*/
|
|
16
17
|
export class ActivityExecutionInterceptor {
|
|
18
|
+
constructor( activities ) {
|
|
19
|
+
this.activities = activities;
|
|
20
|
+
};
|
|
21
|
+
|
|
17
22
|
async execute( input, next ) {
|
|
18
23
|
const { workflowExecution: { workflowId }, activityId, activityType, workflowType } = Context.current().info;
|
|
24
|
+
|
|
25
|
+
const activityFn = this.activities?.[activityType];
|
|
26
|
+
const { type: componentType, skipTrace } = activityFn?.[METADATA_ACCESS_SYMBOL];
|
|
27
|
+
|
|
19
28
|
const context = { workflowId, workflowType, activityId, activityType, ...headersToObject( input.headers ) };
|
|
20
29
|
|
|
21
30
|
return Storage.runWithContext( async _ => {
|
|
22
|
-
|
|
31
|
+
if ( !skipTrace ) {
|
|
32
|
+
trace( { lib: THIS_LIB_NAME, event: `${componentType}_start`, input: input.args } );
|
|
33
|
+
}
|
|
23
34
|
const output = await next( input );
|
|
24
|
-
|
|
35
|
+
if ( !skipTrace ) {
|
|
36
|
+
trace( { lib: THIS_LIB_NAME, event: `${componentType}_end`, output } );
|
|
37
|
+
}
|
|
25
38
|
return output;
|
|
26
39
|
}, context );
|
|
27
40
|
}
|
package/src/worker/loader.js
CHANGED
|
@@ -12,10 +12,10 @@ const __dirname = dirname( fileURLToPath( import.meta.url ) );
|
|
|
12
12
|
|
|
13
13
|
// returns a map of activities, where the key is they path + name and the value is the function with metadata
|
|
14
14
|
export async function loadActivities( path ) {
|
|
15
|
-
const activityPaths = recursiveNavigateWhileCollecting( path, [ 'steps.js' ] );
|
|
15
|
+
const activityPaths = recursiveNavigateWhileCollecting( path, [ 'steps.js', 'evaluators.js' ] );
|
|
16
16
|
const activities = [];
|
|
17
17
|
for await ( const { component, metadata, pathname, path } of iteratorOverImportedComponents( activityPaths ) ) {
|
|
18
|
-
console.log( '[Core.Scanner]', '
|
|
18
|
+
console.log( '[Core.Scanner]', 'Component loaded:', metadata.type, metadata.name, 'at', pathname );
|
|
19
19
|
activities[`${path}#${metadata.name}`] = component;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -48,7 +48,7 @@ export function trace( { lib, event, input = undefined, output = undefined } ) {
|
|
|
48
48
|
rootWorkflowType
|
|
49
49
|
} = Storage.load();
|
|
50
50
|
|
|
51
|
-
const entry = {
|
|
51
|
+
const entry = { event, input, lib, output, parentWorkflowId, stepId, stepName, timestamp: now, workflowId, workflowPath, workflowType };
|
|
52
52
|
|
|
53
53
|
// test for rootWorkflow to append to the same file as the parent/grandparent
|
|
54
54
|
const outputDir = join( callerDir, 'logs', 'runs', rootWorkflowType ?? workflowType );
|
|
@@ -4,7 +4,7 @@ import { tmpdir, EOL } from 'node:os';
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { THIS_LIB_NAME } from '#consts';
|
|
6
6
|
|
|
7
|
-
const createTempDir = () => mkdtempSync( join( tmpdir(), '
|
|
7
|
+
const createTempDir = () => mkdtempSync( join( tmpdir(), 'output-sdk-trace-' ) );
|
|
8
8
|
|
|
9
9
|
// Single mocks (configured per-test by mutating the backing objects)
|
|
10
10
|
const mockConfig = { tracing: { enabled: false } };
|
|
@@ -32,7 +32,6 @@ describe( 'tracer/index', () => {
|
|
|
32
32
|
vi.clearAllMocks();
|
|
33
33
|
vi.useFakeTimers();
|
|
34
34
|
vi.setSystemTime( new Date( '2020-01-01T00:00:00.000Z' ) );
|
|
35
|
-
|
|
36
35
|
} );
|
|
37
36
|
|
|
38
37
|
afterEach( () => {
|
|
@@ -46,11 +46,12 @@ export const buildLogTree = src => {
|
|
|
46
46
|
const { event, workflowId, workflowType, workflowPath, parentWorkflowId, stepId, stepName, input, output, timestamp } = entry;
|
|
47
47
|
|
|
48
48
|
const baseEntry = { children: [], startedAt: timestamp, workflowId };
|
|
49
|
-
if (
|
|
50
|
-
|
|
49
|
+
if ( [ TraceEvent.EVALUATOR_START, TraceEvent.STEP_START ].includes( event ) ) {
|
|
50
|
+
const eventName = event === TraceEvent.EVALUATOR_START ? 'evaluator' : 'step';
|
|
51
|
+
stepsMap.set( `${workflowId}:${stepId}`, { event: eventName, input, stepId, stepName, ...baseEntry } );
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
if (
|
|
54
|
+
if ( [ TraceEvent.EVALUATOR_END, TraceEvent.STEP_END ].includes( event ) ) {
|
|
54
55
|
Object.assign( stepsMap.get( `${workflowId}:${stepId}` ) ?? {}, { output, endedAt: timestamp } );
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -7,7 +7,7 @@ import { EOL } from 'os';
|
|
|
7
7
|
import { buildLogTree } from './tracer_tree.js';
|
|
8
8
|
import { THIS_LIB_NAME, TraceEvent } from '#consts';
|
|
9
9
|
|
|
10
|
-
const createTempDir = () => mkdtempSync( join( tmpdir(), '
|
|
10
|
+
const createTempDir = () => mkdtempSync( join( tmpdir(), 'output-sdk-trace-tree-' ) );
|
|
11
11
|
|
|
12
12
|
describe( 'tracer/tracer_tree', () => {
|
|
13
13
|
it( 'builds a tree JSON from a raw log file', () => {
|
|
@@ -2,9 +2,11 @@ import traverseModule from '@babel/traverse';
|
|
|
2
2
|
import {
|
|
3
3
|
buildWorkflowNameMap,
|
|
4
4
|
getLocalNameFromDestructuredProperty,
|
|
5
|
+
isEvaluatorsPath,
|
|
5
6
|
isStepsPath,
|
|
6
7
|
isWorkflowPath,
|
|
7
8
|
buildStepsNameMap,
|
|
9
|
+
buildEvaluatorsNameMap,
|
|
8
10
|
toAbsolutePath
|
|
9
11
|
} from './tools.js';
|
|
10
12
|
import {
|
|
@@ -34,15 +36,16 @@ const traverse = traverseModule.default ?? traverseModule;
|
|
|
34
36
|
* @returns {{ stepImports: Array<{localName:string,stepName:string}>,
|
|
35
37
|
* flowImports: Array<{localName:string,workflowName:string}> }} Collected info mappings.
|
|
36
38
|
*/
|
|
37
|
-
export default function collectTargetImports( ast, fileDir, { stepsNameCache, workflowNameCache } ) {
|
|
39
|
+
export default function collectTargetImports( ast, fileDir, { stepsNameCache, workflowNameCache, evaluatorsNameCache } ) {
|
|
38
40
|
const stepImports = [];
|
|
39
41
|
const flowImports = [];
|
|
42
|
+
const evaluatorImports = [];
|
|
40
43
|
|
|
41
44
|
traverse( ast, {
|
|
42
45
|
ImportDeclaration: path => {
|
|
43
46
|
const src = path.node.source.value;
|
|
44
47
|
// Ignore other imports
|
|
45
|
-
if ( !isStepsPath( src ) && !isWorkflowPath( src ) ) {
|
|
48
|
+
if ( !isStepsPath( src ) && !isWorkflowPath( src ) && !isEvaluatorsPath( src ) ) {
|
|
46
49
|
return;
|
|
47
50
|
}
|
|
48
51
|
|
|
@@ -58,6 +61,17 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
}
|
|
64
|
+
if ( isEvaluatorsPath( src ) ) {
|
|
65
|
+
const nameMap = buildEvaluatorsNameMap( absolutePath, evaluatorsNameCache );
|
|
66
|
+
for ( const s of path.node.specifiers.filter( s => isImportSpecifier( s ) ) ) {
|
|
67
|
+
const importedName = s.imported.name;
|
|
68
|
+
const localName = s.local.name;
|
|
69
|
+
const evaluatorName = nameMap.get( importedName );
|
|
70
|
+
if ( evaluatorName ) {
|
|
71
|
+
evaluatorImports.push( { localName, evaluatorName } );
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
61
75
|
if ( isWorkflowPath( src ) ) {
|
|
62
76
|
const { named, default: defName } = buildWorkflowNameMap( absolutePath, workflowNameCache );
|
|
63
77
|
for ( const s of path.node.specifiers ) {
|
|
@@ -94,7 +108,7 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
94
108
|
|
|
95
109
|
const req = firstArgument.value;
|
|
96
110
|
// Must be steps/workflows module
|
|
97
|
-
if ( !isStepsPath( req ) && !isWorkflowPath( req ) ) {
|
|
111
|
+
if ( !isStepsPath( req ) && !isWorkflowPath( req ) && !isEvaluatorsPath( req ) ) {
|
|
98
112
|
return;
|
|
99
113
|
}
|
|
100
114
|
|
|
@@ -116,6 +130,23 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
116
130
|
} else {
|
|
117
131
|
path.remove();
|
|
118
132
|
}
|
|
133
|
+
} else if ( isEvaluatorsPath( req ) && isObjectPattern( path.node.id ) ) {
|
|
134
|
+
const nameMap = buildEvaluatorsNameMap( absolutePath, evaluatorsNameCache );
|
|
135
|
+
for ( const prop of path.node.id.properties.filter( prop => isObjectProperty( prop ) && isIdentifier( prop.key ) ) ) {
|
|
136
|
+
const importedName = prop.key.name;
|
|
137
|
+
const localName = getLocalNameFromDestructuredProperty( prop );
|
|
138
|
+
if ( localName ) {
|
|
139
|
+
const evaluatorName = nameMap.get( importedName );
|
|
140
|
+
if ( evaluatorName ) {
|
|
141
|
+
evaluatorImports.push( { localName, evaluatorName } );
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if ( isVariableDeclaration( path.parent ) && path.parent.declarations.length === 1 ) {
|
|
146
|
+
path.parentPath.remove();
|
|
147
|
+
} else {
|
|
148
|
+
path.remove();
|
|
149
|
+
}
|
|
119
150
|
} else if ( isWorkflowPath( req ) && isIdentifier( path.node.id ) ) {
|
|
120
151
|
const { default: defName } = buildWorkflowNameMap( absolutePath, workflowNameCache );
|
|
121
152
|
const localName = path.node.id.name;
|
|
@@ -129,5 +160,5 @@ export default function collectTargetImports( ast, fileDir, { stepsNameCache, wo
|
|
|
129
160
|
}
|
|
130
161
|
} );
|
|
131
162
|
|
|
132
|
-
return { stepImports, flowImports };
|
|
163
|
+
return { stepImports, evaluatorImports, flowImports };
|
|
133
164
|
};
|
|
@@ -16,6 +16,9 @@ describe( 'collect_target_imports', () => {
|
|
|
16
16
|
'export const StepA = step({ name: "step.a" })',
|
|
17
17
|
'export const StepB = step({ name: "step.b" })'
|
|
18
18
|
].join( '\n' ) );
|
|
19
|
+
writeFileSync( join( dir, 'evaluators.js' ), [
|
|
20
|
+
'export const EvalA = evaluator({ name: "eval.a" })'
|
|
21
|
+
].join( '\n' ) );
|
|
19
22
|
writeFileSync( join( dir, 'workflow.js' ), [
|
|
20
23
|
'export const FlowA = workflow({ name: "flow.a" })',
|
|
21
24
|
'export default workflow({ name: "flow.def" })'
|
|
@@ -23,16 +26,18 @@ describe( 'collect_target_imports', () => {
|
|
|
23
26
|
|
|
24
27
|
const source = [
|
|
25
28
|
'import { StepA } from "./steps.js";',
|
|
29
|
+
'import { EvalA } from "./evaluators.js";',
|
|
26
30
|
'import WF, { FlowA } from "./workflow.js";',
|
|
27
31
|
'const x = 1;'
|
|
28
32
|
].join( '\n' );
|
|
29
33
|
|
|
30
34
|
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
31
|
-
const { stepImports, flowImports } = collectTargetImports(
|
|
35
|
+
const { stepImports, evaluatorImports, flowImports } = collectTargetImports(
|
|
32
36
|
ast,
|
|
33
37
|
dir,
|
|
34
|
-
{ stepsNameCache: new Map(), workflowNameCache: new Map() }
|
|
38
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
35
39
|
);
|
|
40
|
+
expect( evaluatorImports ).toEqual( [ { localName: 'EvalA', evaluatorName: 'eval.a' } ] );
|
|
36
41
|
|
|
37
42
|
expect( stepImports ).toEqual( [ { localName: 'StepA', stepName: 'step.a' } ] );
|
|
38
43
|
expect( flowImports ).toEqual( [
|
|
@@ -48,20 +53,23 @@ describe( 'collect_target_imports', () => {
|
|
|
48
53
|
it( 'collects CJS requires and removes declarators (steps + default workflow)', () => {
|
|
49
54
|
const dir = mkdtempSync( join( tmpdir(), 'collect-cjs-' ) );
|
|
50
55
|
writeFileSync( join( dir, 'steps.js' ), 'export const StepB = step({ name: "step.b" })\n' );
|
|
56
|
+
writeFileSync( join( dir, 'evaluators.js' ), 'export const EvalB = evaluator({ name: "eval.b" })\n' );
|
|
51
57
|
writeFileSync( join( dir, 'workflow.js' ), 'export default workflow({ name: "flow.c" })\n' );
|
|
52
58
|
|
|
53
59
|
const source = [
|
|
54
60
|
'const { StepB } = require("./steps.js");',
|
|
61
|
+
'const { EvalB } = require("./evaluators.js");',
|
|
55
62
|
'const WF = require("./workflow.js");',
|
|
56
63
|
'const obj = {};'
|
|
57
64
|
].join( '\n' );
|
|
58
65
|
|
|
59
66
|
const ast = makeAst( source, join( dir, 'file.js' ) );
|
|
60
|
-
const { stepImports, flowImports } = collectTargetImports(
|
|
67
|
+
const { stepImports, evaluatorImports, flowImports } = collectTargetImports(
|
|
61
68
|
ast,
|
|
62
69
|
dir,
|
|
63
|
-
{ stepsNameCache: new Map(), workflowNameCache: new Map() }
|
|
70
|
+
{ stepsNameCache: new Map(), evaluatorsNameCache: new Map(), workflowNameCache: new Map() }
|
|
64
71
|
);
|
|
72
|
+
expect( evaluatorImports ).toEqual( [ { localName: 'EvalB', evaluatorName: 'eval.b' } ] );
|
|
65
73
|
|
|
66
74
|
expect( stepImports ).toEqual( [ { localName: 'StepB', stepName: 'step.b' } ] );
|
|
67
75
|
expect( flowImports ).toEqual( [ { localName: 'WF', workflowName: 'flow.c' } ] );
|
|
@@ -10,6 +10,7 @@ const generate = generatorModule.default ?? generatorModule;
|
|
|
10
10
|
|
|
11
11
|
// Caches to avoid re-reading files during a build
|
|
12
12
|
const stepsNameCache = new Map(); // path -> Map<exported, stepName>
|
|
13
|
+
const evaluatorsNameCache = new Map(); // path -> Map<exported, evaluatorName>
|
|
13
14
|
const workflowNameCache = new Map(); // path -> { default?: name, named: Map<exported, flowName> }
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -25,20 +26,20 @@ const workflowNameCache = new Map(); // path -> { default?: name, named: Map<exp
|
|
|
25
26
|
export default function stepImportRewriterAstLoader( source, inputMap ) {
|
|
26
27
|
this.cacheable?.( true );
|
|
27
28
|
const callback = this.async?.() ?? this.callback;
|
|
28
|
-
const cache = { stepsNameCache, workflowNameCache };
|
|
29
|
+
const cache = { stepsNameCache, evaluatorsNameCache, workflowNameCache };
|
|
29
30
|
|
|
30
31
|
try {
|
|
31
32
|
const filename = this.resourcePath;
|
|
32
33
|
const ast = parse( String( source ), filename );
|
|
33
34
|
const fileDir = dirname( filename );
|
|
34
|
-
const { stepImports, flowImports } = collectTargetImports( ast, fileDir, cache );
|
|
35
|
+
const { stepImports, evaluatorImports, flowImports } = collectTargetImports( ast, fileDir, cache );
|
|
35
36
|
|
|
36
37
|
// No imports
|
|
37
|
-
if (
|
|
38
|
+
if ( [].concat( stepImports, evaluatorImports, flowImports ).length === 0 ) {
|
|
38
39
|
return callback( null, source, inputMap );
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const rewrote = rewriteFnBodies( ast, stepImports, flowImports );
|
|
42
|
+
const rewrote = rewriteFnBodies( { ast, stepImports, evaluatorImports, flowImports } );
|
|
42
43
|
// No edits performed
|
|
43
44
|
if ( !rewrote ) {
|
|
44
45
|
return callback( null, source, inputMap );
|
|
@@ -15,12 +15,14 @@ const traverse = traverseModule.default ?? traverseModule;
|
|
|
15
15
|
* `this.invokeStep('name', ...)` and `FlowY(...)` with
|
|
16
16
|
* `this.startWorkflow('name', ...)`.
|
|
17
17
|
*
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {Array<{localName:string,
|
|
18
|
+
* @param {object} params
|
|
19
|
+
* @param {import('@babel/types').File} params.ast - Parsed file AST.
|
|
20
|
+
* @param {Array<{localName:string,stepName:string}>} params.stepImports - Step imports.
|
|
21
|
+
* @param {Array<{localName:string,evaluatorName:string}>} params.evaluatorImports - Evaluator imports.
|
|
22
|
+
* @param {Array<{localName:string,workflowName:string}>} params.flowImports - Workflow imports.
|
|
21
23
|
* @returns {boolean} True if the AST was modified; false otherwise.
|
|
22
24
|
*/
|
|
23
|
-
export default function rewriteFnBodies( ast, stepImports, flowImports ) {
|
|
25
|
+
export default function rewriteFnBodies( { ast, stepImports, evaluatorImports, flowImports } ) {
|
|
24
26
|
const state = { rewrote: false };
|
|
25
27
|
traverse( ast, {
|
|
26
28
|
ObjectProperty: path => {
|
|
@@ -56,6 +58,13 @@ export default function rewriteFnBodies( ast, stepImports, flowImports ) {
|
|
|
56
58
|
state.rewrote = true;
|
|
57
59
|
return; // Stop after rewriting as step call
|
|
58
60
|
}
|
|
61
|
+
const evaluator = evaluatorImports.find( x => x.localName === callee.name );
|
|
62
|
+
if ( evaluator ) {
|
|
63
|
+
const args = cPath.node.arguments;
|
|
64
|
+
cPath.replaceWith( createThisMethodCall( 'invokeEvaluator', evaluator.evaluatorName, args ) );
|
|
65
|
+
state.rewrote = true;
|
|
66
|
+
return; // Stop after rewriting as evaluator call
|
|
67
|
+
}
|
|
59
68
|
const flow = flowImports.find( x => x.localName === callee.name );
|
|
60
69
|
if ( flow ) {
|
|
61
70
|
const args = cPath.node.arguments;
|
|
@@ -16,17 +16,31 @@ describe( 'rewrite_fn_bodies', () => {
|
|
|
16
16
|
const stepImports = [ { localName: 'StepA', stepName: 'step.a' } ];
|
|
17
17
|
const flowImports = [ { localName: 'FlowB', workflowName: 'flow.b' } ];
|
|
18
18
|
|
|
19
|
-
const rewrote = rewriteFnBodies( ast, stepImports, flowImports );
|
|
19
|
+
const rewrote = rewriteFnBodies( { ast, stepImports, evaluatorImports: [], flowImports } );
|
|
20
20
|
expect( rewrote ).toBe( true );
|
|
21
21
|
|
|
22
22
|
const code = ast.program.body.map( n => n.type ).length; // smoke: ast mutated
|
|
23
23
|
expect( code ).toBeGreaterThan( 0 );
|
|
24
24
|
} );
|
|
25
25
|
|
|
26
|
+
it( 'rewrites evaluator calls to this.invokeEvaluator', () => {
|
|
27
|
+
const src = [
|
|
28
|
+
'const obj = {',
|
|
29
|
+
' fn: async (x) => {',
|
|
30
|
+
' EvalA(3);',
|
|
31
|
+
' }',
|
|
32
|
+
'}'
|
|
33
|
+
].join( '\n' );
|
|
34
|
+
const ast = parse( src, 'file.js' );
|
|
35
|
+
const evaluatorImports = [ { localName: 'EvalA', evaluatorName: 'eval.a' } ];
|
|
36
|
+
const rewrote = rewriteFnBodies( { ast, stepImports: [], evaluatorImports, flowImports: [] } );
|
|
37
|
+
expect( rewrote ).toBe( true );
|
|
38
|
+
} );
|
|
39
|
+
|
|
26
40
|
it( 'does nothing when no matching calls are present', () => {
|
|
27
41
|
const src = [ 'const obj = { fn: function() { other(); } }' ].join( '\n' );
|
|
28
42
|
const ast = parse( src, 'file.js' );
|
|
29
|
-
const rewrote = rewriteFnBodies( ast, [], [] );
|
|
43
|
+
const rewrote = rewriteFnBodies( { ast, stepImports: [], evaluatorImports: [], flowImports: [] } );
|
|
30
44
|
expect( rewrote ).toBe( false );
|
|
31
45
|
} );
|
|
32
46
|
} );
|
|
@@ -106,6 +106,13 @@ export const toFunctionExpression = arrow => {
|
|
|
106
106
|
*/
|
|
107
107
|
export const isStepsPath = value => /(^|\/)steps\.js$/.test( value );
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Check if a module specifier or request string points to evaluators.js.
|
|
111
|
+
* @param {string} value - Module path or request string.
|
|
112
|
+
* @returns {boolean} True if it matches evaluators.js.
|
|
113
|
+
*/
|
|
114
|
+
export const isEvaluatorsPath = value => /(^|\/)evaluators\.js$/.test( value );
|
|
115
|
+
|
|
109
116
|
/**
|
|
110
117
|
* Check if a module specifier or request string points to workflow.js.
|
|
111
118
|
* @param {string} value - Module path or request string.
|
|
@@ -165,14 +172,18 @@ export const resolveNameFromOptions = ( optionsNode, consts, errorMessagePrefix
|
|
|
165
172
|
};
|
|
166
173
|
|
|
167
174
|
/**
|
|
168
|
-
* Build a map
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* @param {
|
|
172
|
-
* @
|
|
173
|
-
* @
|
|
175
|
+
* Build a map of exported component identifiers to declared names by scanning a module.
|
|
176
|
+
* Coerces only static, analyzable forms: `export const X = callee({ name: '...' })`.
|
|
177
|
+
*
|
|
178
|
+
* @param {object} params
|
|
179
|
+
* @param {string} params.path - Absolute path to the module file.
|
|
180
|
+
* @param {Map<string, Map<string,string>>} params.cache - Cache for memoizing results by file path.
|
|
181
|
+
* @param {('step'|'evaluator')} params.calleeName - Factory function identifier to match.
|
|
182
|
+
* @param {string} params.invalidMessagePrefix - Prefix used in thrown errors when name is invalid.
|
|
183
|
+
* @returns {Map<string,string>} Map of `exportedIdentifier` -> `declaredName`.
|
|
184
|
+
* @throws {Error} When names are missing, dynamic, or otherwise non-static.
|
|
174
185
|
*/
|
|
175
|
-
|
|
186
|
+
const buildComponentNameMap = ( { path, cache, calleeName, invalidMessagePrefix } ) => {
|
|
176
187
|
if ( cache.has( path ) ) {
|
|
177
188
|
return cache.get( path );
|
|
178
189
|
}
|
|
@@ -180,24 +191,46 @@ export const buildStepsNameMap = ( path, cache ) => {
|
|
|
180
191
|
const ast = parse( text, path );
|
|
181
192
|
const consts = extractTopLevelStringConsts( ast );
|
|
182
193
|
|
|
183
|
-
const
|
|
194
|
+
const result = ast.program.body
|
|
184
195
|
.filter( node => isExportNamedDeclaration( node ) && isVariableDeclaration( node.declaration ) )
|
|
185
196
|
.reduce( ( map, node ) => {
|
|
186
|
-
|
|
187
197
|
node.declaration.declarations
|
|
188
|
-
.filter( dec => isIdentifier( dec.id ) && isCallExpression( dec.init ) && isIdentifier( dec.init.callee, { name:
|
|
198
|
+
.filter( dec => isIdentifier( dec.id ) && isCallExpression( dec.init ) && isIdentifier( dec.init.callee, { name: calleeName } ) )
|
|
189
199
|
.map( dec => [
|
|
190
200
|
dec,
|
|
191
|
-
resolveNameFromOptions( dec.init.arguments[0], consts,
|
|
201
|
+
resolveNameFromOptions( dec.init.arguments[0], consts, `${invalidMessagePrefix} ${path} for "${dec.id.name}"` )
|
|
192
202
|
] )
|
|
193
203
|
.forEach( ( [ dec, name ] ) => map.set( dec.id.name, name ) );
|
|
194
204
|
return map;
|
|
195
205
|
}, new Map() );
|
|
196
206
|
|
|
197
|
-
cache.set( path,
|
|
198
|
-
return
|
|
207
|
+
cache.set( path, result );
|
|
208
|
+
return result;
|
|
199
209
|
};
|
|
200
210
|
|
|
211
|
+
export const buildStepsNameMap = ( path, cache ) => buildComponentNameMap( {
|
|
212
|
+
path,
|
|
213
|
+
cache,
|
|
214
|
+
calleeName: 'step',
|
|
215
|
+
invalidMessagePrefix: 'Invalid step name in'
|
|
216
|
+
} );
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Build a map from exported evaluator identifier to declared evaluator name.
|
|
220
|
+
* Parses `evaluators.js` for `export const X = evaluator({ name: '...' })`.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} path - Absolute path to the evaluators module file.
|
|
223
|
+
* @param {Map<string, Map<string,string>>} cache - Cache of computed evaluator name maps.
|
|
224
|
+
* @returns {Map<string,string>} Exported identifier -> evaluator name.
|
|
225
|
+
* @throws {Error} When a evaluator name is invalid (non-static or missing).
|
|
226
|
+
*/
|
|
227
|
+
export const buildEvaluatorsNameMap = ( path, cache ) => buildComponentNameMap( {
|
|
228
|
+
path,
|
|
229
|
+
cache,
|
|
230
|
+
calleeName: 'evaluator',
|
|
231
|
+
invalidMessagePrefix: 'Invalid evaluator name in'
|
|
232
|
+
} );
|
|
233
|
+
|
|
201
234
|
/**
|
|
202
235
|
* Build a structure with default and named workflow names from a workflow module.
|
|
203
236
|
* Extracts names from `workflow({ name: '...' })` calls.
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
createThisMethodCall,
|
|
16
16
|
resolveNameFromOptions,
|
|
17
17
|
buildStepsNameMap,
|
|
18
|
-
buildWorkflowNameMap
|
|
18
|
+
buildWorkflowNameMap,
|
|
19
|
+
buildEvaluatorsNameMap
|
|
19
20
|
} from './tools.js';
|
|
20
21
|
|
|
21
22
|
describe( 'workflow_rewriter tools', () => {
|
|
@@ -72,6 +73,23 @@ describe( 'workflow_rewriter tools', () => {
|
|
|
72
73
|
rmSync( dir, { recursive: true, force: true } );
|
|
73
74
|
} );
|
|
74
75
|
|
|
76
|
+
it( 'buildEvaluatorsNameMap: reads names from evaluators module and caches result', () => {
|
|
77
|
+
const dir = mkdtempSync( join( tmpdir(), 'tools-evals-' ) );
|
|
78
|
+
const evalsPath = join( dir, 'evaluators.js' );
|
|
79
|
+
writeFileSync( evalsPath, [
|
|
80
|
+
'export const EvalA = evaluator({ name: "eval.a" })',
|
|
81
|
+
'export const EvalB = evaluator({ name: "eval.b" })'
|
|
82
|
+
].join( '\n' ) );
|
|
83
|
+
const cache = new Map();
|
|
84
|
+
const map1 = buildEvaluatorsNameMap( evalsPath, cache );
|
|
85
|
+
expect( map1.get( 'EvalA' ) ).toBe( 'eval.a' );
|
|
86
|
+
expect( map1.get( 'EvalB' ) ).toBe( 'eval.b' );
|
|
87
|
+
expect( cache.get( evalsPath ) ).toBe( map1 );
|
|
88
|
+
const map2 = buildEvaluatorsNameMap( evalsPath, cache );
|
|
89
|
+
expect( map2 ).toBe( map1 );
|
|
90
|
+
rmSync( dir, { recursive: true, force: true } );
|
|
91
|
+
} );
|
|
92
|
+
|
|
75
93
|
it( 'getLocalNameFromDestructuredProperty: handles { a }, { a: b }, { a: b = 1 }', () => {
|
|
76
94
|
// { a }
|
|
77
95
|
const p1 = t.objectProperty( t.identifier( 'a' ), t.identifier( 'a' ), false, true );
|
|
@@ -88,7 +106,7 @@ describe( 'workflow_rewriter tools', () => {
|
|
|
88
106
|
} );
|
|
89
107
|
|
|
90
108
|
it( 'buildWorkflowNameMap: reads named and default workflow names and caches', () => {
|
|
91
|
-
const dir = mkdtempSync( join( tmpdir(), 'tools-
|
|
109
|
+
const dir = mkdtempSync( join( tmpdir(), 'tools-output-' ) );
|
|
92
110
|
const wfPath = join( dir, 'workflow.js' );
|
|
93
111
|
writeFileSync( wfPath, [
|
|
94
112
|
'export const FlowA = workflow({ name: "flow.a" })',
|
|
File without changes
|