@output.ai/core 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/core",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -27,7 +27,6 @@
27
27
  "@babel/types": "7.28.4",
28
28
  "@temporalio/worker": "1.13.0",
29
29
  "@temporalio/workflow": "1.13.0",
30
- "ajv": "8.17.1",
31
30
  "zod": "4.1.9"
32
31
  },
33
32
  "imports": {
package/src/index.d.ts CHANGED
@@ -1,175 +1,100 @@
1
- // JSON Schema TypeScript type mapping (subset sufficient for IntelliSense)
2
- type Simplify<T> = { [K in keyof T]: T[K] };
3
- type EnumValues = readonly ( string | number | boolean | null )[];
1
+ // Import Zod types for dual schema support
2
+ import type { z } from 'zod';
4
3
 
5
- type JSONSchema = {
6
- type?: 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'object' | 'array';
7
- enum?: EnumValues;
8
- const?: string | number | boolean | null;
9
- anyOf?: readonly JSONSchema[];
10
- oneOf?: readonly JSONSchema[];
11
- allOf?: readonly JSONSchema[];
12
- // object
13
- properties?: { [key: string]: JSONSchema };
14
- required?: readonly string[];
15
- additionalProperties?: boolean | JSONSchema;
16
- // array
17
- items?: JSONSchema | readonly JSONSchema[];
18
- // ignore $ref & others for typing purposes
19
- $ref?: string;
20
- } & Record<string, unknown>;
4
+ // Re-export Zod for consumers to use
5
+ export { z } from 'zod';
21
6
 
22
- type UnionToIntersection<U> = ( U extends unknown ? ( k: U ) => void : never ) extends ( k: infer I ) => void ? I : never;
23
-
24
- type FromEnum<S> = S extends { enum: infer E extends EnumValues } ? E[number] : never;
25
- type FromConst<S> = S extends { const: infer C } ? C : never;
26
-
27
- type FromPrimitive<S> =
28
- S extends { type: 'string' } ? string :
29
- S extends { type: 'number' | 'integer' } ? number :
30
- S extends { type: 'boolean' } ? boolean :
31
- S extends { type: 'null' } ? null :
32
- never;
33
-
34
- type ObjectProps<S extends JSONSchema> = S extends { properties: infer P extends Record<string, JSONSchema> }
35
- ? { [K in keyof P]: FromSchema<P[K]> }
36
- : Record<never, never>;
37
-
38
- type ObjectRequiredKeys<S extends JSONSchema> = S extends { required: readonly ( infer R )[] }
39
- ? Extract<R & string, keyof ObjectProps<S>>
40
- : never;
41
-
42
- type ObjectFrom<S extends JSONSchema> = Simplify<
43
- Pick<ObjectProps<S>, ObjectRequiredKeys<S>> &
44
- Partial<Omit<ObjectProps<S>, ObjectRequiredKeys<S>>>
45
- >;
46
-
47
- type ArrayFrom<S extends JSONSchema> = S extends { items: infer I }
48
- ? I extends readonly unknown[]
49
- ? FromSchema<I[number]>[]
50
- : FromSchema<I>[]
51
- : unknown[];
52
-
53
- export type FromSchema<S extends JSONSchema> =
54
- // combinators first
55
- S extends { anyOf: infer A extends readonly JSONSchema[] } ? FromSchema<A[number]> :
56
- S extends { oneOf: infer A extends readonly JSONSchema[] } ? FromSchema<A[number]> :
57
- S extends { allOf: infer A extends readonly JSONSchema[] } ? UnionToIntersection<FromSchema<A[number]>> :
58
- // enum/const
59
- FromEnum<S> extends never ? ( FromConst<S> extends never ? (
60
- // object (explicit or by presence of properties)
61
- S extends { type: 'object' } | { properties: Record<string, JSONSchema> } ? ObjectFrom<S> :
62
- // array
63
- S extends { type: 'array' } ? ArrayFrom<S> :
64
- // primitives
65
- FromPrimitive<S> extends never ? unknown : FromPrimitive<S>
66
- ) : FromConst<S> ) : FromEnum<S>;
67
-
68
- export type OutputFrom<O extends JSONSchema> = FromSchema<O>;
69
-
70
- // Utility to strip readonly from inferred types
71
- /** Recursively removes readonly modifiers from object and array types. */
72
- type DeepMutable<T> =
73
- T extends readonly ( infer U )[] ? DeepMutable<U>[] :
74
- T extends object ? { -readonly [K in keyof T]: DeepMutable<T[K]> } :
75
- T;
7
+ /**
8
+ * Type alias for any Zod schema type.
9
+ *
10
+ * Note: The use of `any` here is intentional and necessary. Zod's ZodType has three
11
+ * generic parameters (Output, Def, Input) that vary based on the specific schema type.
12
+ * When we want to accept ANY Zod schema (string, number, object, etc.), we must use
13
+ * `any` for these parameters. Using `unknown` would be too restrictive and would
14
+ * break Zod's type inference system.
15
+ *
16
+ * This is a common pattern in TypeScript when dealing with highly generic types,
17
+ * and Zod itself uses this pattern internally.
18
+ */
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ type AnyZodSchema = z.ZodType<any, any, any>;
76
21
 
77
22
  /*
78
- * There is 4 overrides of the "step" function"
23
+ * There are 4 overloads of the "step" function:
79
24
  * - with fn receiving input and returning output;
80
25
  * - with fn receiving input and returning void;
81
- * - with fn receiving void and returning outout;
26
+ * - with fn receiving void and returning output;
82
27
  * - with fn receiving void and returning void;
83
28
  */
84
29
 
85
30
  /**
86
- * @callback StepFn
87
- * @param {any} input - The input, defined by the inputSchema
88
- * @returns {any} The output defined by the outputSchema
89
- */
90
-
91
- /**
92
- * Creates logical unit of work, called "step", which will be invoked from workflows.
31
+ * Creates logical unit of work with Zod schemas for both input and output.
93
32
  *
94
33
  * @param {object} options - Step options
95
34
  * @param {string} options.name - Human-readable step name (only letters, numbers and "_")
96
35
  * @param {string} [options.description] - Description of the step
97
- * @param {JSONSchema} options.inputSchema - JSON Schema for the fn input, will infer a TS type
98
- * @param {JSONSchema} options.outputSchema - JSON Schema for the fn output, will infer a TS type
99
- * @param {StepFn} options.fn - The function logic
100
- * @returns {StepFn} The options.fn function
36
+ * @param {z.ZodType} options.inputSchema - Zod schema for the fn input
37
+ * @param {z.ZodType} options.outputSchema - Zod schema for the fn output
38
+ * @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
39
+ * @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
101
40
  */
102
41
  export async function step<
103
- const InputSchema extends JSONSchema,
104
- const OutputSchema extends JSONSchema
42
+ InputSchema extends AnyZodSchema,
43
+ OutputSchema extends AnyZodSchema
105
44
  >( options: {
106
45
  name: string;
107
46
  description?: string;
108
47
  inputSchema: InputSchema;
109
48
  outputSchema: OutputSchema;
110
- fn: ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<DeepMutable<FromSchema<OutputSchema>>>;
111
- } ): ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<DeepMutable<FromSchema<OutputSchema>>>;
112
-
113
- /**
114
- * @callback InputOnlyStepFn
115
- * @param {any} input - The input, defined by the inputSchema
116
- */
49
+ fn: ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
50
+ } ): ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
117
51
 
