@output.ai/core 0.0.8 → 0.0.10

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/README.md CHANGED
@@ -1,64 +1,100 @@
1
1
  # Core
2
2
 
3
- Provides tools to run a workflow, which is a well defined logical unit of work.
3
+ Provides tools to develop and run a workflow, which is a well defined logical unit of work.
4
4
 
5
- ## step()
5
+ ## Structure
6
6
 
7
- Defines a step, which is a enclosed unit of work which can import external dependencies.
7
+ Workflows are defined using core functions "workflow" and "step", separate files:
8
8
 
9
- ```ts
10
- import { step } from 'flow-core';
9
+ ```
10
+ workflows
11
+ └ example
12
+ ├ workflow.ts|js <- workflow entry point
13
+ ├ steps.ts|js <- file with each step of this workflow
14
+ └ prompt.prompt <- a prompt file
15
+ └ other-example
11
16
 
12
- export const aSingleStep = step( {
13
- name: 'aSingleStep',
14
- fn: async (): Promise<string> => {
15
- // do stuff
16
- }
17
- } );
18
17
  ```
19
18
 
20
- All step needs a name, and the name will be used in the context of the workflow (see below).
19
+ Think that workflows is the orchestrator and steps are executors. So the workflow only call the steps and the steps call the IO operations, like APIs, DBs, LLMs, etc.
21
20
 
22
- ## workflow()
21
+ ## Workflow code
23
22
 
24
- Defines a logical structure of step invocations. No external dependencies can be imported in the file containing the workflow.
23
+ ### workflow.js
25
24
 
26
- ```ts
27
- import { workflow } from 'flow-core';
28
- import type { WorkflowContext } from 'flow-core';
29
- import type { PromptWorkflowInput, PromptWorkflowOutput } from './types';
25
+ ```js
26
+ import { workflow } from '@output.ai/workflow';
27
+ import { guessByName } from './steps.js';
30
28
 
31
29
  export default workflow( {
32
- name: 'prompt',
33
- description: 'A workflow to demonstrate the prompt feature',
34
- fn: async ( input: PromptWorkflowInput, context: WorkflowContext ): Promise<PromptWorkflowOutput> => {
35
- const result = await context.steps.aSingleStep();
36
-
37
- return { result };
30
+ name: 'guessMyProfession',
31
+ description: 'Guess a person profession by its name',
32
+ inputSchema: {
33
+ type: 'object',
34
+ required: [ 'name' ],
35
+ properties: {
36
+ name: { type: 'string'}
37
+ }
38
+ },
39
+ outputSchema: {
40
+ type: 'object',
41
+ required: [ 'profession' ],
42
+ properties: {
43
+ profession: { type: 'string'}
44
+ }
45
+ },
46
+ fn: async input => {
47
+ const profession = await guessByName( input.name );
48
+ return { profession };
38
49
  }
39
- } );
50
+ })
40
51
  ```
41
52
 
42
- The workflow Context has the following properties:
53
+ ### steps.js
43
54
 
44
- ### .steps.*()
55
+ ```js
56
+ import { api } from './api.js'
45
57
 
46
- Each step defined in the step file is available here as js function with the "name" it was defined there. The input arguments are the input arguments of its `fn` function.
58
+ export const guessByName = step( {
59
+ name: 'guessByName',
60
+ inputSchema: {
61
+ type: 'string'
62
+ },
63
+ outputSchema: {
64
+ type: 'string'
65
+ },
66
+ fn: async name => {
67
+ const res = await api.consumer( name );
68
+ return res.body;
69
+ }
70
+ } )
71
+ ```
47
72
 
48
- ### .tools.webhook()
73
+ ## webhooks
49
74
 
50
- Provides a webhook feature where a POST is made to a given url and the workflow is stopped until another POST is made back to the workflows API:
75
+ workflows can call webhooks that will stop their execution until an answer is given back.
51
76
 
52
77
  ```js
53
- const result = await context.tools.webhook( {
54
- name: 'needFeedback',
55
- description: 'Get a feedback',
56
- url: 'http://xxx.xxx/feedback',
57
- payload: { /* a given payload */ }
58
- } );
78
+ import { workflow, createWebhook } from '@output.ai/workflow';
79
+ import { guessByName } from './steps.js';
80
+
81
+ export default workflow( {
82
+ ...
83
+ fn: async input => {
84
+ ...
85
+
86
+ const result = await createWebhook( {
87
+ url: 'http://xxx.xxx/feedback',
88
+ payload: {
89
+ progressSoFar: 'plenty'
90
+ }
91
+ } );
92
+
93
+ }
94
+ })
59
95
  ```
