import{buildSchema as b,isObjectType as y,isScalarType as I,isEnumType as R,isInterfaceType as g,isUnionType as T,isInputObjectType as d,getNamedType as h,printType as O}from"graphql";import{removeLeadingSlash as $}from"@redocly/theme/core/utils";import{toKebabCase as m}from"../../../../../../utils/string/to-kebab-case.js";import{capitalize as D}from"../../../../../../utils/string/capitalize.js";import{promiseMapLimit as u,promiseMapLimitWithStatus as w}from"../../../../../utils/async.js";import{removeMarkdocTags as L}from"../../../../markdown/markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as v}from"./base.js";import{sha1 as A}from"../../../../../utils/crypto.js";const S=15,E=15,F=new Set(["String","ID","Int","Float","Boolean","DateTime","Date","Time","URL","URI","JSON","JSONObject","BigInt","BigDecimal"]);class q extends v{#t;constructor(t){super("graphql",t),this.#t=t.context.logger}async loadApiDescriptions(){const t=[];for(const{relativePath:e}of await this.context.fs.scan(/(\.gql|\.graphql)$/))try{if(e.includes("@l10n")||await this.context.isPathIgnored(e))continue;const a=await this.context.cache.load(e,"graphql-doc");if(!a.data)continue;const{content:n,metadata:r}=a.data,s=b(n),o=A(n);t.push({realRelativePath:e,relativePath:e,isVirtual:!1,hash:o,schema:s,content:n,metadata:r})}catch(a){this.#t.warn(`Failed to load GraphQL schema from ${e}: ${a instanceof Error?a.message:"Unknown error"}`)}return t}mapApiDescriptionToEntity(t){const e=$(t.realRelativePath),a=t.relativePath.replace(/\.(gql|graphql)$/,""),n=this.#s(a.split("/").pop()||"GraphQL Schema"),r=m(n),s=this.#c(t.metadata?.tags);return{type:this.type,key:r,title:n,summary:t.metadata?.description||null,tags:s.concat("graphql"),metadata:{specType:this.specType,descriptionFile:e}}}async processApiDescription(t,e){const{schema:a}=t;a&&await Promise.all([this.#a(t,e),this.#f(t,e)])}#a=async(t,e)=>{const{schema:a}=t,n=a.getTypeMap(),r=this.#i(a),s=Object.entries(n).filter(([c])=>!c.startsWith("__")&&!F.has(c)&&!r.has(c));if(s.length===0)return;const o=await w(s,E,async([c,i])=>{const l=await this.#l({typeName:c,type:i,description:t,parentKey:e});await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:e,type:"uses",targetKey:l,fileHash:t.hash})},this.#t);o.count.failed>0&&this.#t.warn(`Schema processing completed with ${o.count.failed} failures out of ${s.length} types for ${t.realRelativePath}`),await this.#h({description:t,descriptionUniqueKey:e,userTypes:s})};#s=t=>{const e=t.replace(/[^A-Za-z0-9]+/g," ").trim();return e?e.split(" ").map(a=>a&&D(a)).join(" "):"GraphQL Schema"};#e=(t,e)=>`${e}-${m(t)}`;#n=t=>y(t)?"object":g(t)?"interface":T(t)?"union":R(t)?"enum":d(t)?"input":I(t)?"scalar":"unknown";#i=t=>{const e=new Set,a=t.getQueryType();a&&e.add(a.name);const n=t.getMutationType();n&&e.add(n.name);const r=t.getSubscriptionType();return r&&e.add(r.name),e};#r(t){const e=[],a=t.getQueryType();if(a){const s=a.getFields();for(const[o,c]of Object.entries(s))e.push({fieldName:o,field:c,operationType:"QUERY",rootTypeName:a.name})}const n=t.getMutationType();if(n){const s=n.getFields();for(const[o,c]of Object.entries(s))e.push({fieldName:o,field:c,operationType:"MUTATION",rootTypeName:n.name})}const r=t.getSubscriptionType();if(r){const s=r.getFields();for(const[o,c]of Object.entries(s))e.push({fieldName:o,field:c,operationType:"SUBSCRIBE",rootTypeName:r.name})}return e}#o=(t,e)=>{const a=[];if(t.args)for(const s of t.args){const o=h(s.type);o&&!o.name.startsWith("__")&&a.push(this.#e(o.name,e))}const n=[],r=h(t.type);return r&&!r.name.startsWith("__")&&n.push(this.#e(r.name,e)),{inputTypes:a,returnTypes:n}};#c=t=>{if(Array.isArray(t))return t.map(e=>String(e)).map(e=>e.trim()).filter(e=>e.length>0);if(typeof t=="string"){const e=t.trim();return e?[e]:[]}return[]};#l=async({typeName:t,type:e,description:a,parentKey:n})=>{const r=this.#e(t,n),s=e.description??null,o=this.#n(e),c={type:"data-schema",key:r,title:t,summary:s,tags:["graphql"],metadata:{specType:this.specType,typeKind:o,sdl:this.#p(e,a.schema)}};return await this.catalogEntitiesService.createEntityInLocalDatabase({entity:c,sourceFile:a.realRelativePath,fileHash:a.hash}),c.key};#p=(t,e)=>{const a=new Set,n=[],r=new Set(["String","ID","Int","Float","Boolean"]),s=i=>{if(!i)return;const l=i.name;if(l.startsWith("__")||r.has(l)||a.has(l))return;const p=e.getType(l);p&&(a.add(l),n.push(p))};for(s(t);n.length>0;){const i=n.pop();if(y(i)){for(const p of i.getInterfaces?.()??[])s(p);const l=i.getFields();for(const p of Object.values(l)){s(h(p.type));for(const f of p.args??[])s(h(f.type))}}else if(g(i)){const l=i.getFields();for(const p of Object.values(l))s(h(p.type))}else if(T(i))for(const l of i.getTypes())s(l);else if(d(i)){const l=i.getFields();for(const p of Object.values(l))s(h(p.type))}}const o=[t.name,...[...a].filter(i=>i!==t.name)],c=[];for(const i of o){const l=e.getType(i);l&&c.push(O(l))}return c.join(`