118
52
  /**
119
- * Creates logical unit of work, called "step", which will be invoked from workflows.
53
+ * Creates logical unit of work with Zod schema for input only.
120
54
  *
121
55
  * @param {object} options - Step options
122
56
  * @param {string} options.name - Human-readable step name (only letters, numbers and "_")
123
57
  * @param {string} [options.description] - Description of the step
124
- * @param {JSONSchema} options.inputSchema - JSON Schema for the fn input, will infer a TS type
125
- * @param {InputOnlyStepFn} options.fn - The function logic
126
- * @returns {InputOnlyStepFn} The options.fn function
58
+ * @param {z.ZodType} options.inputSchema - Zod schema for the fn input
59
+ * @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<void>`
60
+ * @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<void>`
127
61
  */
128
62
  export async function step<
129
- const InputSchema extends JSONSchema
63
+ InputSchema extends AnyZodSchema
130
64
  >( options: {
131
65
  name: string;
132
66
  description?: string;
133
67
  inputSchema: InputSchema;
134
- fn: ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<void>;
135
- } ): ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<void>;
68
+ fn: ( input: z.infer<InputSchema> ) => Promise<void>;
69
+ } ): ( input: z.infer<InputSchema> ) => Promise<void>;
136
70
 
137
71
  /**
138
- * @callback OutputOnlyStepFn
139
- * @returns {any} The output defined by the outputSchema
140
- */
141
-
142
- /**
143
- * Creates logical unit of work, called "step", which will be invoked from workflows.
72
+ * Creates logical unit of work with Zod schema for output only.
144
73
  *
145
74
  * @param {object} options - Step options
146
75
  * @param {string} options.name - Human-readable step name (only letters, numbers and "_")
147
76
  * @param {string} [options.description] - Description of the step
148
- * @param {JSONSchema} options.outputSchema - JSON Schema for the fn output, will infer a TS type
149
- * @param {OutputOnlyStepFn} options.fn - The function logic
150
- * @returns {OutputOnlyStepFn} The options.fn function
77
+ * @param {z.ZodType} options.outputSchema - Zod schema for the fn output
78
+ * @param {function} options.fn - The function logic: `() => Promise<z.infer<OutputSchema>>`
79
+ * @returns {function} Function with signature: `() => Promise<z.infer<OutputSchema>>`
151
80
  */