60
96
 
61
- the url will receive the following payload:
97
+ The url of the example will receive the payload, plus the workflowId:
62
98
 
63
99
  ```js
64
100
  {
@@ -67,38 +103,39 @@ the url will receive the following payload:
67
103
  }
68
104
  ```
69
105
 
70
- To resume the workflow, a POST has to be made to a given url, with a response payload and the workflowId.
106
+ To resume the workflow, a POST has to be made with a response payload and the workflowId.
71
107
 
72
- The host is localhost:3001 if running locally, there is not remote host yet.
108
+ - Production: `https://output-api-production.onrender.com/workflow/feedback`
109
+ - Staging: `https://output-api-staging.onrender.com/workflow/feedback`
110
+ - Local: `http://localhost:3001/workflow/feedback`
73
111
 
74
- The format is:
75
- ```
76
- POST https://locahost:3001/workflow/feedback
112
+ Example:
113
+
114
+ ```bash
115
+ POST http://locahost:3001/workflow/feedback
77
116
  {
78
117
  workflowId,
79
118
  payload: {}
80
119
  }
81
120
  ```
82
121
 
83
- ## Folder structure:
122
+ ## Developing
84
123
 
85
- ```
86
- └ workflows
87
- └ my workflow
88
- ├ index.ts // contains the workflow()
89
- ├ types.ts // contains input, output types, and other misc types
90
- └ steps.ts // contains all steps
91
- ```
92
-
93
- ## Booting
124
+ To develop workflows you need the code, which will be called the worker, the API and the engine (Temporal).
94
125
 
95
- The project with workflows will be a "worker", meaning its workflows can be invoked individually by their names. To do so it has to be started using a npx command.
126
+ After having the API and the engine running, to start the worker just run:
96
127
 
97
- _package.json_
98
128
  ```js
