@@ -10,7 +10,7 @@ import {
1010 type HealthCheckCandidate ,
1111 type HealthCheckSpec ,
1212} from "@executor-js/sdk/shared" ;
13- import { useIntegrationPlugins } from "@executor-js/sdk/client" ;
13+ import { useIntegrationPlugins , type IntegrationPreset } from "@executor-js/sdk/client" ;
1414import { integrationWriteKeys , healthCheckWriteKeys } from "@executor-js/react/api/reactivity-keys" ;
1515import { setIntegrationHealthCheck } from "@executor-js/react/api/atoms" ;
1616import {
@@ -49,7 +49,11 @@ import type { SpecPreviewSummary } from "../sdk/preview";
4949import { type Authentication } from "../sdk/types" ;
5050import { resolveServerUrl } from "../sdk/openapi-utils" ;
5151import { detectedAuthenticationTemplates } from "../sdk/derive-auth" ;
52- import { parseSpecOverridesText } from "../sdk/spec-overrides" ;
52+ import {
53+ decodeOpenApiSpecOverrides ,
54+ formatSpecOverridesText ,
55+ parseSpecOverridesText ,
56+ } from "../sdk/spec-overrides" ;
5357import { SpecOverridesEditor } from "./SpecOverridesEditor" ;
5458
5559const normalizePresetUrl = ( url : string ) : string => {
@@ -61,6 +65,22 @@ const normalizePresetUrl = (url: string): string => {
6165 return parsed . toString ( ) . replace ( / \/ $ / , "" ) ;
6266} ;
6367
68+ /** Resolve catalog defaults from an explicit preset id or a matching spec URL. */
69+ export const resolveOpenApiPreset = (
70+ presets : readonly IntegrationPreset [ ] | undefined ,
71+ initialPresetId : string | undefined ,
72+ specUrl : string ,
73+ ) : IntegrationPreset | null => {
74+ const explicitPreset = presets ?. find ( ( preset ) => preset . id === initialPresetId ) ;
75+ if ( explicitPreset ) return explicitPreset ;
76+ if ( specUrl . trim ( ) . length === 0 ) return null ;
77+ const normalizedSpecUrl = normalizePresetUrl ( specUrl ) ;
78+ return (
79+ presets ?. find ( ( preset ) => preset . url && normalizePresetUrl ( preset . url ) === normalizedSpecUrl ) ??
80+ null
81+ ) ;
82+ } ;
83+
6484const specInputForAdd = ( input : string ) => {
6585 const value = input . trim ( ) ;
6686 const parsed = Effect . runSyncExit (
@@ -154,20 +174,23 @@ export default function AddOpenApiIntegration(props: {
154174 const integrationPlugins = useIntegrationPlugins ( ) ;
155175 const openApiPlugin = integrationPlugins . find ( ( plugin ) => plugin . key === "openapi" ) ;
156176 const openApiPresets = openApiPlugin ?. presets ;
157- const initialPreset = openApiPresets ?. find ( ( preset ) => preset . id === props . initialPreset ) ?? null ;
158177 const [ specUrl , setSpecUrl ] = useState ( props . initialUrl ?? "" ) ;
159- const [ specOverridesText , setSpecOverridesText ] = useState ( "" ) ;
178+ const [ specOverridesDraft , setSpecOverridesDraft ] = useState < string | null > ( null ) ;
160179 const [ analyzing , setAnalyzing ] = useState ( false ) ;
161180 const [ analyzeError , setAnalyzeError ] = useState < string | null > ( null ) ;
162181
182+ const activePreset = resolveOpenApiPreset ( openApiPresets , props . initialPreset , specUrl ) ;
183+ const presetSpecOverrides = decodeOpenApiSpecOverrides ( activePreset ?. specOverrides ) ;
184+ const specOverridesText = specOverridesDraft ?? formatSpecOverridesText ( presetSpecOverrides ) ;
185+
163186 // After analysis
164187 const [ preview , setPreview ] = useState < SpecPreviewSummary | null > ( null ) ;
165188 const [ baseUrl , setBaseUrl ] = useState ( "" ) ;
166189 // Agent-visible description: prefilled from the spec's `info.description`
167190 // until the user types (null = untouched, keep deriving from the preview).
168191 const [ descriptionDraft , setDescriptionDraft ] = useState < string | null > ( null ) ;
169192 const identityFallbackName =
170- initialPreset ?. name ?? ( preview ? Option . getOrElse ( preview . title , ( ) => "" ) : "" ) ;
193+ activePreset ?. name ?? ( preview ? Option . getOrElse ( preview . title , ( ) => "" ) : "" ) ;
171194 const identity = useIntegrationIdentity ( {
172195 fallbackName : identityFallbackName ,
173196 fallbackNamespace : props . initialNamespace ,
@@ -219,14 +242,11 @@ export default function AddOpenApiIntegration(props: {
219242 const firstServerUrl = firstServer
220243 ? resolveServerUrl ( firstServer . url , Option . getOrUndefined ( firstServer . variables ) , { } )
221244 : "" ;
222- const previewPresetIcon =
223- openApiPresets ?. find (
224- ( preset ) => preset . url && normalizePresetUrl ( preset . url ) === normalizePresetUrl ( specUrl ) ,
225- ) ?. icon ?? null ;
245+ const previewPresetIcon = activePreset ?. icon ?? null ;
226246
227247 const resolvedBaseUrl = baseUrl . trim ( ) ;
228248 const resolvedIntegrationId =
229- initialPreset ?. defaultSlug ||
249+ activePreset ?. defaultSlug ||
230250 slugifyNamespace ( identity . namespace ) ||
231251 ( preview ? Option . getOrElse ( preview . title , ( ) => "openapi" ) : "openapi" ) ;
232252 const resolvedDisplayName =
@@ -245,8 +265,8 @@ export default function AddOpenApiIntegration(props: {
245265 // Keyed off `preview` (stable per analysis) so the memo doesn't re-run on the
246266 // freshly-allocated `?? []` fallback arrays.
247267 const authenticationTemplate : readonly Authentication [ ] = useMemo ( ( ) => {
248- if ( initialPreset ?. authTemplate ) {
249- return initialPreset . authTemplate . flatMap ( ( template ) : readonly Authentication [ ] =>
268+ if ( activePreset ?. authTemplate ) {
269+ return activePreset . authTemplate . flatMap ( ( template ) : readonly Authentication [ ] =>
250270 template . kind === "oauth2"
251271 ? [
252272 {
@@ -263,7 +283,7 @@ export default function AddOpenApiIntegration(props: {
263283 preview ?. oauth2Presets ?? [ ] ,
264284 resolvedBaseUrl ,
265285 ) ;
266- } , [ initialPreset ?. authTemplate , preview , resolvedBaseUrl ] ) ;
286+ } , [ activePreset ?. authTemplate , preview , resolvedBaseUrl ] ) ;
267287
268288 // Editable auth methods, seeded from the spec-detected templates. The add flow
269289 // registers EVERY method (P6), so this is a LIST, preserving multi-method
@@ -359,7 +379,7 @@ export default function AddOpenApiIntegration(props: {
359379 const exit = await doPreview ( {
360380 payload : {
361381 spec : specUrl ,
362- specFormat : initialPreset ?. specFormat ,
382+ specFormat : activePreset ?. specFormat ,
363383 ...( parsedSpecOverrides . value . length > 0
364384 ? { specOverrides : parsedSpecOverrides . value }
365385 : { } ) ,
@@ -391,9 +411,9 @@ export default function AddOpenApiIntegration(props: {
391411 ? { description : resolvedDescription . trim ( ) }
392412 : { } ) ,
393413 baseUrl : resolvedBaseUrl ,
394- specFormat : initialPreset ?. specFormat ,
395- family : initialPreset ?. family ,
396- healthCheck : initialPreset ?. healthCheck ,
414+ specFormat : activePreset ?. specFormat ,
415+ family : activePreset ?. family ,
416+ healthCheck : activePreset ?. healthCheck ,
397417 ...( parsedSpecOverrides . ok && parsedSpecOverrides . value . length > 0
398418 ? { specOverrides : parsedSpecOverrides . value }
399419 : { } ) ,
@@ -422,9 +442,9 @@ export default function AddOpenApiIntegration(props: {
422442 resolvedBaseUrl ,
423443 editedAuthenticationTemplate ,
424444 parsedSpecOverrides ,
425- initialPreset ?. family ,
426- initialPreset ?. healthCheck ,
427- initialPreset ?. specFormat ,
445+ activePreset ?. family ,
446+ activePreset ?. healthCheck ,
447+ activePreset ?. specFormat ,
428448 ] ) ;
429449
430450 const handleAdd = async ( ) => {
@@ -565,7 +585,7 @@ export default function AddOpenApiIntegration(props: {
565585 < SpecOverridesEditor
566586 value = { specOverridesText }
567587 onChange = { ( value ) => {
568- setSpecOverridesText ( value ) ;
588+ setSpecOverridesDraft ( value ) ;
569589 setAnalyzeError ( null ) ;
570590 setPreview ( null ) ;
571591 setBaseUrl ( "" ) ;
@@ -583,7 +603,7 @@ export default function AddOpenApiIntegration(props: {
583603
584604 { preview ? (
585605 < AddOpenApiHealthCheckSection
586- presetHealthCheck = { initialPreset ?. healthCheck }
606+ presetHealthCheck = { activePreset ?. healthCheck }
587607 candidates = { healthCheckCandidates }
588608 selected = { hcSelected }
589609 operation = { hcOperation }
0 commit comments