@@ -2,7 +2,18 @@ import { USER_FILE_ACCESSIBLE_PROPERTIES } from '@/lib/workflows/types'
22import { normalizeName } from '@/executor/constants'
33import { navigatePath } from '@/executor/variables/resolvers/reference'
44
5- export type OutputSchema = Record < string , { type ?: string ; description ?: string } | unknown >
5+ /**
6+ * A single schema node encountered while walking an `OutputSchema`. Captures
7+ * only the fields this module inspects — not a full schema type.
8+ */
9+ interface SchemaNode {
10+ type ?: string
11+ description ?: string
12+ properties ?: unknown
13+ items ?: unknown
14+ }
15+
16+ export type OutputSchema = Record < string , SchemaNode | unknown >
617
718export interface BlockReferenceContext {
819 blockNameMapping : Record < string , string >
@@ -29,25 +40,26 @@ export class InvalidFieldError extends Error {
2940 }
3041}
3142
43+ function asSchemaNode ( value : unknown ) : SchemaNode | undefined {
44+ if ( typeof value !== 'object' || value === null ) return undefined
45+ return value as SchemaNode
46+ }
47+
3248function isFileType ( value : unknown ) : boolean {
33- if ( typeof value !== 'object' || value === null ) return false
34- const typed = value as { type ?: string }
35- return typed . type === 'file' || typed . type === 'file[]'
49+ const node = asSchemaNode ( value )
50+ return node ?. type === 'file' || node ?. type === 'file[]'
3651}
3752
3853function isArrayType ( value : unknown ) : value is { type : 'array' ; items ?: unknown } {
39- if ( typeof value !== 'object' || value === null ) return false
40- return ( value as { type ?: string } ) . type === 'array'
54+ return asSchemaNode ( value ) ?. type === 'array'
4155}
4256
4357function getArrayItems ( schema : unknown ) : unknown {
44- if ( typeof schema !== 'object' || schema === null ) return undefined
45- return ( schema as { items ?: unknown } ) . items
58+ return asSchemaNode ( schema ) ?. items
4659}
4760
4861function getProperties ( schema : unknown ) : Record < string , unknown > | undefined {
49- if ( typeof schema !== 'object' || schema === null ) return undefined
50- const props = ( schema as { properties ?: unknown } ) . properties
62+ const props = asSchemaNode ( schema ) ?. properties
5163 return typeof props === 'object' && props !== null
5264 ? ( props as Record < string , unknown > )
5365 : undefined
@@ -69,6 +81,19 @@ function lookupField(schema: unknown, fieldName: string): unknown | undefined {
6981 return undefined
7082}
7183
84+ function isOpaqueSchemaNode ( value : unknown ) : boolean {
85+ const node = asSchemaNode ( value )
86+ if ( ! node ) return false
87+ // A schema node whose nested shape isn't enumerated. Any path beneath it
88+ // is accepted because there's no declared structure to validate against.
89+ // `object` / `json` with declared `properties` are walked via lookupField.
90+ if ( node . type === 'any' ) return true
91+ if ( ( node . type === 'json' || node . type === 'object' ) && node . properties === undefined ) {
92+ return true
93+ }
94+ return false
95+ }
96+
7297function isPathInSchema ( schema : OutputSchema | undefined , pathParts : string [ ] ) : boolean {
7398 if ( ! schema || pathParts . length === 0 ) {
7499 return true
@@ -83,6 +108,10 @@ function isPathInSchema(schema: OutputSchema | undefined, pathParts: string[]):
83108 return false
84109 }
85110
111+ if ( isOpaqueSchemaNode ( current ) ) {
112+ return true
113+ }
114+
86115 if ( / ^ \d + $ / . test ( part ) ) {
87116 if ( isFileType ( current ) ) {
88117 const nextPart = pathParts [ i + 1 ]
@@ -183,14 +212,12 @@ export function resolveBlockReference(
183212 }
184213
185214 const blockOutput = context . blockData [ blockId ]
186- const schema = context . blockOutputSchemas ?. [ blockId ]
187215
216+ // When the block has not produced any output (e.g. it lives on a branched
217+ // path that wasn't taken), resolve the reference to undefined without
218+ // validating against the declared schema. Callers map this to an empty
219+ // value so that references to skipped blocks don't fail the workflow.
188220 if ( blockOutput === undefined ) {
189- if ( schema && pathParts . length > 0 ) {
190- if ( ! isPathInSchema ( schema , pathParts ) ) {
191- throw new InvalidFieldError ( blockName , pathParts . join ( '.' ) , getSchemaFieldNames ( schema ) )
192- }
193- }
194221 return { value : undefined , blockId }
195222 }
196223
@@ -200,6 +227,7 @@ export function resolveBlockReference(
200227
201228 const value = navigatePath ( blockOutput , pathParts )
202229
230+ const schema = context . blockOutputSchemas ?. [ blockId ]
203231 if ( value === undefined && schema ) {
204232 if ( ! isPathInSchema ( schema , pathParts ) ) {
205233 throw new InvalidFieldError ( blockName , pathParts . join ( '.' ) , getSchemaFieldNames ( schema ) )
0 commit comments