77// blueprint.namespaces.
88
99import type {
10+ ActionAttemptStatus ,
1011 Blueprint ,
1112 Endpoint ,
1213 Property ,
@@ -23,6 +24,7 @@ import { setImportsLayoutContext } from './layouts/imports.js'
2324import {
2425 type DiscriminatedVariantSource ,
2526 getCommonScalarProperties ,
27+ sameDescription ,
2628 setResourceLayoutContext ,
2729} from './layouts/resource.js'
2830import { setRoutesFileLayoutContext } from './layouts/routes-file.js'
@@ -94,12 +96,46 @@ interface ResourceSource extends ResourceDocumentation {
9496 variants ?: DiscriminatedVariantSource [ ]
9597}
9698
99+ // The fallback class only scopes a property to statuses when every known
100+ // variant scopes it; the statuses are the union across the variants. If any
101+ // variant leaves the property unscoped, the fallback leaves it unscoped too.
102+ const mergeActionAttemptStatuses = (
103+ occurrences : Property [ ] ,
104+ ) : ActionAttemptStatus [ ] | undefined => {
105+ if (
106+ occurrences . some (
107+ ( { actionAttemptStatuses } ) => actionAttemptStatuses == null ,
108+ )
109+ ) {
110+ return undefined
111+ }
112+ return [
113+ ...new Set (
114+ occurrences . flatMap (
115+ ( { actionAttemptStatuses } ) => actionAttemptStatuses ?? [ ] ,
116+ ) ,
117+ ) ,
118+ ]
119+ }
120+
97121const createActionAttemptBaseProperties = (
98122 variants : DiscriminatedVariantSource [ ] ,
99123) : Property [ ] => {
100124 const propertyLists = variants . map ( ( { properties } ) => properties )
101125 const common = getCommonScalarProperties ( propertyLists )
102- const nested = [ 'error' , 'result' ] . flatMap ( ( name ) => {
126+
127+ // Nested object properties every variant carries (e.g. error and result)
128+ // fall back to the scalar fields their occurrences share.
129+ const nestedNames = [
130+ ...new Set (
131+ propertyLists . flatMap ( ( properties ) =>
132+ properties
133+ . filter ( ( { format } ) => format === 'object' )
134+ . map ( ( { name } ) => name ) ,
135+ ) ,
136+ ) ,
137+ ]
138+ const nested = nestedNames . flatMap ( ( name ) => {
103139 const occurrences = propertyLists . map ( ( properties ) =>
104140 properties . find ( ( property ) => property . name === name ) ,
105141 )
@@ -116,17 +152,21 @@ const createActionAttemptBaseProperties = (
116152 >
117153 const first = objects [ 0 ]
118154 if ( first == null ) return [ ]
119- return [
120- {
121- ...first ,
122- description :
123- name === 'error' ? 'Error associated with the action.' : '' ,
124- isNullable : true ,
125- properties : getCommonScalarProperties (
126- objects . map ( ( { properties } ) => properties ) ,
127- ) ,
128- } ,
129- ]
155+ const property : Extract < Property , { format : 'object' } > = {
156+ ...first ,
157+ description : sameDescription ( objects ) ,
158+ isNullable : objects . some ( ( { isNullable } ) => isNullable ) ,
159+ properties : getCommonScalarProperties (
160+ objects . map ( ( { properties } ) => properties ) ,
161+ ) ,
162+ }
163+ const statuses = mergeActionAttemptStatuses ( objects )
164+ if ( statuses == null ) {
165+ delete property . actionAttemptStatuses
166+ } else {
167+ property . actionAttemptStatuses = statuses
168+ }
169+ return [ property ]
130170 } )
131171
132172 return [ ...common , ...nested ] . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
@@ -157,16 +197,13 @@ const getResources = (
157197 }
158198
159199 if ( blueprint . actionAttempts . length > 0 ) {
200+ // Properties annotated with actionAttemptStatuses only hold a value for
201+ // the listed statuses (e.g. error and result while pending); the resource
202+ // layout turns that annotation into nullable, status-scoped accessors.
160203 const variants = blueprint . actionAttempts . map ( ( actionAttempt ) => ( {
161204 discriminatorValue : actionAttempt . actionAttemptType ,
162205 description : actionAttempt . description ,
163- // Action attempts can return null for both fields while pending. The
164- // blueprint currently loses that per-status nullability upstream.
165- properties : actionAttempt . properties . map ( ( property ) =>
166- [ 'error' , 'result' ] . includes ( property . name )
167- ? { ...property , isNullable : true }
168- : property ,
169- ) ,
206+ properties : actionAttempt . properties ,
170207 } ) )
171208 resources . set ( 'action_attempt' , {
172209 description :
0 commit comments