99
- ...
100
- "scripts": {
101
- "start": "npx flow-worker"
102
- },
103
- ...
129
+ `npm run outputai`
104
130
  ```
131
+
132
+ ## Env variables
133
+
134
+ Necessary env variables to run the worker locally:
135
+
136
+ - `TEMPORAL_ADDRESS`: The temporal backend address, prefer the remote;
137
+ - `TEMPORAL_NAMESPACE`: The name of the namespace, if using remote, use: "output-staging.i0jzq";
138
+ - `TEMPORAL_API_KEY`: The API key to access remote temporal. If using local temporal, leave it blank;
139
+ - `CATALOG_ID`: The name of the local catalog, always set this. Use your email;
140
+ - `API_AUTH_KEY`: The API key to access the Framework API. Local can be blank, remote use the proper API Key;
141
+ - `TRACING_ENABLED`: A "stringbool" value indicating if traces should be generated or note;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/core",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -13,19 +13,22 @@
13
13
  "worker": "node ./src/worker/index.js"
14
14
  },
15
15
  "bin": {
16
- "flow-worker": "./bin/worker.sh"
16
+ "flow-worker": "./bin/worker.sh",
17
+ "outputai": "./bin/worker.sh"
17
18
  },
18
19
  "repository": {
19
20
  "type": "git",
20
21
  "url": "git+https://github.com/growthxai/flow-sdk"
21
22
  },
22
23
  "dependencies": {
23
- "@babel/generator": "7.26.5",
24
- "@babel/parser": "7.26.7",
25
- "@babel/traverse": "7.25.9",
26
- "@babel/types": "7.26.7",
24
+ "@babel/generator": "7.28.3",
25
+ "@babel/parser": "7.28.4",
26
+ "@babel/traverse": "7.28.4",
27
+ "@babel/types": "7.28.4",
27
28
  "@temporalio/worker": "1.13.0",
28
- "@temporalio/workflow": "1.13.0"
29
+ "@temporalio/workflow": "1.13.0",
30
+ "ajv": "8.17.1",
31
+ "zod": "4.1.9"
29
32
  },
30
33
  "imports": {
31
34
  "#consts": "./src/consts.js",
package/src/configs.js CHANGED
@@ -1,17 +1,39 @@
1
+ import * as z from 'zod';
2
+
3
+ class InvalidEnvVarsErrors extends Error {}
4
+
5
+ const envVarSchema = z.object( {
6
+ TEMPORAL_ADDRESS: z.union( [
7
+ z.httpUrl(),
8
+ z.string().regex( /^[a-z0-9_-]+:\d{2,5}$/i ) // local docker container name like worker:7233
9
+ ] ),
10
+ TEMPORAL_NAMESPACE: z.string().optional().default( 'default' ),
11
+ TEMPORAL_API_KEY: z.string().optional(),
12
+ CATALOG_ID: z.string().regex( /^[a-z0-9_.@-]+$/i ),
13
+ API_AUTH_KEY: z.string().optional(),
14
+ TRACING_ENABLED: z.stringbool().optional()
15
+ } );
16
+
17
+ const { data: safeEnvVar, error } = envVarSchema.safeParse( process.env );
18
+ if ( error ) {
19
+ throw new InvalidEnvVarsErrors( z.prettifyError( error ) );
20
+ }
21
+
1
22
  export const worker = {
2
- address: process.env.TEMPORAL_ADDRESS,
3
- apiKey: process.env.TEMPORAL_API_KEY,
23
+ address: safeEnvVar.TEMPORAL_ADDRESS,
24
+ apiKey: safeEnvVar.TEMPORAL_API_KEY,
4
25
  executionTimeout: '1m',
5
26
  maxActivities: 100,
6
27
  maxWorkflows: 100,
7
- namespace: process.env.TEMPORAL_NAMESPACE ?? 'default',
8
- taskQueue: process.env.TEMPORAL_TASK_QUEUE
28
+ namespace: safeEnvVar.TEMPORAL_NAMESPACE ?? 'default',
29
+ taskQueue: safeEnvVar.CATALOG_ID,
30
+ catalogId: safeEnvVar.CATALOG_ID
9
31
  };
10
32
 
11
33
  export const api = {
12
- authKey: process.env.API_AUTH_KEY
34
+ authKey: safeEnvVar.API_AUTH_KEY
13
35
  };
14
36
 
15
37
  export const tracing = {
16
- enabled: [ '1', 'true' ].includes( process.env.TRACING_ENABLED )
38
+ enabled: safeEnvVar.TRACING_ENABLED
17
39
  };
package/src/consts.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export const SEND_WEBHOOK_ACTIVITY_NAME = '__internal#sendWebhookPost';
2
2
  export const METADATA_ACCESS_SYMBOL = Symbol( '__metadata' );
3
3
  export const WORKFLOWS_INDEX_FILENAME = '__workflows_entrypoint.js';
4
+ export const THIS_LIB_NAME = 'core';
package/src/errors.js CHANGED
@@ -1,3 +1,14 @@
1
+ /**
2
+ * These are errors exposed as tools for the user to break their flow
3
+ * They work in both steps and workflows
4
+ */
5
+
6
+ /**
7
+ * Any generic fatal errors
8
+ */
1
9
  export class FatalError extends Error { }
2
10
 
11
+ /**
12
+ * Any validation error
13
+ */
3
14
  export class ValidationError extends Error { }
@@ -1,6 +1,20 @@
1
1
  import { setMetadata } from './metadata.js';
2
+ import { validateStep } from './validations/static.js';
3
+ import { validateStepInput, validateStepOutput } from './validations/runtime.js';
4
+ import { invokeFnAndValidateOutputPreservingExecutionModel } from './utils.js';
2
5
 
3
6
  export function step( { name, description, inputSchema, outputSchema, fn } ) {
4
- setMetadata( fn, { name, description, inputSchema, outputSchema } );
5
- return fn;
7
+ validateStep( { name, description, inputSchema, outputSchema, fn } );
8
+ const wrapper = input => {
9
+ if ( inputSchema ) {
10
+ validateStepInput( name, inputSchema, input );
11
+ }
12
+ if ( !outputSchema ) {
13
+ return fn( input );
14
+ }
15
+ return invokeFnAndValidateOutputPreservingExecutionModel( fn, input, validateStepOutput.bind( null, name, outputSchema ) );
16
+ };
17
+
18
+ setMetadata( wrapper, { name, description, inputSchema, outputSchema } );
19
+ return wrapper;
6
20
  };
@@ -1,9 +1,46 @@
1
- // This is rigged to return which folder had the source of calls for both interface methods (step workflow) functions
2
- // Important, if to refactor, pay attention to the depth in the stack trace to extract the info
3
- // now is 3 cause (1 line is name, 2 line is this function, 3 line is step/workflow, 4 line is caller)
4
- export const getInvocationDir = _ => new Error()
1
+ /**
2
+ * Function rigged to return the folder path of the source of calls for the interface methods (step/workflow)
3
+ *
4
+ * IMPORTANT!!!
5
+ * If to refactor this, pay attention to the depth in the stack trace to extract the info.
6
+ * Currently it is 3:
7
+ * - 1st line is the name of the function;
8
+ * - 2nd line is this function;
9
+ * - 3rd line is step/workflow;
10
+ * - 4th line is caller;
11
+ *
12
+ * @returns {string} The folder path of the caller
13
+ */
14
+ export const getInvocationDir = () => new Error()
5
15
  .stack.split( '\n' )[3]
6
16
  .split( ' ' )
7
17
  .at( -1 )
8
18
  .replace( /\((.+):\d+:\d+\)/, '$1' )
9
19
  .split( '/' ).slice( 0, -1 ).join( '/' );
20
+
21
+ /**
22
+ * This mouthful function will invoke a function with given arguments, and validate its return
23
+ * using a given validator.
24
+ *
25
+ * It will preserver the execution model (asynchronous vs synchronous), so if the function is
26
+ * sync the validation happens here, if it is async (returns Promise) the validation is attached
27
+ * to a .then().
28
+ *
29
+ *
30
+ * @param {Function} fn - The function to execute
31
+ * @param {any} input - The payload to call the function
32
+ * @param {Function} validate - The validator function
33
+ * @returns {any} Function result (Promise or not)
34
+ */
35
+ export const invokeFnAndValidateOutputPreservingExecutionModel = ( fn, input, validate ) => {
36
+ const uniformReturn = output => {
37
+ validate( output );
38
+ return output;
39
+ };
40
+
41
+ const output = fn( input );
42
+ if ( output?.constructor === Promise ) {
43
+ return output.then( resolvedOutput => uniformReturn( resolvedOutput ) );
44
+ }
45
+ return uniformReturn( output );
46
+ };
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { getInvocationDir, invokeFnAndValidateOutputPreservingExecutionModel } from './utils.js';
3
+
4
+ describe( 'interface/utils', () => {
5
+ describe( 'getInvocationDir', () => {
6
+ it( 'returns the caller directory from stack trace', () => {
7
+ const fakeCaller = '/tmp/project/src/caller/file.js';
8
+ const OriginalError = Error;
9
+ // Provide a deterministic stack shape for the function under test
10
+ // Lines: 1) Error, 2) getInvocationDir, 3) step/workflow, 4) actual caller
11
+ // Include typical V8 formatting with leading spaces and without function name
12
+ // for the caller line
13
+
14
+ Error = class extends OriginalError {
15
+ constructor( ...args ) {
16
+ super( ...args );
17
+ this.stack = [
18
+ 'Error',
19
+ ' at getInvocationDir (a:1:1)',
20
+ ' at step (b:1:1)',
21
+ ` at ${fakeCaller}:10:20`
22
+ ].join( '\n' );
23
+ }
24
+ };
25
+ try {
26
+ const dir = getInvocationDir();
27
+ expect( dir ).toBe( '/tmp/project/src/caller' );
28
+ } finally {
29
+
30
+ Error = OriginalError;
31
+ }
32
+ } );
33
+ } );
34
+
35
+ describe( 'invokeFnAndValidateOutputPreservingExecutionModel', () => {
36
+ it( 'validates and returns sync output', () => {
37
+ const fn = vi.fn( x => x * 2 );
38
+ const validate = vi.fn();
39
+ const result = invokeFnAndValidateOutputPreservingExecutionModel( fn, 3, validate );
40
+ expect( result ).toBe( 6 );
41
+ expect( validate ).toHaveBeenCalledWith( 6 );
42
+ } );
43
+
44
+ it( 'validates and returns async output preserving promise', async () => {
45
+ const fn = vi.fn( async x => x + 1 );
46
+ const validate = vi.fn();
47
+ const resultPromise = invokeFnAndValidateOutputPreservingExecutionModel( fn, 4, validate );
48
+ expect( resultPromise ).toBeInstanceOf( Promise );
49
+ const result = await resultPromise;
50
+ expect( result ).toBe( 5 );
51
+ expect( validate ).toHaveBeenCalledWith( 5 );
52
+ } );
53
+
54
+ it( 'propagates validator errors (sync)', () => {
55
+ const fn = vi.fn( x => x );
56
+ const validate = vi.fn( () => {
57
+ throw new Error( 'invalid' );
58
+ } );
59
+ expect( () => invokeFnAndValidateOutputPreservingExecutionModel( fn, 'a', validate ) ).toThrow( 'invalid' );
60
+ } );
61
+
62
+ it( 'propagates validator errors (async)', async () => {
63
+ const fn = vi.fn( async x => x );
64
+ const validate = vi.fn( () => {
65
+ throw new Error( 'invalid' );
66
+ } );
67
+ await expect( invokeFnAndValidateOutputPreservingExecutionModel( fn, 'a', validate ) ).rejects.toThrow( 'invalid' );
68
+ } );
69
+ } );
70
+ } );
71
+
@@ -0,0 +1,3 @@
1
+ import Ajv from 'ajv';
2
+
3
+ export const ajv = new Ajv();
@@ -0,0 +1,69 @@
1
+ import { FatalError } from '#errors';
2
+ import { ajv } from './ajv_provider.js';
3
+
4
+ /**
5
+ * Error type for when the input/output of a step/workflow doesn't match its input/output schema, respectively
6
+ */
7
+ export class MismatchSchemaError extends FatalError {}
8
+ /**
9
+ * Error type for when the input of a step/workflow doesn't match its input schema
10
+ * @extends MismatchSchemaError
11
+ */
12
+ export class InvalidInputError extends MismatchSchemaError {}
13
+ /**
14
+ * Error type for when the output of a step/workflow doesn't match its output schema
15
+ * @extends MismatchSchemaError
16
+ */
17
+ export class InvalidOutputError extends MismatchSchemaError {}
18
+
19
+ const validate = ( ErrorClass, type, name, schema, payload ) => {
20
+ const validate = ajv.compile( schema );
21
+ const valid = validate( payload );
22
+
23
+ if ( !valid ) {
24
+ throw new ErrorClass( `Invalid input at ${type} "${name}": ${ajv.errorsText( validate.errors )}` );
25
+ }
26
+ };
27
+
28
+ const validateInput = validate.bind( null, InvalidInputError );
29
+ const validateOutput = validate.bind( null, InvalidOutputError );
30
+
31
+ /**
32
+ * Validates step input
33
+ *
34
+ * @param {name} name - step's name
35
+ * @param {object} schema - step's input schema
36
+ * @param {any} - the input to validate
37
+ * @throws InvalidInputError
38
+ */
39
+ export const validateStepInput = validateInput.bind( null, 'step' );
40
+
41
+ /**
42
+ * Validates step output
43
+ *
44
+ * @param {name} name - step's name
45
+ * @param {object} schema - step's output schema
46
+ * @param {any} - the output to validate
47
+ * @throws InvalidOutputError
48
+ */
49
+ export const validateStepOutput = validateOutput.bind( null, 'step' );
50
+
51
+ /**
52
+ * Validates workflow input
53
+ *
54
+ * @param {name} name - workflow's name
55
+ * @param {object} schema - workflow's input schema
56
+ * @param {any} - the input to validate
57
+ * @throws InvalidInputError
58
+ */
59
+ export const validateWorkflowInput = validateInput.bind( null, 'workflow' );
60
+
61
+ /**
62
+ * Validates workflow output
63
+ *
64
+ * @param {name} name - workflow's name
65
+ * @param {object} schema - workflow's output schema
66
+ * @param {any} - the output to validate
67
+ * @throws InvalidOutputError
68
+ */
69
+ export const validateWorkflowOutput = validateOutput.bind( null, 'workflow' );
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateStepInput, validateWorkflowInput, InvalidInputError } from './runtime.js';
3
+
4
+ describe( 'Runtime validations spec', () => {
5
+ describe( 'validateStepInput', () => {
6
+ it( 'passes with matching payload', () => {
7
+ const schema = {
8
+ type: 'object',
9
+ properties: { id: { type: 'string' }, count: { type: 'integer', minimum: 0 } },
10
+ required: [ 'id' ],
11
+ additionalProperties: false
12
+ };
13
+ expect( () => validateStepInput( 'myStep', schema, { id: 'x', count: 2 } ) ).not.toThrow();
14
+ } );
15
+
16
+ it( 'rejects invalid payload', () => {
17
+ const schema = {
18
+ type: 'object',
19
+ properties: { id: { type: 'string' }, count: { type: 'integer', minimum: 0 } },
20
+ required: [ 'id' ],
21
+ additionalProperties: false
22
+ };
23
+ const error = new InvalidInputError( 'Invalid input at step "myStep": data must have required property \'id\'' );
24
+ expect( () => validateStepInput( 'myStep', schema, { count: -1 } ) ).toThrow( error );
25
+ } );
26
+ } );
27
+
28
+ describe( 'validateWorkflowInput', () => {
29
+ it( 'passes with matching payload', () => {
30
+ const schema = {
31
+ type: 'object',
32
+ properties: { name: { type: 'string' }, enabled: { type: 'boolean' } },
33
+ required: [ 'name' ],
34
+ additionalProperties: false
35
+ };
36
+ expect( () => validateWorkflowInput( 'myWorkflow', schema, { name: 'wf', enabled: true } ) ).not.toThrow();
37
+ } );
38
+
39
+ it( 'rejects invalid payload', () => {
40
+ const schema = {
41
+ type: 'object',
42
+ properties: { name: { type: 'string' }, enabled: { type: 'boolean' } },
43
+ required: [ 'name' ],
44
+ additionalProperties: false
45
+ };
46
+ const error = new InvalidInputError( 'Invalid input at workflow "myWorkflow": data must have required property \'name\'' );
47
+ expect( () => validateWorkflowInput( 'myWorkflow', schema, { enabled: 'yes' } ) ).toThrow( error );
48
+ } );
49
+ } );
50
+ } );
@@ -0,0 +1,67 @@
1
+ import { ajv } from './ajv_provider.js';
2
+ import * as z from 'zod';
3
+
4
+ /**
5
+ * Error is thrown when the definition of a step/workflow has problems
6
+ */
7
+ export class StaticValidationError extends Error {};
8
+
9
+ // Custom validation for zod, to be used when validating JSONSchema def with ajv
10
+ const refineJsonSchema = ( value, ctx ) => {
11
+ if ( value && !ajv.validateSchema( value ) ) {
12
+ ctx.addIssue( {
13
+ code: 'invalid_format',
14
+ message: ajv.errorsText( ajv.errors )
15
+ } );
16
+ }
17
+ };
18
+
19
+ const stepAndWorkflowSchema = z.object( {
20
+ name: z.string().regex( /^[a-z_][a-z0-9_]*$/i ),
21
+ description: z.string().optional(),
22
+ inputSchema: z.looseObject( {} ).optional().superRefine( refineJsonSchema ),
23
+ outputSchema: z.looseObject( {} ).optional().superRefine( refineJsonSchema ),
24
+ fn: z.function()
25
+ } );
26
+
27
+ const webhookSchema = z.object( {
28
+ url: z.url( { protocol: /^https?$/ } ),
29
+ payload: z.any().optional()
30
+ } );
31
+
32
+ const validateAgainstSchema = ( schema, args ) => {
33
+ const result = schema.safeParse( args );
34
+ if ( !result.success ) {
35
+ throw new StaticValidationError( z.prettifyError( result.error ) );
36
+ }
37
+ };
38
+
39
+ /**
40
+ * Validate step payload
41
+ *
42
+ * @param {object} args - The step arguments
43
+ * @throws {StaticValidationError} Throws if args are invalid
44
+ */
45
+ export function validateStep( args ) {
46
+ validateAgainstSchema( stepAndWorkflowSchema, args );
47
+ };
48
+
49
+ /**
50
+ * Validate workflow payload
51
+ *
52
+ * @param {object} args - The workflow arguments
53
+ * @throws {StaticValidationError} Throws if args are invalid
54
+ */
55
+ export function validateWorkflow( args ) {
56
+ validateAgainstSchema( stepAndWorkflowSchema, args );
57
+ };
58
+
59
+ /**
60
+ * Validate createWebhook payload
61
+ *
62
+ * @param {object} args - The createWebhook arguments
63
+ * @throws {StaticValidationError} Throws if args are invalid
64
+ */
65
+ export function validateCreateWebhook( args ) {
66
+ validateAgainstSchema( webhookSchema, args );
67
+ };