`)};#h=async({description:t,descriptionUniqueKey:e,userTypes:a})=>{const n=a.filter(([,r])=>y(r));n.length!==0&&await u(n,E,async([r,s])=>{const c=s.getInterfaces?.()??[];if(!c.length)return;const i=this.#e(r,e);await u([...c],5,async l=>{const p=this.#e(l.name,e);try{await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:i,type:"implements",targetKey:p,fileHash:t.hash})}catch(f){this.#t.warn(`Failed to create 'implements' relation ${i} -> ${p}: ${f instanceof Error?f.message:"Unknown error"}`)}})})};#f=async(t,e)=>{const{schema:a}=t,n=this.#r(a);if(n.length===0)return;const s=(await w(n,S,async({fieldName:o,field:c,operationType:i,rootTypeName:l})=>{await this.#y(o,c,i,l,t,e)},this.#t)).count.failed;s>0&&this.#t.warn(`Operation extraction completed with ${s} failures out of ${n.length} operations for ${e}`)};#y=async(t,e,a,n,r,s)=>{const{inputTypes:o,returnTypes:c}=this.#o(e,s),i=await this.#m({fieldName:t,field:e,operationType:a,rootTypeName:n,inputTypes:o,returnTypes:c,description:r});await this.#u({apiOperationKey:i,inputTypes:o,returnTypes:c,descriptionUniqueKey:s,description:r})};#m=async({fieldName:t,field:e,operationType:a,rootTypeName:n,inputTypes:r,returnTypes:s,description:o})=>{const c=t,i=`${this.mapApiDescriptionToEntity(o).key}-${m(`${a.toLowerCase()}-${t}`)}`,l={type:"api-operation",key:i,title:c,summary:e.description?L(e.description):null,tags:["graphql"],metadata:{method:a,path:`${n}.${t}`,payload:r,responses:s}};return await this.catalogEntitiesService.createEntityInLocalDatabase({entity:l,sourceFile:o.realRelativePath,fileHash:o.hash}),i};#u=async({apiOperationKey:t,inputTypes:e,returnTypes:a,descriptionUniqueKey:n,description:r})=>{await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:t,type:"partOf",targetKey:n,fileHash:r.hash});const s=[...new Set(e)],o=[...new Set(a)],c=[...s.map(i=>({key:i,relationType:"uses"})),...o.map(i=>({key:i,relationType:"returns"}))];await u(c,S,async({key:i,relationType:l})=>{try{await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:t,type:l,targetKey:i,fileHash:r.hash})}catch(p){this.#t.warn(`Failed to create relation between operation ${t} and type ${i} (${l}): ${p instanceof Error?p.message:"Unknown error"}`)}})}}export{q as GraphqlEntitiesExtractor};