152
81
  export async function step<
153
- const OutputSchema extends JSONSchema
82
+ OutputSchema extends AnyZodSchema
154
83
  >( options: {
155
84
  name: string;
156
85
  description?: string;
157
86
  outputSchema: OutputSchema;
158
- fn: () => Promise<DeepMutable<FromSchema<OutputSchema>>>;
159
- } ): () => Promise<DeepMutable<FromSchema<OutputSchema>>>;
87
+ fn: () => Promise<z.infer<OutputSchema>>;
88
+ } ): () => Promise<z.infer<OutputSchema>>;
160
89
 
161
90
  /**
162
- * @callback VoidStepFn
163
- */
164
-
165
- /**
166
- * Creates logical unit of work, called "step", which will be invoked from workflows.
91
+ * Creates logical unit of work without schemas.
167
92
  *
168
93
  * @param {object} options - Step options
169
94
  * @param {string} options.name - Human-readable step name (only letters, numbers and "_")
170
95
  * @param {string} [options.description] - Description of the step
171
- * @param {VoidStepFn} options.fn - The function logic
172
- * @returns {VoidStepFn} The options.fn function
96
+ * @param {function} options.fn - The function logic: `() => Promise<void>`
97
+ * @returns {function} Function with signature: `() => Promise<void>`
173
98
  */
