@@ -28,23 +28,90 @@ const stripSdkSurfaces = (doc: DiscoveryDocWithSurfaces): DiscoveryDocWithSurfac
2828 surfaces : doc . surfaces . filter ( ( s ) => ! isSdkNotCli ( s ) ) ,
2929} ) ;
3030
31- /** The domain page's render source: durable discovery result first, then the
32- * prerendered baseline discovery JSON. */
31+ type SurfaceLike = {
32+ type ?: string ;
33+ url ?: string ;
34+ spec ?: string ;
35+ slug ?: string ;
36+ } ;
37+
38+ /** The machine locator per surface type — what a consumer needs to actually
39+ * connect (an http surface's docs page or base URL is not it). */
40+ const locatorField = ( type : string | undefined ) : "spec" | "url" | null =>
41+ type === "http" || type === "graphql" ? "spec" : type === "mcp" ? "url" : null ;
42+
43+ // GraphQL's connectable locator is really the endpoint; treat a surface as
44+ // machine-reachable when either the endpoint or the SDL pointer is present.
45+ const hasLocator = ( s : SurfaceLike ) : boolean =>
46+ s . type === "graphql" ? ! ! ( s . url ?? s . spec ) : ! ! s [ locatorField ( s . type ) ?? "url" ] ;
47+
48+ /**
49+ * Live-discovered docs sometimes drop the machine locators the static catalog
50+ * carries (the LLM found the API but not its spec URL). Backfill from the
51+ * baseline, conservatively, per surface type:
52+ * - any stored surface of the type already has a locator → leave the type alone;
53+ * - exactly one stored surface and one locator-bearing baseline candidate →
54+ * fill the stored surface's missing `spec`/`url`;
55+ * - otherwise append the baseline's locator-bearing surfaces (slug-deduped),
56+ * so registry-known specs stay reachable alongside the discovered surface.
57+ */
58+ const backfillBaselineLocators = (
59+ stored : DiscoveryDocWithSurfaces ,
60+ baseline : DiscoveryDocWithSurfaces ,
61+ ) : DiscoveryDocWithSurfaces => {
62+ const surfaces : SurfaceLike [ ] = [ ...stored . surfaces ] ;
63+ const takenSlugs = new Set ( surfaces . map ( ( s ) => s . slug ) . filter ( Boolean ) ) ;
64+ for ( const type of [ "http" , "graphql" , "mcp" ] ) {
65+ const storedOfType = surfaces . filter ( ( s ) => s . type === type ) ;
66+ if ( storedOfType . some ( hasLocator ) ) continue ;
67+ const candidates = ( baseline . surfaces as SurfaceLike [ ] ) . filter (
68+ ( s ) => s . type === type && hasLocator ( s ) ,
69+ ) ;
70+ if ( candidates . length === 0 ) continue ;
71+ if ( storedOfType . length === 1 && candidates . length === 1 ) {
72+ const target = storedOfType [ 0 ] ! ;
73+ const source = candidates [ 0 ] ! ;
74+ if ( ! target . spec && source . spec ) target . spec = source . spec ;
75+ if ( ! target . url && source . url ) target . url = source . url ;
76+ continue ;
77+ }
78+ for ( const candidate of candidates ) {
79+ if ( candidate . slug && takenSlugs . has ( candidate . slug ) ) continue ;
80+ if ( candidate . slug ) takenSlugs . add ( candidate . slug ) ;
81+ surfaces . push ( candidate ) ;
82+ }
83+ }
84+ return { ...stored , surfaces : surfaces as DiscoveryDocWithSurfaces [ "surfaces" ] } ;
85+ } ;
86+
87+ const baselineDoc = async (
88+ env : Env ,
89+ origin : string ,
90+ canonical : string ,
91+ ) : Promise < DiscoveryDocWithSurfaces | null > => {
92+ const res = await env . ASSETS . fetch ( `${ origin } /disc/${ encodeURIComponent ( canonical ) } .json` ) ;
93+ if ( ! res . ok ) return null ;
94+ const baseline = ( await res . json ( ) ) as DiscoverData ;
95+ return hasSurfaceArray ( baseline ) ? baseline : null ;
96+ } ;
97+
98+ /** The domain page's render source: durable discovery result first (with
99+ * missing machine locators backfilled from the baseline), then the prerendered
100+ * baseline discovery JSON. */
33101export async function discoveryDoc ( env : Env , origin : string , domain : string ) : Promise < DiscoverData | null > {
34102 const canonical = canonicalDomain ( domain ) ;
35103 try {
36104 const raw = await discoveryKvGet ( env , canonical ) ;
37105 if ( raw ) {
38106 const stored = JSON . parse ( raw ) as { result ?: DiscoverData ; discoveredAt ?: string } ;
39107 if ( hasSurfaceArray ( stored . result ) ) {
40- return stripSdkSurfaces ( { ...stored . result , discoveredAt : stored . result . discoveredAt ?? stored . discoveredAt } ) ;
108+ const doc = { ...stored . result , discoveredAt : stored . result . discoveredAt ?? stored . discoveredAt } ;
109+ const baseline = await baselineDoc ( env , origin , canonical ) ;
110+ return stripSdkSurfaces ( baseline ? backfillBaselineLocators ( doc , baseline ) : doc ) ;
41111 }
42112 }
43- const res = await env . ASSETS . fetch ( `${ origin } /disc/${ encodeURIComponent ( canonical ) } .json` ) ;
44- if ( res . ok ) {
45- const baseline = ( await res . json ( ) ) as DiscoverData ;
46- if ( hasSurfaceArray ( baseline ) ) return stripSdkSurfaces ( baseline ) ;
47- }
113+ const baseline = await baselineDoc ( env , origin , canonical ) ;
114+ if ( baseline ) return stripSdkSurfaces ( baseline ) ;
48115 } catch {
49116 /* unavailable or malformed discovery data */
50117 }
0 commit comments