174
99
  export async function step( options: {
175
100
  name: string;
@@ -178,103 +103,81 @@ export async function step( options: {
178
103
  } ): () => Promise<void>;
179
104
 
180
105
  /*
181
- * There is 4 overrides of the "workflow" function"
106
+ * There are 4 overloads of the "workflow" function:
182
107
  * - with fn receiving input and returning output;
183
108
  * - with fn receiving input and returning void;
184
- * - with fn receiving void and returning outout;
109
+ * - with fn receiving void and returning output;
185
110
  * - with fn receiving void and returning void;
186
111
  */
187
112
 
188
113
  /**
189
- * @callback WorkflowFn
190
- * @param {any} input - The input, defined by the inputSchema
191
- * @returns {any} The output defined by the outputSchema
192
- */
193
-
194
- /**
195
- * Creates a workflow orchestrator which can invoke steps.
114
+ * Creates a workflow orchestrator with Zod schemas for both input and output.
196
115
  *
197
116
  * @param {object} options - Workflow options
198
117
  * @param {string} options.name - Unique workflow name
199
118
  * @param {string} [options.description] - Description of the workflow
200
- * @param {JSONSchema} [options.inputSchema] - JSON Schema for workflow input
201
- * @param {JSONSchema} [options.outputSchema] - JSON Schema for workflow output
202
- * @param {WorkflowFn} options.fn - Workflow logic
203
- * @returns {WorkflowFn} Callable workflow function
119
+ * @param {z.ZodType} options.inputSchema - Zod schema for workflow input
120
+ * @param {z.ZodType} options.outputSchema - Zod schema for workflow output
121
+ * @param {function} options.fn - Workflow logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
122
+ * @returns {function} Callable workflow function: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
204
123
  */
205
124
  export function workflow<
206
- const InputSchema extends JSONSchema,
207
- const OutputSchema extends JSONSchema
125
+ InputSchema extends AnyZodSchema,
126
+ OutputSchema extends AnyZodSchema
208
127
  >( options : {
209
128
  name: string,
210
129
  description?: string,
211
130
  inputSchema: InputSchema,
212
131
  outputSchema: OutputSchema,
213
- fn: ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<DeepMutable<FromSchema<OutputSchema>>>
214
- } ): ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<DeepMutable<FromSchema<OutputSchema>>>;
132
+ fn: ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>
133
+ } ): ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
215
134
 
216
135
  /**
217
- * @callback InputOnlyWorkflowFn
218
- * @param {any} input - The input, defined by the inputSchema
219
- */
220
-
221
- /**
222
- * Creates a workflow orchestrator which can invoke steps.
136
+ * Creates a workflow orchestrator with Zod schema for input only.
223
137
  *
224
138
  * @param {object} options - Workflow options
225
139
  * @param {string} options.name - Unique workflow name
226
140
  * @param {string} [options.description] - Description of the workflow
227
- * @param {JSONSchema} [options.inputSchema] - JSON Schema for workflow input
228
- * @param {InputOnlyWorkflowFn} options.fn - Workflow logic
229
- * @returns {InputOnlyWorkflowFn} Callable workflow function
141
+ * @param {z.ZodType} options.inputSchema - Zod schema for workflow input
142
+ * @param {function} options.fn - Workflow logic: `(input: z.infer<InputSchema>) => Promise<void>`
143
+ * @returns {function} Callable workflow function: `(input: z.infer<InputSchema>) => Promise<void>`
230
144
  */
231
145
  export function workflow<
232
- const InputSchema extends JSONSchema
146
+ InputSchema extends AnyZodSchema
233
147
  >( options : {
234
148
  name: string,
235
149
  description?: string,
236
150
  inputSchema: InputSchema,
237
- fn: ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<void>
238
- } ): ( input: DeepMutable<FromSchema<InputSchema>> ) => Promise<void>;
151
+ fn: ( input: z.infer<InputSchema> ) => Promise<void>
152
+ } ): ( input: z.infer<InputSchema> ) => Promise<void>;
239
153
 
240
154
  /**
241
- * @callback OutputOnlyWorkflowFn
242
- * @returns {any} The output defined by the outputSchema
243
- */
244
-
245
- /**
246
- * Creates a workflow orchestrator which can invoke steps.
155
+ * Creates a workflow orchestrator with Zod schema for output only.
247
156
  *
248
157
  * @param {object} options - Workflow options
249
158
  * @param {string} options.name - Unique workflow name
250
159
  * @param {string} [options.description] - Description of the workflow
251
- * @param {JSONSchema} [options.outputSchema] - JSON Schema for workflow output
252
- * @param {OutputOnlyWorkflowFn} options.fn - Workflow logic
253
- * @returns {OutputOnlyWorkflowFn} Callable workflow function
160
+ * @param {z.ZodType} options.outputSchema - Zod schema for workflow output
161
+ * @param {function} options.fn - Workflow logic: `() => Promise<z.infer<OutputSchema>>`
162
+ * @returns {function} Callable workflow function: `() => Promise<z.infer<OutputSchema>>`
254
163
  */
255
164
  export function workflow<
256
- const OutputSchema extends JSONSchema
165
+ OutputSchema extends AnyZodSchema
257
166
  >( options : {
258
167
  name: string,
259
168
  description?: string,
260
169
  outputSchema: OutputSchema,
261
- fn: () => Promise<DeepMutable<FromSchema<OutputSchema>>>
262
- } ): () => Promise<DeepMutable<FromSchema<OutputSchema>>>;
263
-
264
- /**
265
- * @callback VoidWorkflowFn
266
- * @param {any} input - The input, defined by the inputSchema
267
- * @returns {any} The output defined by the outputSchema
268
- */
170
+ fn: () => Promise<z.infer<OutputSchema>>
171
+ } ): () => Promise<z.infer<OutputSchema>>;
269
172
 
270
173
  /**
271
- * Creates a workflow orchestrator which can invoke steps.
174
+ * Creates a workflow orchestrator without schemas.
272
175
  *
273
176
  * @param {object} options - Workflow options
274
177
  * @param {string} options.name - Unique workflow name
275
178
  * @param {string} [options.description] - Description of the workflow
276
- * @param {VoidWorkflowFn} options.fn - Workflow logic
277
- * @returns {VoidWorkflowFn} Callable workflow function
179
+ * @param {function} options.fn - Workflow logic: `() => Promise<void>`
180
+ * @returns {function} Callable workflow function: `() => Promise<void>`
278
181
  */
279
182
  export function workflow( options : {
280
183
  name: string,
package/src/index.js CHANGED
@@ -2,5 +2,6 @@ import { step } from './interface/step.js';
2
2
  import { workflow } from './interface/workflow.js';
3
3
  import { createWebhook } from './interface/webhook.js';
4
4
  import { FatalError, ValidationError } from './errors.js';
5
+ import { z } from 'zod';
5
6
 
6
- export { step, workflow, createWebhook, FatalError, ValidationError };
7
+ export { step, workflow, createWebhook, FatalError, ValidationError, z };
@@ -0,0 +1,34 @@
1
+ import { ValidationError } from '#errors';
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
+ /**
17
+ * Validates data against a Zod schema using safeParse
18
+ * @param {unknown} schema - The Zod schema to validate against
19
+ * @param {unknown} data - The data to validate
20
+ * @param {string} context - Description of what's being validated (for error messages)
21
+ * @throws {ValidationError} If validation fails
22
+ * @returns {void}
23
+ */
24
+ export function validateWithSchema( schema, data, context ) {
25
+ if ( !schema ) {
26
+ return;
27
+ }
28
+
29
+ const result = schema.safeParse( data );
30
+ if ( !result.success ) {
31
+ throw new ValidationError( `${context} validation failed: ${result.error.message}` );
32
+ }
33
+ }
34
+
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { z } from 'zod';
3
+
4
+ // Import the actual implementations
5
+ import { isZodSchema } from './schema_utils.js';
6
+
7
+ describe( 'Schema Detection', () => {
8
+ describe( 'isZodSchema', () => {
9
+ it( 'should correctly identify Zod schemas', () => {
10
+ const zodSchema = z.object( {
11
+ name: z.string(),
12
+ age: z.number()
13
+ } );
14
+
15
+ expect( isZodSchema( zodSchema ) ).toBe( true );
16
+ } );
17
+
18
+ it( 'should correctly identify Zod string schema', () => {
19
+ const zodString = z.string();
20
+ expect( isZodSchema( zodString ) ).toBe( true );
21
+ } );
22
+
23
+ it( 'should correctly identify Zod array schema', () => {
24
+ const zodArray = z.array( z.number() );
25
+ expect( isZodSchema( zodArray ) ).toBe( true );
26
+ } );
27
+
28
+ it( 'should correctly identify Zod union schema', () => {
29
+ const zodUnion = z.union( [ z.string(), z.number() ] );
30
+ expect( isZodSchema( zodUnion ) ).toBe( true );
31
+ } );
32
+
33
+ it( 'should return false for JSON Schema objects', () => {
34
+ const jsonSchema = {
35
+ type: 'object',
36
+ properties: {
37
+ name: { type: 'string' },
38
+ age: { type: 'number' }
39
+ },
40
+ required: [ 'name' ]
41
+ };
42
+
43
+ expect( isZodSchema( jsonSchema ) ).toBe( false );
44
+ } );
45
+
46
+ it( 'should return false for plain objects', () => {
47
+ expect( isZodSchema( {} ) ).toBe( false );
48
+ expect( isZodSchema( { type: 'string' } ) ).toBe( false );
49
+ } );
50
+
51
+ it( 'should return false for null and undefined', () => {
52
+ expect( isZodSchema( null ) ).toBeFalsy();
53
+ expect( isZodSchema( undefined ) ).toBeFalsy();
54
+ } );
55
+
56
+ it( 'should return false for primitive values', () => {
57
+ expect( isZodSchema( 'string' ) ).toBe( false );
58
+ expect( isZodSchema( 123 ) ).toBe( false );
59
+ expect( isZodSchema( true ) ).toBe( false );
60
+ } );
61
+
62
+ it( 'should return false for arrays', () => {
63
+ expect( isZodSchema( [] ) ).toBe( false );
64
+ expect( isZodSchema( [ 1, 2, 3 ] ) ).toBe( false );
65
+ } );
66
+ } );
67
+ } );
@@ -1,17 +1,23 @@
1
1
  import { setMetadata } from './metadata.js';
2
2
  import { validateStep } from './validations/static.js';
3
- import { validateStepInput, validateStepOutput } from './validations/runtime.js';
3
+ import { validateWithSchema } from './schema_utils.js';
4
4
 
5
5
  export function step( { name, description, inputSchema, outputSchema, fn } ) {
6
- validateStep( { name, description, inputSchema, outputSchema, fn } );
6
+ validateStep( {
7
+ name,
8
+ description,
9
+ inputSchema,
10
+ outputSchema,
11
+ fn
12
+ } );
13
+
7
14
  const wrapper = async input => {
8
- if ( inputSchema ) {
9
- validateStepInput( name, inputSchema, input );
10
- }
15
+ validateWithSchema( inputSchema, input, `Step ${name} input` );
16
+
11
17
  const output = await fn( input );
12
- if ( outputSchema ) {
13
- validateStepOutput( name, outputSchema, output );
14
- }
18
+
19
+ validateWithSchema( outputSchema, output, `Step ${name} output` );
20
+
15
21
  return output;
16
22
  };
17
23
 
@@ -1,26 +1,27 @@
1
- import { ajv } from './ajv_provider.js';
2
1
  import * as z from 'zod';
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
6
6
  */
7
7
  export class StaticValidationError extends Error {};
8
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
- } );
9
+ const refineSchema = ( value, ctx ) => {
10
+ if ( !value || isZodSchema( value ) ) {
11
+ return;
16
12
  }
13
+
14
+ ctx.addIssue( {
15
+ code: 'invalid_type',
16
+ message: 'Schema must be a Zod schema'
17
+ } );
17
18
  };
18
19
 
19
20
  const stepAndWorkflowSchema = z.object( {
20
21
  name: z.string().regex( /^[a-z_][a-z0-9_]*$/i ),
21
22
  description: z.string().optional(),
22
- inputSchema: z.looseObject( {} ).optional().superRefine( refineJsonSchema ),
23
- outputSchema: z.looseObject( {} ).optional().superRefine( refineJsonSchema ),
23
+ inputSchema: z.any().optional().superRefine( refineSchema ),
24
+ outputSchema: z.any().optional().superRefine( refineSchema ),
24
25
  fn: z.function()
25
26
  } );
26
27
 
@@ -1,11 +1,12 @@
1
1
  import { describe, it, expect } from 'vitest';
2
+ import { z } from 'zod';
2
3
  import { validateStep, validateWorkflow, validateCreateWebhook, StaticValidationError } from './static.js';
3
4
 
4
5
  const validArgs = Object.freeze( {
5
6
  name: 'valid_name',
6
7
  description: 'desc',
7
- inputSchema: { type: 'object' },
8
- outputSchema: { type: 'object' },
8
+ inputSchema: z.object( {} ),
9
+ outputSchema: z.object( {} ),
9
10
  fn: () => {}
10
11
  } );
11
12
 
@@ -35,26 +36,24 @@ describe( 'interface/validator', () => {
35
36
  expect( () => validateStep( { ...validArgs, description: 10 } ) ).toThrow( error );
36
37
  } );
37
38
 
38
- it( 'rejects non-object inputSchema', () => {
39
- const error = new StaticValidationError( '✖ Invalid input: expected object, received string\n → at inputSchema' );
40
- expect( () => validateStep( { ...validArgs, inputSchema: 'not-an-object' } ) ).toThrow( error );
39
+ it( 'rejects non-Zod inputSchema', () => {
40
+ const error = new StaticValidationError( '✖ Schema must be a Zod schema\n → at inputSchema' );
41
+ expect( () => validateStep( { ...validArgs, inputSchema: 'not-a-zod-schema' } ) ).toThrow( error );
41
42
  } );
42
43
 
43
- it( 'rejects invalid inputSchema structure', () => {
44
- const error = new StaticValidationError( '✖ data/type must be equal to one of the allowed values, \
45
- data/type must be array, data/type must match a schema in anyOf\n → at inputSchema' );
46
- expect( () => validateStep( { ...validArgs, inputSchema: { type: 1 } } ) ).toThrow( error );
44
+ it( 'rejects JSON Schema inputSchema', () => {
45
+ const error = new StaticValidationError( '✖ Schema must be a Zod schema\n → at inputSchema' );
46
+ expect( () => validateStep( { ...validArgs, inputSchema: { type: 'object' } } ) ).toThrow( error );
47
47
  } );
48
48
 
49
- it( 'rejects non-object outputSchema', () => {
50
- const error = new StaticValidationError( '✖ Invalid input: expected object, received number\n → at outputSchema' );
49
+ it( 'rejects non-Zod outputSchema', () => {
50
+ const error = new StaticValidationError( '✖ Schema must be a Zod schema\n → at outputSchema' );
51
51
  expect( () => validateStep( { ...validArgs, outputSchema: 10 } ) ).toThrow( error );
52
52
  } );
53
53
 
54
- it( 'rejects invalid outputSchema structure', () => {
55
- const error = new StaticValidationError( '✖ data/type must be equal to one of the allowed values, \
56
- data/type must be array, data/type must match a schema in anyOf\n → at outputSchema' );
57
- expect( () => validateStep( { ...validArgs, outputSchema: { type: 1 } } ) ).toThrow( error );
54
+ it( 'rejects JSON Schema outputSchema', () => {
55
+ const error = new StaticValidationError( '✖ Schema must be a Zod schema\n → at outputSchema' );
56
+ expect( () => validateStep( { ...validArgs, outputSchema: { type: 'string' } } ) ).toThrow( error );
58
57
  } );
59
58
 
60
59
  it( 'rejects missing fn', () => {