oapiex 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +10 -10
- package/dist/index.cjs +114 -18
- package/dist/index.d.ts +4 -1
- package/dist/index.mjs +113 -19
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"axios";import{Logger as r}from"@h3ravel/shared";import i from"node:path";import a,{readFile as o}from"node:fs/promises";import{Command as s,Kernel as c}from"@h3ravel/musket";import l from"prettier";import{pathToFileURL as u}from"node:url";import{existsSync as d,readdirSync as f}from"node:fs";import{fileURLToPath as p}from"url";const m=[`axios`,`happy-dom`,`jsdom`,`puppeteer`],h={outputFormat:`pretty`,outputShape:`raw`,requestTimeout:5e4,maxRedirects:5,userAgent:`Mozilla/5.0 (X11; Linux x64) AppleWebKit/537.36 (KHTML, like Gecko) OpenApiExtractor/1.0.0`,retryCount:3,retryDelay:1e3,browser:`puppeteer`,happyDom:{enableJavaScriptEvaluation:!0,suppressInsecureJavaScriptEnvironmentWarning:!0},puppeteer:{headless:!0,args:[`--no-sandbox`,`--disable-setuid-sandbox`]}};let g=h;const _=()=>globalThis.__oapieBrowserSession,v=async(e=g)=>{let t=_();if(t?.browser===e.browser)return t;t&&await y();let n={browser:e.browser,closers:[]};return e.browser===`puppeteer`&&(n.puppeteerBrowser=await(await import(`puppeteer`)).launch({headless:e.puppeteer?.headless??!0,args:e.puppeteer?.args??[`--no-sandbox`,`--disable-setuid-sandbox`]})),globalThis.__oapieBrowserSession=n,n},y=async()=>{let e=_();if(e){globalThis.__oapieBrowserSession=void 0;for(let t of e.closers.reverse())await t();e.puppeteerBrowser&&await e.puppeteerBrowser.close()}},b=(e,t)=>{let n=_();return!n||n.browser!==e?!1:(n.closers.push(t),!0)},x=e=>{let t={...h,...e,happyDom:{...h.happyDom,...e.happyDom}};return g=t,t},S=e=>m.includes(e),C=async(r,i=g,a=!1)=>{let{data:o}=i.browser===`puppeteer`?{data:``}:await n.get(r,{timeout:i.requestTimeout,maxRedirects:i.maxRedirects,headers:{"User-Agent":i.userAgent,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8`}});if(i.browser===`axios`)return o;if(i.browser===`happy-dom`){let e=new t({url:r,innerWidth:1024,innerHeight:768,settings:i.happyDom});e.document.write(o),await e.happyDOM.waitUntilComplete();let n=e.document.documentElement.outerHTML;if(!n)throw await e.happyDOM.close(),Error(`Unable to extract HTML from remote source: ${r}`);return b(`happy-dom`,()=>e.happyDOM.close())||await e.happyDOM.close(),n}else if(i.browser===`jsdom`){let t;try{({window:t}=new e(o,{url:r,contentType:`text/html`,runScripts:`dangerously`,includeNodeLocations:!0}));let n=t.document.documentElement.outerHTML;if(!n)throw Error(`Unable to extract HTML from remote source: ${r}`);let i=t;return b(`jsdom`,()=>i.close())||t.close(),t=void 0,n}finally{t&&t.close()}}else if(i.browser===`puppeteer`){let e=_(),t=Math.max(i.requestTimeout,3e4),o=e?.browser===`puppeteer`?e.puppeteerBrowser:void 0,s=!1,c;try{o||(o=await(await import(`puppeteer`)).launch({headless:i.puppeteer?.headless??!0,args:i.puppeteer?.args??[`--no-sandbox`,`--disable-setuid-sandbox`]}),s=!0),c=await o.newPage(),await c.setUserAgent({userAgent:i.userAgent}),await c.setRequestInterception(!0),c.on(`request`,e=>{let t=e.resourceType();if([`image`,`font`,`media`,`stylesheet`].includes(t))return void e.abort();e.continue()});try{await c.goto(r,{waitUntil:`domcontentloaded`,timeout:t})}catch(e){if(!c||!await ae(c))throw e}let e=await ne(c,t,a);if(!e)throw Error(`Unable to extract HTML from remote source: ${r}`);if(!e.includes(`id="ssr-props"`)){let{data:t}=await n.get(r,{timeout:i.requestTimeout,maxRedirects:i.maxRedirects,headers:{"User-Agent":i.userAgent,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8`}});e=oe(e,t)}return e}finally{c&&!c.isClosed()&&await c.close(),s&&o&&await o.close()}}else throw Error(`Unsupported browser specified in configuration: ${g.browser}`)},ee=async(e,t,n=!1)=>{try{n&&await e.waitForSelector(`.hub-sidebar-content .rm-Sidebar-link`),await e.waitForSelector(`[data-testid="http-method"], article#content, script#ssr-props`,{timeout:t})}catch{}},te=async(e,t)=>{try{await e.waitForFunction(()=>{let e=!!document.querySelector(`[data-testid="http-method"]`),t=!!document.querySelector(`form[name="Parameters"]`),n=!!document.querySelector(`.rm-PlaygroundRequest`),r=!!document.querySelector(`.rm-PlaygroundResponse`),i=!!document.querySelector(`script#ssr-props`);return e?t||n||r||i:!1},{timeout:t})}catch{}try{await e.waitForSelector(`.rm-PlaygroundRequest, .rm-PlaygroundResponse, form[name="Parameters"], script#ssr-props`,{timeout:Math.min(t,5e3)})}catch{}try{await e.waitForNetworkIdle?.({idleTime:500,timeout:Math.min(t,5e3)})}catch{}},ne=async(e,t,n=!1,r=3)=>{let i;for(let a=1;a<=r;a+=1)try{return await ee(e,t,n),await te(e,t),await e.content()}catch(n){if(i=n,!ie(n)||a===r)throw n;await re(e,t)}throw i instanceof Error?i:Error(`Unable to extract stable HTML from remote source`)},re=async(e,t)=>{try{await e.waitForFunction(()=>document.readyState===`interactive`||document.readyState===`complete`,{timeout:Math.min(t,5e3)})}catch{}try{await e.waitForNetworkIdle?.({idleTime:500,timeout:Math.min(t,5e3)})}catch{}},ie=e=>{if(!(e instanceof Error))return!1;let t=e.message.toLowerCase();return t.includes(`execution context was destroyed`)||t.includes(`cannot find context with specified id`)||t.includes(`most likely because of a navigation`)},ae=async e=>!!await e.$(`[data-testid="http-method"], article#content, script#ssr-props`),oe=(e,t)=>{if(e.includes(`id="ssr-props"`))return e;let n=se(t);return n?e.includes(`</body>`)?e.replace(`</body>`,`${n}</body>`):e.includes(`</html>`)?e.replace(`</html>`,`${n}</html>`):`${e}${n}`:e},se=e=>e.match(/<script id="ssr-props"[^>]*>[\s\S]*?<\/script>/i)?.[0]??null;var w=class e{static parsePossiblyTruncated=t=>{let n=new e,r=t.trim();if(!/^(?:\{|\[)/.test(r))return null;try{return JSON.parse(r)}catch{let e=n.repairCommonJsonIssues(r);try{return JSON.parse(e)}catch{return null}}};repairCommonJsonIssues=e=>{let t=this.removeUnexpectedObjectTokens(e),n=this.insertMissingCommas(t);return`${n}${this.buildMissingJsonClosers(n)}`};removeUnexpectedObjectTokens=e=>e.replace(/([[{,]\s*)([A-Za-z_$][\w$-]*)(?=\s*"(?:\\.|[^"\\])*"\s*:)/g,`$1`);insertMissingCommas=e=>{let t=``,n=!1,r=!1,i=``;for(let a=0;a<e.length;a+=1){let o=e[a];if(n){if(t+=o,r){r=!1;continue}if(o===`\\`){r=!0;continue}o===`"`&&(n=!1,i=`"`);continue}if(o===`"`){let r=e.slice(a);/^"(?:\\.|[^"\\])*"\s*:/.test(r)&&this.shouldInsertCommaBeforeKey(i,t)&&(t+=`,`),t+=o,n=!0;continue}t+=o,/\s/.test(o)||(i=o)}return t};shouldInsertCommaBeforeKey=(e,t)=>{if(!e||![`"`,`}`,`]`,`e`,`l`,`0`,`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`].includes(e))return!1;let n=t.trimEnd();return!n.endsWith(`,`)&&!n.endsWith(`{`)};buildMissingJsonClosers=e=>{let t=[],n=!1,r=!1;for(let i of e){if(r){r=!1;continue}if(i===`\\`){r=!0;continue}if(i===`"`){n=!n;continue}if(!n){if(i===`{`){t.push(`}`);continue}if(i===`[`){t.push(`]`);continue}(i===`}`||i===`]`)&&t[t.length-1]===i&&t.pop()}}return t.reverse().join(``)}};const T=e=>typeof e==`object`&&!!e&&!Array.isArray(e),E=e=>{let n=new t;n.document.write(e);let{document:r}=n,i=r.querySelector(`article#content`)??r.body,a=i.querySelector(`form[name="Parameters"]`),o=r.querySelector(`.rm-PlaygroundRequest`),s=r.querySelector(`.rm-PlaygroundResponse`),c=j(o),l=Te(c[0]??null),u=M(s);return ce({method:q(i.querySelector(`[data-testid="http-method"]`))?.toUpperCase()??null,url:q(i.querySelector(`[data-testid="serverurl"]`)),description:A(i),sidebarLinks:N(r.querySelector(`.rm-Sidebar.hub-sidebar-content`)),requestParams:P(a),requestCodeSnippets:c,requestExample:c[0]?.body??null,requestExampleNormalized:l,responseSchemas:F(i),responseBodies:u,responseExample:u[0]?.body??null,responseExampleRaw:u[0]?.rawBody??null},le(r))},ce=(e,t)=>t?{method:e.method??t.method,url:e.url??t.url,description:e.description??t.description,sidebarLinks:e.sidebarLinks.length>0?e.sidebarLinks:t.sidebarLinks,requestParams:e.requestParams.length>0?e.requestParams:t.requestParams,requestCodeSnippets:e.requestCodeSnippets.length>0?e.requestCodeSnippets:t.requestCodeSnippets,requestExample:e.requestExample??t.requestExample,requestExampleNormalized:e.requestExampleNormalized??t.requestExampleNormalized,responseSchemas:e.responseSchemas.length>0?e.responseSchemas:t.responseSchemas,responseBodies:e.responseBodies.length>0?e.responseBodies:t.responseBodies,responseExample:e.responseExample??t.responseExample,responseExampleRaw:e.responseExampleRaw??t.responseExampleRaw}:e,le=e=>{let t=e.querySelector(`script#ssr-props`)?.textContent?.trim();if(!t)return null;let n;try{n=JSON.parse(t)}catch{return null}let r=T(n)&&T(n.document)?n.document.api:null;if(!T(r))return null;let i=typeof r.method==`string`?r.method.toUpperCase():null,a=typeof r.path==`string`?r.path:null,o=T(r.schema)?r.schema:null,s=D(o,a,i?.toLowerCase()??null),c=Array.isArray(o?.servers)&&T(o.servers[0])&&typeof o.servers[0].url==`string`?o.servers[0].url:null;if(!s&&!i&&!a)return null;let l=me(s?.responses??{});return{method:i,url:de(c,a),description:s?.description??null,sidebarLinks:[],requestParams:[...ue(s?.parameters),...fe(s?.requestBody)],requestCodeSnippets:[],requestExample:null,requestExampleNormalized:null,responseSchemas:pe(s?.responses??{}),responseBodies:l,responseExample:l[0]?.body??null,responseExampleRaw:l[0]?.rawBody??null}},ue=e=>e?e.map(e=>({name:e.name,in:e.in,path:[e.name],type:e.schema?.type??null,required:e.required??!1,defaultValue:e.schema?.default==null?e.example==null?null:String(e.example):String(e.schema.default),description:e.description??e.schema?.description??null})):[],D=(e,t,n)=>{if(!e||!t||!n||!T(e.paths))return null;let r=e.paths[t];return!T(r)||!T(r[n])?null:r[n]},de=(e,t)=>!e||!t?null:`${e.replace(/\/$/,``)}${t.startsWith(`/`)?t:`/${t}`}`,fe=e=>{let t=e?.content?.[`application/json`]?.schema;return t?O(t):[]},O=(e,t=[])=>{if(!e.properties)return[];let n=[];for(let[r,i]of Object.entries(e.properties)){let a=[...t,r],o=e.required?.includes(r)??!1;if(i.type===`object`&&i.properties){n.push(...O(i,a).map(e=>({...e,required:e.path.length===a.length+1?o&&e.required:e.required})));continue}n.push({name:r,in:`body`,path:a,type:i.type??null,required:o,defaultValue:i.default==null?null:String(i.default),description:i.description??null})}return n},pe=e=>Object.entries(e).map(([e,t])=>({statusCode:e,description:t.description??e})),me=e=>{let t=[];for(let[n,r]of Object.entries(e))for(let[e,i]of Object.entries(r.content??{})){let a=k(i);if(a==null)continue;let o=typeof a==`string`?a:JSON.stringify(a,null,2),s=V(o,e);t.push({format:s.format,contentType:e,statusCode:n,label:r.description??n,body:s.body,rawBody:o})}return t},k=e=>{if(e.example!==void 0)return e.example;let t=e.examples;if(t&&T(t)){for(let e of Object.values(t))if(T(e)&&`value`in e)return e.value??null}return e.schema?.example===void 0?null:e.schema.example},A=e=>{let t=e.querySelector(`header`);if(!t)return null;let n=Array.from(t.querySelectorAll(`[data-testid="RDMD"]`));return q(n[n.length-1]??null)},j=e=>{if(!e)return[];let t=R(e);return I(e).map(e=>({label:t,body:e}))},M=e=>{if(!e)return[];let t=I(e),n=ge(e),r=_e(e);return t.map((e,t)=>{let i=r[t]??r[0]??null,a=n[t]??n[0]??null,o=V(e,a);return{format:o.format,contentType:a,statusCode:i?.match(/\b\d{3}\b/)?.[0]??null,label:i,body:o.body,rawBody:e}})},N=e=>e?Array.from(e.querySelectorAll(`.rm-Sidebar-link`)).map(e=>{let t=e.closest(`.rm-Sidebar-section`),n=q(e.querySelector(`[data-testid="http-method"]`))?.toUpperCase()??null;return{section:q(t?.querySelector(`.rm-Sidebar-heading`)??null),label:he(e,n),href:e.getAttribute(`href`),method:n,active:e.classList.contains(`active`)||e.getAttribute(`aria-current`)===`page`,subpage:e.classList.contains(`subpage`)}}).filter(e=>e.label.length>0):[],P=e=>e?Array.from(e.querySelectorAll(`label`)).map(t=>{let n=q(t),r=ve(t,e),i=ye(r,t);return{name:n??``,in:be(r,t,e),path:xe(t,n),type:Se(r,i),required:Ce(r,i),defaultValue:z(i),description:we(r)}}).filter(e=>e.name.length>0):[],F=e=>{let t=e.querySelector(`#response-schemas`)?.nextElementSibling;return!t||!t.classList.contains(`rm-APIResponseSchemaPicker`)?[]:Array.from(t.querySelectorAll(`button`)).map(e=>{let t=q(e),n=t?.match(/\b\d{3}\b/),r=Array.from(e.querySelectorAll(`[data-testid="RDMD"]`));return{statusCode:n?.[0]??null,description:q(r[0]??null)??t}}).filter(e=>e.statusCode!==null)},I=e=>Array.from(e.querySelectorAll(`.CodeSnippet`)).map(e=>L(e)).filter(e=>!!e),L=e=>{if(!e)return null;let t=Array.from(e.querySelectorAll(`.CodeMirror-code pre.CodeMirror-line`));return t.length===0?null:t.map(e=>e.textContent?.replace(/\u00a0/g,` `)??``).join(`
|
|
2
|
-
`).trimEnd()||null},
|
|
3
|
-
`;)i+=1;i<e.length&&(t+=e[i]);continue}t+=a}return t},
|
|
1
|
+
import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"axios";import{Logger as r}from"@h3ravel/shared";import i from"node:path";import a,{readFile as o}from"node:fs/promises";import{Command as s,Kernel as c}from"@h3ravel/musket";import l from"prettier";import{pathToFileURL as u}from"node:url";import{existsSync as d,readdirSync as f}from"node:fs";import{fileURLToPath as p}from"url";const m=[`SIGINT`,`SIGTERM`,`SIGTSTP`],h=[`axios`,`happy-dom`,`jsdom`,`puppeteer`],g={outputFormat:`pretty`,outputShape:`raw`,requestTimeout:5e4,maxRedirects:5,userAgent:`Mozilla/5.0 (X11; Linux x64) AppleWebKit/537.36 (KHTML, like Gecko) OpenApiExtractor/1.0.0`,retryCount:3,retryDelay:1e3,browser:`puppeteer`,happyDom:{enableJavaScriptEvaluation:!0,suppressInsecureJavaScriptEnvironmentWarning:!0},puppeteer:{headless:!0,args:[`--no-sandbox`,`--disable-setuid-sandbox`]}};let _=g,v=!1,y=!1;const b=new Set,x=()=>globalThis.__oapieBrowserSession,S=()=>{!x()&&b.size===0&&w()},C=()=>{if(!y){for(let e of m)process.on(e,E);y=!0}},w=()=>{if(y){for(let e of m)process.off(e,E);y=!1}},T=e=>(C(),b.add(e),()=>{b.delete(e),S()}),ee=async()=>{let e=Array.from(b).reverse();b.clear();for(let t of e)await t();await O()},E=async e=>{if(!v){v=!0,w();try{await ee()}catch(e){console.error(`Failed to close active browser resources:`,e)}finally{v=!1}if(e===`SIGTSTP`){process.kill(process.pid,e);return}process.exit(e===`SIGINT`?130:143)}},D=async(e=_)=>{let t=x();if(t?.browser===e.browser)return t;t&&await O();let n={browser:e.browser,closers:[]};return e.browser===`puppeteer`&&(n.puppeteerBrowser=await(await import(`puppeteer`)).launch({headless:e.puppeteer?.headless??!0,args:e.puppeteer?.args??[`--no-sandbox`,`--disable-setuid-sandbox`]})),C(),globalThis.__oapieBrowserSession=n,n},O=async()=>{let e=x();if(e){globalThis.__oapieBrowserSession=void 0;for(let t of e.closers.reverse())await t();e.puppeteerBrowser&&await e.puppeteerBrowser.close(),S()}},k=(e,t)=>{let n=x();return!n||n.browser!==e?!1:(n.closers.push(t),!0)},A=e=>{let t={...g,...e,happyDom:{...g.happyDom,...e.happyDom}};return _=t,t},j=e=>h.includes(e),te=async(r,i=_,a=!1)=>{let{data:o}=i.browser===`puppeteer`?{data:``}:await n.get(r,{timeout:i.requestTimeout,maxRedirects:i.maxRedirects,headers:{"User-Agent":i.userAgent,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8`}});if(i.browser===`axios`)return o;if(i.browser===`happy-dom`){let e=new t({url:r,innerWidth:1024,innerHeight:768,settings:i.happyDom}),n=T(()=>e.happyDOM.close());try{e.document.write(o),await e.happyDOM.waitUntilComplete();let t=e.document.documentElement.outerHTML;if(!t)throw await e.happyDOM.close(),Error(`Unable to extract HTML from remote source: ${r}`);return k(`happy-dom`,()=>e.happyDOM.close())||await e.happyDOM.close(),t}finally{n()}}else if(i.browser===`jsdom`){let t,n;try{({window:t}=new e(o,{url:r,contentType:`text/html`,runScripts:`dangerously`,includeNodeLocations:!0})),n=T(()=>t?.close());let i=t.document.documentElement.outerHTML;if(!i)throw Error(`Unable to extract HTML from remote source: ${r}`);let a=t;return k(`jsdom`,()=>a.close())||t.close(),t=void 0,i}finally{n?.(),t&&t.close()}}else if(i.browser===`puppeteer`){let e=x(),t=Math.max(i.requestTimeout,3e4),o=e?.browser===`puppeteer`?e.puppeteerBrowser:void 0,s=!1,c,l,u;try{o||(o=await(await import(`puppeteer`)).launch({headless:i.puppeteer?.headless??!0,args:i.puppeteer?.args??[`--no-sandbox`,`--disable-setuid-sandbox`]}),s=!0,l=T(async()=>{o?.connected&&await o.close()})),c=await o.newPage(),u=T(async()=>{c&&!c.isClosed()&&await c.close()}),await c.setUserAgent({userAgent:i.userAgent}),await c.setRequestInterception(!0),c.on(`request`,e=>{let t=e.resourceType();if([`image`,`font`,`media`,`stylesheet`].includes(t))return void e.abort();e.continue()});try{await c.goto(r,{waitUntil:`domcontentloaded`,timeout:t})}catch(e){if(!c||!await se(c))throw e}let e=await ie(c,t,a);if(!e)throw Error(`Unable to extract HTML from remote source: ${r}`);if(!e.includes(`id="ssr-props"`)){let{data:t}=await n.get(r,{timeout:i.requestTimeout,maxRedirects:i.maxRedirects,headers:{"User-Agent":i.userAgent,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8`}});e=ce(e,t)}return e}finally{u?.(),c&&!c.isClosed()&&await c.close(),l?.(),s&&o&&await o.close()}}else throw Error(`Unsupported browser specified in configuration: ${_.browser}`)},ne=async(e,t,n=!1)=>{try{n&&await e.waitForSelector(`.hub-sidebar-content .rm-Sidebar-link`),await e.waitForSelector(`[data-testid="http-method"], article#content, script#ssr-props`,{timeout:t})}catch{}},re=async(e,t)=>{try{await e.waitForFunction(()=>{let e=!!document.querySelector(`[data-testid="http-method"]`),t=!!document.querySelector(`form[name="Parameters"]`),n=!!document.querySelector(`.rm-PlaygroundRequest`),r=!!document.querySelector(`.rm-PlaygroundResponse`),i=!!document.querySelector(`script#ssr-props`);return e?t||n||r||i:!1},{timeout:t})}catch{}try{await e.waitForSelector(`.rm-PlaygroundRequest, .rm-PlaygroundResponse, form[name="Parameters"], script#ssr-props`,{timeout:Math.min(t,5e3)})}catch{}try{await e.waitForNetworkIdle?.({idleTime:500,timeout:Math.min(t,5e3)})}catch{}},ie=async(e,t,n=!1,r=3)=>{let i;for(let a=1;a<=r;a+=1)try{return await ne(e,t,n),await re(e,t),await e.content()}catch(n){if(i=n,!oe(n)||a===r)throw n;await ae(e,t)}throw i instanceof Error?i:Error(`Unable to extract stable HTML from remote source`)},ae=async(e,t)=>{try{await e.waitForFunction(()=>document.readyState===`interactive`||document.readyState===`complete`,{timeout:Math.min(t,5e3)})}catch{}try{await e.waitForNetworkIdle?.({idleTime:500,timeout:Math.min(t,5e3)})}catch{}},oe=e=>{if(!(e instanceof Error))return!1;let t=e.message.toLowerCase();return t.includes(`execution context was destroyed`)||t.includes(`cannot find context with specified id`)||t.includes(`most likely because of a navigation`)},se=async e=>!!await e.$(`[data-testid="http-method"], article#content, script#ssr-props`),ce=(e,t)=>{if(e.includes(`id="ssr-props"`))return e;let n=le(t);return n?e.includes(`</body>`)?e.replace(`</body>`,`${n}</body>`):e.includes(`</html>`)?e.replace(`</html>`,`${n}</html>`):`${e}${n}`:e},le=e=>e.match(/<script id="ssr-props"[^>]*>[\s\S]*?<\/script>/i)?.[0]??null;var M=class e{static parsePossiblyTruncated=t=>{let n=new e,r=t.trim();if(!/^(?:\{|\[)/.test(r))return null;try{return JSON.parse(r)}catch{let e=n.repairCommonJsonIssues(r);try{return JSON.parse(e)}catch{return null}}};repairCommonJsonIssues=e=>{let t=this.removeUnexpectedObjectTokens(e),n=this.insertMissingCommas(t);return`${n}${this.buildMissingJsonClosers(n)}`};removeUnexpectedObjectTokens=e=>e.replace(/([[{,]\s*)([A-Za-z_$][\w$-]*)(?=\s*"(?:\\.|[^"\\])*"\s*:)/g,`$1`);insertMissingCommas=e=>{let t=``,n=!1,r=!1,i=``;for(let a=0;a<e.length;a+=1){let o=e[a];if(n){if(t+=o,r){r=!1;continue}if(o===`\\`){r=!0;continue}o===`"`&&(n=!1,i=`"`);continue}if(o===`"`){let r=e.slice(a);/^"(?:\\.|[^"\\])*"\s*:/.test(r)&&this.shouldInsertCommaBeforeKey(i,t)&&(t+=`,`),t+=o,n=!0;continue}t+=o,/\s/.test(o)||(i=o)}return t};shouldInsertCommaBeforeKey=(e,t)=>{if(!e||![`"`,`}`,`]`,`e`,`l`,`0`,`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`].includes(e))return!1;let n=t.trimEnd();return!n.endsWith(`,`)&&!n.endsWith(`{`)};buildMissingJsonClosers=e=>{let t=[],n=!1,r=!1;for(let i of e){if(r){r=!1;continue}if(i===`\\`){r=!0;continue}if(i===`"`){n=!n;continue}if(!n){if(i===`{`){t.push(`}`);continue}if(i===`[`){t.push(`]`);continue}(i===`}`||i===`]`)&&t[t.length-1]===i&&t.pop()}}return t.reverse().join(``)}};const N=e=>typeof e==`object`&&!!e&&!Array.isArray(e),P=e=>{let n=new t;n.document.write(e);let{document:r}=n,i=r.querySelector(`article#content`)??r.body,a=i.querySelector(`form[name="Parameters"]`),o=r.querySelector(`.rm-PlaygroundRequest`),s=r.querySelector(`.rm-PlaygroundResponse`),c=_e(o),l=Pe(c[0]??null),u=ve(s);return ue({method:q(i.querySelector(`[data-testid="http-method"]`))?.toUpperCase()??null,url:q(i.querySelector(`[data-testid="serverurl"]`)),description:ge(i),sidebarLinks:ye(r.querySelector(`.rm-Sidebar.hub-sidebar-content`)),requestParams:be(a),requestCodeSnippets:c,requestExample:c[0]?.body??null,requestExampleNormalized:l,responseSchemas:xe(i),responseBodies:u,responseExample:u[0]?.body??null,responseExampleRaw:u[0]?.rawBody??null},de(r))},ue=(e,t)=>t?{method:e.method??t.method,url:e.url??t.url,description:e.description??t.description,sidebarLinks:e.sidebarLinks.length>0?e.sidebarLinks:t.sidebarLinks,requestParams:e.requestParams.length>0?e.requestParams:t.requestParams,requestCodeSnippets:e.requestCodeSnippets.length>0?e.requestCodeSnippets:t.requestCodeSnippets,requestExample:e.requestExample??t.requestExample,requestExampleNormalized:e.requestExampleNormalized??t.requestExampleNormalized,responseSchemas:e.responseSchemas.length>0?e.responseSchemas:t.responseSchemas,responseBodies:e.responseBodies.length>0?e.responseBodies:t.responseBodies,responseExample:e.responseExample??t.responseExample,responseExampleRaw:e.responseExampleRaw??t.responseExampleRaw}:e,de=e=>{let t=e.querySelector(`script#ssr-props`)?.textContent?.trim();if(!t)return null;let n;try{n=JSON.parse(t)}catch{return null}let r=N(n)&&N(n.document)?n.document.api:null;if(!N(r))return null;let i=typeof r.method==`string`?r.method.toUpperCase():null,a=typeof r.path==`string`?r.path:null,o=N(r.schema)?r.schema:null,s=pe(o,a,i?.toLowerCase()??null),c=Array.isArray(o?.servers)&&N(o.servers[0])&&typeof o.servers[0].url==`string`?o.servers[0].url:null;if(!s&&!i&&!a)return null;let l=L(s?.responses??{});return{method:i,url:me(c,a),description:s?.description??null,sidebarLinks:[],requestParams:[...fe(s?.parameters),...he(s?.requestBody)],requestCodeSnippets:[],requestExample:null,requestExampleNormalized:null,responseSchemas:I(s?.responses??{}),responseBodies:l,responseExample:l[0]?.body??null,responseExampleRaw:l[0]?.rawBody??null}},fe=e=>e?e.map(e=>({name:e.name,in:e.in,path:[e.name],type:e.schema?.type??null,required:e.required??!1,defaultValue:e.schema?.default==null?e.example==null?null:String(e.example):String(e.schema.default),description:e.description??e.schema?.description??null})):[],pe=(e,t,n)=>{if(!e||!t||!n||!N(e.paths))return null;let r=e.paths[t];return!N(r)||!N(r[n])?null:r[n]},me=(e,t)=>!e||!t?null:`${e.replace(/\/$/,``)}${t.startsWith(`/`)?t:`/${t}`}`,he=e=>{let t=e?.content?.[`application/json`]?.schema;return t?F(t):[]},F=(e,t=[])=>{if(!e.properties)return[];let n=[];for(let[r,i]of Object.entries(e.properties)){let a=[...t,r],o=e.required?.includes(r)??!1;if(i.type===`object`&&i.properties){n.push(...F(i,a).map(e=>({...e,required:e.path.length===a.length+1?o&&e.required:e.required})));continue}n.push({name:r,in:`body`,path:a,type:i.type??null,required:o,defaultValue:i.default==null?null:String(i.default),description:i.description??null})}return n},I=e=>Object.entries(e).map(([e,t])=>({statusCode:e,description:t.description??e})),L=e=>{let t=[];for(let[n,r]of Object.entries(e))for(let[e,i]of Object.entries(r.content??{})){let a=R(i);if(a==null)continue;let o=typeof a==`string`?a:JSON.stringify(a,null,2),s=H(o,e);t.push({format:s.format,contentType:e,statusCode:n,label:r.description??n,body:s.body,rawBody:o})}return t},R=e=>{if(e.example!==void 0)return e.example;let t=e.examples;if(t&&N(t)){for(let e of Object.values(t))if(N(e)&&`value`in e)return e.value??null}return e.schema?.example===void 0?null:e.schema.example},ge=e=>{let t=e.querySelector(`header`);if(!t)return null;let n=Array.from(t.querySelectorAll(`[data-testid="RDMD"]`));return q(n[n.length-1]??null)},_e=e=>{if(!e)return[];let t=Ce(e);return z(e).map(e=>({label:t,body:e}))},ve=e=>{if(!e)return[];let t=z(e),n=Ee(e),r=De(e);return t.map((e,t)=>{let i=r[t]??r[0]??null,a=n[t]??n[0]??null,o=H(e,a);return{format:o.format,contentType:a,statusCode:i?.match(/\b\d{3}\b/)?.[0]??null,label:i,body:o.body,rawBody:e}})},ye=e=>e?Array.from(e.querySelectorAll(`.rm-Sidebar-link`)).map(e=>{let t=e.closest(`.rm-Sidebar-section`),n=q(e.querySelector(`[data-testid="http-method"]`))?.toUpperCase()??null;return{section:q(t?.querySelector(`.rm-Sidebar-heading`)??null),label:Te(e,n),href:e.getAttribute(`href`),method:n,active:e.classList.contains(`active`)||e.getAttribute(`aria-current`)===`page`,subpage:e.classList.contains(`subpage`)}}).filter(e=>e.label.length>0):[],be=e=>e?Array.from(e.querySelectorAll(`label`)).map(t=>{let n=q(t),r=Oe(t,e),i=ke(r,t);return{name:n??``,in:Ae(r,t,e),path:je(t,n),type:Me(r,i),required:V(r,i),defaultValue:we(i),description:Ne(r)}}).filter(e=>e.name.length>0):[],xe=e=>{let t=e.querySelector(`#response-schemas`)?.nextElementSibling;return!t||!t.classList.contains(`rm-APIResponseSchemaPicker`)?[]:Array.from(t.querySelectorAll(`button`)).map(e=>{let t=q(e),n=t?.match(/\b\d{3}\b/),r=Array.from(e.querySelectorAll(`[data-testid="RDMD"]`));return{statusCode:n?.[0]??null,description:q(r[0]??null)??t}}).filter(e=>e.statusCode!==null)},z=e=>Array.from(e.querySelectorAll(`.CodeSnippet`)).map(e=>Se(e)).filter(e=>!!e),Se=e=>{if(!e)return null;let t=Array.from(e.querySelectorAll(`.CodeMirror-code pre.CodeMirror-line`));return t.length===0?null:t.map(e=>e.textContent?.replace(/\u00a0/g,` `)??``).join(`
|
|
2
|
+
`).trimEnd()||null},Ce=e=>{let t=e.querySelector(`header`);if(!t)return null;let n=Array.from(t.querySelectorAll(`button`)).map(e=>Y(e)).filter(e=>!!e);return n.find(e=>e.toLowerCase()!==`examples`)??n[0]??null},we=e=>e&&e.getAttribute(`value`)?.trim()||null,Te=(e,t)=>J(e.querySelectorAll(`span`)).filter(e=>!t||e.toUpperCase()!==t).sort((e,t)=>t.length-e.length)[0]??q(e)??``,Ee=e=>J(e.querySelectorAll(`div`)).filter(e=>/^[\w.+-]+\/[\w.+-]+$/i.test(e)),De=e=>Array.from(e.querySelectorAll(`button, [role="button"]`)).map(e=>Y(e)).filter(e=>!!e).filter(e=>/\b\d{3}\b/.test(e)),Oe=(e,t)=>{let n=e.getAttribute(`for`),r=e;for(;r;){let e=n?r.querySelector(`#${K(n)}`):null,i=r.querySelector(`input, textarea, select`);if((e||i)&&r!==t)return r;r=r.parentElement}return e},ke=(e,t)=>{let n=t.getAttribute(`for`),r=n?e.querySelector(`#${K(n)}`):null,i=e.querySelector(`input, textarea, select`);return r??i},Ae=(e,t,n)=>{let r=B(`${t.getAttribute(`for`)?.toLowerCase()??``} ${e.closest(`[id]`)?.getAttribute(`id`)?.toLowerCase()??``}`);if(r)return r;let i=e;for(;i&&i!==n;){let e=i.previousElementSibling;for(;e;){if(e.tagName===`HEADER`){let t=B(q(e)??``);if(t)return t}e=e.previousElementSibling}i=i.parentElement}return null},B=e=>{let t=e.trim().toLowerCase();return t.includes(`query params`)||t.includes(`query-`)?`query`:t.includes(`headers`)||t.includes(`header-`)?`header`:t.includes(`body params`)||t.includes(`request body`)||t.includes(`body-`)?`body`:t.includes(`path params`)||t.includes(`path-`)?`path`:t.includes(`cookie`)||t.includes(`cookie-`)?`cookie`:null},je=(e,t)=>{let n=e.getAttribute(`for`)??``,r=n.includes(`_`)?n.slice(n.indexOf(`_`)+1):``;return r.includes(`.`)?r.split(`.`).map(e=>e.trim()).filter(e=>e.length>0):t?[t]:[]},Me=(e,t)=>{let n=t?.getAttribute(`type`)?.toLowerCase();if(n===`text`)return`string`;if(n)return n;let r=Array.from(e.querySelectorAll(`[class]`)).find(e=>(e.getAttribute(`class`)??``).split(/\s+/).some(e=>e.startsWith(`field-`)))?.getAttribute(`class`)?.split(/\s+/).find(e=>e.startsWith(`field-`));return r?r.replace(/^field-/,``):null},V=(e,t)=>t?.hasAttribute(`required`)?!0:J(e.querySelectorAll(`*`)).some(e=>e.toLowerCase()===`required`),Ne=e=>q(e.querySelector(`[id$="__description"]`)||(Array.from(e.querySelectorAll(`[data-testid="RDMD"]`))[0]??null)),H=(e,t)=>{let n=e.trim();if(t?.toLowerCase().includes(`json`)||/^(?:\{|\[)/.test(n)){let t=M.parsePossiblyTruncated(n);if(t!==null)return{format:`json`,body:t};let r=G(n);return r===null?{format:`text`,body:e}:{format:`json`,body:r}}return{format:`text`,body:e}},Pe=e=>e?Fe(e)??Ie(e)??{sourceLabel:e.label,method:null,url:null,headers:{},bodyFormat:null,body:null,rawBody:null}:null,Fe=e=>{if(!e.body.startsWith(`curl `))return null;let t=e.body.match(/--request\s+([A-Z]+)/)?.[1]??null,n=e.body.match(/--url\s+(\S+)/)?.[1]??null,r=Object.fromEntries(Array.from(e.body.matchAll(/--header\s+'([^:]+):\s*([^']+)'/g)).map(e=>[e[1].trim(),e[2].trim()])),i=e.body.match(/--data\s+'([\s\S]*)'$/)?.[1]?.trim()??null,a=i?H(i,r[`content-type`]??r[`Content-Type`]??null):null;return{sourceLabel:e.label,method:t,url:n,headers:r,bodyFormat:a?.format??null,body:a?.body??null,rawBody:i}},Ie=e=>{let t=e.body.match(/fetch\(\s*(["'])(.*?)\1\s*,\s*\{([\s\S]*)\}\s*\)/);if(!t)return null;let[,,n,r]=t,i=U(r,`method`)?.toUpperCase()??null,a=Le(r),o=Re(r),s=a[`content-type`]??a[`Content-Type`]??null,c=o?ze(o,s):null;return{sourceLabel:e.label,method:i,url:n,headers:a,bodyFormat:c?.format??null,body:c?.body??null,rawBody:o}},Le=e=>{let t=U(e,`headers`);if(!t)return{};let n=G(t);return N(n)?Object.fromEntries(Object.entries(n).map(([e,t])=>[e,String(t)])):{}},Re=e=>U(e,`body`),ze=(e,t)=>{let n=G(e);return n!==null&&(t?.toLowerCase().includes(`json`)||/^[[{]/.test(e.trim()))?{format:`json`,body:n}:H(e,t)},U=(e,t)=>{let n=e.match(RegExp(`\\b${t}\\s*:`,`m`));if(!n||n.index===void 0)return null;let r=n.index+n[0].length;for(;/\s/.test(e[r]??``);)r+=1;if(e.startsWith(`JSON.stringify`,r)){let t=e.indexOf(`(`,r);return t===-1?null:W(e,t,`(`,`)`)?.slice(1,-1).trim()??null}if(e[r]===`{`||e[r]===`[`){let t=e[r]===`{`?`}`:`]`;return W(e,r,e[r],t)?.trim()??null}return e[r]===`"`||e[r]===`'`?Be(e,r):(e.slice(r).match(/^([^,\n]+)/)?.[1])?.trim()??null},W=(e,t,n,r)=>{let i=0,a=null,o=!1;for(let s=t;s<e.length;s+=1){let c=e[s];if(a){if(o){o=!1;continue}if(c===`\\`){o=!0;continue}c===a&&(a=null);continue}if(c===`"`||c===`'`){a=c;continue}if(c===n){i+=1;continue}if(c===r&&(--i,i===0))return e.slice(t,s+1)}return null},Be=(e,t)=>{let n=e[t],r=``,i=!1;for(let a=t+1;a<e.length;a+=1){let t=e[a];if(i){r+=t,i=!1;continue}if(t===`\\`){i=!0;continue}if(t===n)return r;r+=t}return null},G=e=>{let t=He(e).trim();if(!/^[[{]/.test(t))return null;let n=Ve(t.replace(/([{,]\s*)([A-Za-z_$][\w$-]*)(\s*:)/g,`$1"$2"$3`).replace(/,\s*([}\]])/g,`$1`));try{return JSON.parse(n)}catch{return null}},Ve=e=>{let t=``,n=!1,r=!1,i=!1,a=``;for(let o of e){if(r){if(i){a+=o,i=!1;continue}if(o===`\\`){i=!0;continue}if(o===`'`){t+=JSON.stringify(a),a=``,r=!1;continue}a+=o;continue}if(n){if(t+=o,i){i=!1;continue}if(o===`\\`){i=!0;continue}o===`"`&&(n=!1);continue}if(o===`'`){r=!0,a=``;continue}o===`"`&&(n=!0),t+=o}return t},He=e=>{let t=Ue(e);return t=We(t),t=Ge(t),t},Ue=e=>{let t=``,n=!1,r=!1;for(let i=0;i<e.length;i+=1){let a=e[i],o=e[i+1];if(n){if(t+=a,r){r=!1;continue}if(a===`\\`){r=!0;continue}a===`"`&&(n=!1);continue}if(a===`"`){n=!0,t+=a;continue}if(a===`/`&&o===`/`){for(;i<e.length&&e[i]!==`
|
|
3
|
+
`;)i+=1;i<e.length&&(t+=e[i]);continue}t+=a}return t},We=e=>e.replace(/([[{]\s*)([A-Za-z_$][\w$-]*)(\s+)(?=")/g,`$1`),Ge=e=>{let t=e.trimStart();return!t.startsWith(`{`)||/"data"\s*:\s*\[/.test(t)||!/^\{\s*"[^"]+"\s*:/.test(t)||!/\]\s*,\s*"meta"\s*:/.test(t)?e:`${e.slice(0,e.indexOf(`{`))}{"data": [{${t.slice(1)}`},K=e=>e.replace(/([#.:[\],=])/g,`\\$1`),q=e=>e?.textContent?.replace(/\s+/g,` `).trim()||null,J=e=>Array.from(e).map(e=>q(e)).filter(e=>!!e),Y=e=>J(e.querySelectorAll(`span, div, code`)).filter(e=>e.trim().length>0).sort((e,t)=>t.length-e.length)[0]??q(e),Ke=(e,t)=>{let n=new URL(t),r=e.sidebarLinks.map(e=>e.href).filter(e=>!!e).map(e=>new URL(e,n).toString()).filter(e=>/^https?:\/\//i.test(e));return Array.from(new Set(r))};var qe=class{config;constructor(e={}){this.config=A(e)}getConfig(e={}){return{...this.config,...e}}configure(e){return this.config=A({...this.config,...e}),this.config}async crawlReadmeOperations(e,t,n){let i=this.resolveCrawlBaseUrl(e,n);if(!i)throw Error(`Crawl mode requires a remote source URL or --base-url when using a local file`);let a=!!x();a||await D(this.config);try{let e=Ke(t,i),n=new URL(i).toString(),a=this.attachSourceUrl(n,t),o=e.filter(e=>e!==n);return{rootSource:n,discoveredUrls:e,operations:[a,...(await Promise.all(o.map(async e=>{let t=Date.now(),n=P(await this.loadHtmlSource(e)),a=Date.now()-t;return n.method?(r.twoColumnDetail(r.log([[`Crawled`,`green`],[`${a/1e3}s`,`gray`]],` `,!1),e.replace(i,``)),this.attachSourceUrl(e,n)):null}))).filter(e=>e!==null)]}}finally{a||await O()}}attachSourceUrl(e,t){return{sourceUrl:e,...t}}resolveCrawlBaseUrl(e,t){return t?new URL(t).toString():/^https?:\/\//i.test(e)?e:null}async loadHtmlSource(e,t){if(!e)throw Error(`A source path or URL is required`);return/^https?:\/\//i.test(e)?te(e,this.config,t):o(i.resolve(process.cwd(),e),`utf8`)}},Je=class{renderDeclaration(e){switch(e.kind){case`interface`:return this.renderInterface(e);case`interface-alias`:return`export interface ${e.name} extends ${e.target} {}`;case`type-alias`:return`export type ${e.name} = ${e.target}`;case`shape-alias`:return`export type ${e.name} = ${this.renderShape(e.shape)}`}}renderOpenApiDocumentDefinitions(e,t,n){return[`export interface OpenApiInfo {
|
|
4
4
|
title: string
|
|
5
5
|
version: string
|
|
6
6
|
}`,`export interface OpenApiSchemaDefinition {
|
|
@@ -136,7 +136,7 @@ import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"a
|
|
|
136
136
|
`)}renderValue(e){return this.renderLiteral(e,0)}renderOpenApiDocumentValue(e){return this.renderLiteral(this.normalizeOpenApiDocument(e),0)}toCamelCase(e){let t=this.sanitizeTypeName(e);return t.charAt(0).toLowerCase()+t.slice(1)}renderInterface(e){let t=e.properties.map(e=>` ${this.formatPropertyKey(e.key)}${e.optional?`?`:``}: ${this.renderShape(e.shape)}`).join(`
|
|
137
137
|
`);return`export interface ${e.name} {\n${t}\n}`}renderShape(e){switch(e.kind){case`primitive`:return e.type;case`array`:return`${this.wrapUnion(this.renderShape(e.item))}[]`;case`union`:return e.types.map(e=>this.renderShape(e)).join(` | `);case`object`:return this.inlineObjectShape(e)}}inlineObjectShape(e){return e.properties.length===0?`Record<string, never>`:`{ ${e.properties.map(e=>`${this.formatPropertyKey(e.key)}${e.optional?`?`:``}: ${this.renderShape(e.shape)}`).join(`; `)} }`}wrapUnion(e){return e.includes(` | `)?`(${e})`:e}derivePathTypeName(e){let t=e.split(`/`).map(e=>e.trim()).filter(Boolean).filter(e=>!/^v\d+$/i.test(e)).map(e=>this.isPathParam(e)?`by ${this.stripPathParam(e)}`:e);return`${this.sanitizeTypeName(t.join(` `))}Path`}deriveOperationInterfaceName(e,t){return`${this.derivePathTypeName(e)}${this.sanitizeTypeName(t)}Operation`}sanitizeTypeName(e){let t=e.replace(/[^A-Za-z0-9]+/g,` `).trim();if(!t)return`GeneratedEntity`;let n=t.split(/\s+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_$]/.test(n)?n:`Type${n}`}formatPropertyKey(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:`'${this.escapeStringLiteral(e)}'`}renderSdkMethodSignature(e){let t=[];return e.pathParams.length>0&&t.push(`(params: ${e.paramsType}`),e.queryParams.length>0&&t.push(`${t.length===0?`(`:`, `}query: ${e.queryType}`),e.hasBody&&t.push(`${t.length===0?`(`:`, `}body${e.bodyRequired?``:`?`}: ${e.inputType}`),e.headerParams.length>0&&t.push(`${t.length===0?`(`:`, `}headers?: ${e.headerType}`),t.length===0?`(): Promise<${e.responseType}>`:`${t.join(``)}): Promise<${e.responseType}>`}renderLiteral(e,t){if(e===null)return`null`;if(typeof e==`string`)return`'${this.escapeStringLiteral(e)}'`;if(typeof e==`number`||typeof e==`boolean`)return String(e);if(Array.isArray(e)){if(e.length===0)return`[]`;let n=this.indent(t+1),r=this.indent(t);return`[\n${e.map(e=>`${n}${this.renderLiteral(e,t+1)}`).join(`,
|
|
138
138
|
`)}\n${r}]`}if(typeof e==`object`){let n=Object.entries(e);if(n.length===0)return`{}`;let r=this.indent(t+1),i=this.indent(t);return`{\n${n.map(([e,n])=>`${r}${this.formatPropertyKey(e)}: ${this.renderLiteral(n,t+1)}`).join(`,
|
|
139
|
-
`)}\n${i}}`}return`undefined`}normalizeOpenApiDocument(e){return this.normalizeObject(e,(e,t,n)=>{if(e===`example`&&n&&typeof n==`object`&&!Array.isArray(n)){let e=n;if(e.schema&&this.isPlainObject(e.schema))return this.normalizeExample(t,e.schema);if(this.isSchemaLike(e))return this.normalizeExample(t,e)}return t})}normalizeObject(e,t){if(Array.isArray(e))return e.map(e=>this.normalizeObject(e,t)).filter(e=>e!==void 0);if(!this.isPlainObject(e))return e;let n={};for(let[r,i]of Object.entries(e)){let a=t(r,i,e),o=this.normalizeObject(a,t);o!==void 0&&(n[r]=o)}return n}normalizeExample(e,t){if(e===void 0)return;let n=typeof t.type==`string`?t.type:void 0;if(n===`string`)return typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`?String(e):void 0;if(n===`number`||n===`integer`)return typeof e==`number`?e:typeof e==`string`&&e.trim()!==``&&Number.isFinite(Number(e))?Number(e):void 0;if(n===`boolean`)return typeof e==`boolean`?e:e===`true`?!0:e===`false`?!1:void 0;if(n===`array`){if(!Array.isArray(e))return;let n=this.isPlainObject(t.items)?t.items:void 0;return n?e.map(e=>this.normalizeExample(e,n)).filter(e=>e!==void 0):e}if(n===`object`){if(!this.isPlainObject(e))return;let n=this.isPlainObject(t.properties)?t.properties:{},r=Array.isArray(t.required)?t.required.filter(e=>typeof e==`string`):[],i={};for(let[t,r]of Object.entries(e)){let e=n[t],a=e?this.normalizeExample(r,e):r;a!==void 0&&(i[t]=a)}for(let e of r){if(e in i)continue;let t=n[e];if(!t)return;let r=t.default===void 0?t.example===void 0?void 0:this.normalizeExample(t.example,t):this.normalizeExample(t.default,t);if(r===void 0)return;i[e]=r}return i}return e}isPlainObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}isSchemaLike(e){return`type`in e||`properties`in e||`items`in e||`required`in e||`default`in e}indent(e){return` `.repeat(e)}escapeStringLiteral(e){return e.replace(/\\/g,String.raw`\\`).replace(/'/g,String.raw`\'`).replace(/\r/g,String.raw`\r`).replace(/\n/g,String.raw`\n`).replace(/\t/g,String.raw`\t`).replace(/\f/g,String.raw`\f`).replace(/\x08/g,String.raw`\b`).replace(/\u2028/g,String.raw`\u2028`).replace(/\u2029/g,String.raw`\u2029`)}isPathParam(e){return e.startsWith(`{`)&&e.endsWith(`}`)||/^:[A-Za-z0-9_]+$/.test(e)}stripPathParam(e){return e.replace(/^\{/,``).replace(/\}$/,``).replace(/^:/,``)}},ze=class e{static contextualTailSegments=new Set([`history`,`status`,`detail`,`details`]);static nestedContextSegments=new Set([`account`,`accounts`,`transaction`,`transactions`,`wallet`,`wallets`,`virtual-account`,`virtual-accounts`,`history`]);static roleSuffixes=[`Input`,`Query`,`Header`,`Params`];sanitizeTypeName(e){let t=e.replace(/[^A-Za-z0-9]+/g,` `).trim();if(!t)return`GeneratedEntity`;let n=t.split(/\s+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_$]/.test(n)?n:`Type${n}`}isPathParam(e){return e.startsWith(`{`)&&e.endsWith(`}`)||/^:[A-Za-z0-9_]+$/.test(e)}stripPathParam(e){return e.replace(/^\{/,``).replace(/\}$/,``).replace(/^:/,``)}singularize(e){return/ies$/i.test(e)?`${e.slice(0,-3)}y`:/(sses|shes|ches|xes|zes)$/i.test(e)?e.slice(0,-2):e.endsWith(`s`)&&!e.endsWith(`ss`)&&e.length>1?e.slice(0,-1):e}pluralize(e){return/y$/i.test(e)?`${e.slice(0,-1)}ies`:/s$/i.test(e)?e:`${e}s`}toCamelCase(e){let t=this.sanitizeTypeName(e);return t.charAt(0).toLowerCase()+t.slice(1)}deriveOperationNaming(t){let n=this.getNormalizedPathSegments(t),r=n.filter(e=>!this.isPathParam(e)).map(e=>this.singularize(e)),i=n.filter(e=>this.isPathParam(e)).map(e=>this.singularize(this.stripPathParam(e))),a=r[r.length-1]??`resource`,o=r[r.length-2]??null,s=n.slice(0,-1).some(e=>this.isPathParam(e)),c=!!(o&&(e.contextualTailSegments.has(a.toLowerCase())||s&&e.nestedContextSegments.has(a.toLowerCase())));return{baseName:this.sanitizeTypeName(c?`${o} ${a}`:a),collisionSuffix:i.length>0?`By ${i.map(e=>this.sanitizeTypeName(e)).join(` And `)}`:o&&!c?this.sanitizeTypeName(o):``}}fallbackCollisionSuffix(e,t,n){let r=this.getNormalizedPathSegments(t),i=r.filter(e=>!this.isPathParam(e)),a=i[i.length-1]??``,o=r.some(e=>this.isPathParam(e));return e===`get`&&!o&&/s$/i.test(a)?`List`:e===`post`&&!o?`Create`:(e===`put`||e===`patch`)&&o?`Update`:e===`delete`?`Delete`:`${this.sanitizeTypeName(e)}${n}`}insertCollisionSuffix(t,n){if(!n)return t;for(let r of e.roleSuffixes)if(t.endsWith(r)&&t.length>r.length)return`${t.slice(0,-r.length)}${n}${r}`;return`${t}${n}`}deriveSdkGroupNamesBySignature(e,t){let n=new Map;for(let t of Object.keys(e.paths)){let e=this.getStaticPathSignature(t);n.has(e)||n.set(e,t)}let r=Array.from(n.entries()).map(([e,n])=>({signature:e,staticSegments:e.split(`/`).filter(Boolean),candidates:this.buildSdkGroupNameCandidates(n,t)})).sort((e,t)=>e.staticSegments.length-t.staticSegments.length||e.signature.localeCompare(t.signature)),i=new Map,a=new Set;for(let e of r){let t=e.candidates.find(e=>!a.has(e))??this.createUniqueSdkGroupName(e.candidates[e.candidates.length-1]??`Resource`,a);a.add(t),i.set(e.signature,t)}return i}getStaticPathSegments(e){return this.getNormalizedPathSegments(e).filter(e=>!this.isPathParam(e)).map(e=>this.singularize(e))}getStaticPathSignature(e){return this.getStaticPathSegments(e).join(`/`)}getNormalizedPathSegments(e){return e.split(`/`).map(e=>e.trim()).filter(Boolean).filter(e=>!/^v\d+$/i.test(e))}deriveSdkMethodName(e,t,n,r){if(r===`operation-id`&&n.operationId)return this.toCamelCase(this.sanitizeTypeName(n.operationId));let i=this.getNormalizedPathSegments(t).some(e=>this.isPathParam(e));return e===`get`?this.endsWithPluralStaticSegment(t)?`list`:i?`get`:`list`:e===`post`?`create`:e===`patch`||e===`put`?`update`:e===`delete`?`delete`:this.toCamelCase(this.sanitizeTypeName(e))}ensureUniqueSdkMethodNames(e){let t=new Map;return e.map(e=>{let n=t.get(e.methodName)??0;if(t.set(e.methodName,n+1),n===0)return e;let r=this.sanitizeTypeName(this.fallbackCollisionSuffix(e.method.toLowerCase(),e.path,`Operation`));return{...e,methodName:`${e.methodName}${r}`}})}createSdkParameterManifest(e,t,n){return[...(e??[]).filter(e=>e.in===t).sort((e,t)=>e.name.localeCompare(t.name)),...this.getInferredPathParameters(n,t,(e??[]).filter(e=>e.in===t))].sort((e,t)=>e.name.localeCompare(t.name)).map(e=>({name:e.name,accessor:this.toParameterAccessor(e.name),in:t,required:e.required??!1,description:e.description}))}getInferredPathParameters(e,t,n){if(t!==`path`||!e)return[];let r=new Set(n.map(e=>e.name));return this.getNormalizedPathSegments(e).filter(e=>this.isPathParam(e)).map(e=>this.stripPathParam(e)).filter(e=>!r.has(e)).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}))}buildSdkGroupNameCandidates(e,t){let n=this.getNormalizedPathSegments(e),r=n.filter(e=>!this.isPathParam(e)),i=r.map(e=>this.singularize(e)),a=this.deriveOperationNaming(e).baseName,o=this.getPreferredSdkGroupName(n,r,i),s=i.map((e,t,n)=>this.sanitizeTypeName(n.slice(t).join(` `))).reverse();return t===`scoped`?Array.from(new Set([o??``,this.sanitizeTypeName(i.join(` `)),...s,a].filter(Boolean))):Array.from(new Set([o??``,a,...s].filter(Boolean)))}getPreferredSdkGroupName(e,t,n){let r=t[t.length-1],i=n[n.length-1],a=n[n.length-2],o=e.some(e=>this.isPathParam(e)),s=e.slice(0,-1).some(e=>this.isPathParam(e));return!r||!i||!a?null:t.length===2&&!o?this.sanitizeTypeName(`${i} ${a}`):s?this.singularize(r)===r?this.sanitizeTypeName(`${i} ${a}`):this.sanitizeTypeName(`${a} ${i}`):null}createUniqueSdkGroupName(e,t){let n=2,r=e;for(;t.has(r);)r=`${e}${n}`,n+=1;return r}endsWithPluralStaticSegment(e){let t=this.getNormalizedPathSegments(e).at(-1);return!t||this.isPathParam(t)?!1:this.singularize(t)!==t}toParameterAccessor(e){let t=e.replace(/[^A-Za-z0-9]+/g,` `).trim();if(!t)return`value`;let[n,...r]=t.split(/\s+/).filter(Boolean),i=[n.toLowerCase(),...r.map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase())].join(``);return/^[A-Za-z_$]/.test(i)?i:`value${i}`}},Be=class{constructor(e){this.naming=e}createContext(){return{declarations:[],declarationByName:new Map,nameBySignature:new Map,usedNames:new Set}}namespaceTopLevelShape(e,t){return e.kind===`object`?{...e,signature:`${t}:${e.signature}`}:e}inferShapeFromExample(e,t){if(e===null)return{kind:`primitive`,type:`null`};if(Array.isArray(e)){if(e.length===0)return{kind:`array`,item:{kind:`primitive`,type:`unknown`}};let n=this.dedupeShapes(e.map(e=>this.inferShapeFromExample(e,this.naming.singularize(t))));return{kind:`array`,item:n.length===1?n[0]:{kind:`union`,types:n}}}if(this.isRecord(e))return this.createObjectShape(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>({key:e,optional:!1,shape:this.inferShapeFromExample(t,e)})));switch(typeof e){case`string`:return{kind:`primitive`,type:`string`};case`number`:return{kind:`primitive`,type:`number`};case`boolean`:return{kind:`primitive`,type:`boolean`};default:return{kind:`primitive`,type:`unknown`}}}registerNamedShape(e,t,n,r){if(e.kind===`object`)return this.registerObjectShape(e,t,n,r,!0);let i=this.createUniqueTypeName(t,n,r),a={kind:`shape-alias`,name:i,shape:this.prepareNestedShape(e,t,n)};return n.declarations.push(a),n.declarationByName.set(i,a),i}registerObjectShape(e,t,n,r,i=!1){let a=n.nameBySignature.get(e.signature),o=this.findCompatibleObjectDeclaration(e,t,n);if(a){if(i&&a!==t&&!n.declarationByName.has(t)){let e=this.createUniqueTypeName(t,n,r);if(e!==a){let t={kind:`interface-alias`,name:e,target:a};n.declarations.push(t),n.declarationByName.set(e,t)}}return a}if(o){if(this.isObjectShapeAssignableTo(e,o.rawShape))return n.nameBySignature.set(e.signature,o.name),o.name;let t=this.mergeObjectShapes(o.rawShape,e);return o.rawShape=t,o.properties=t.properties.map(e=>({...e,shape:this.prepareNestedShape(e.shape,e.key,n)})),n.nameBySignature.set(e.signature,o.name),n.nameBySignature.set(t.signature,o.name),o.name}let s=this.createUniqueTypeName(t,n,r),c={kind:`interface`,name:s,baseName:this.naming.sanitizeTypeName(t),rawShape:e,properties:[]};return n.nameBySignature.set(e.signature,s),n.declarations.push(c),n.declarationByName.set(s,c),c.properties=e.properties.map(e=>({...e,shape:this.prepareNestedShape(e.shape,e.key,n)})),s}resolveSdkResponseType(e,t){let n=Object.entries(e).filter(([e])=>/^2\d\d$/.test(e)).sort(([e],[t])=>e.localeCompare(t))[0]?.[1];if(!n)return t;let r=this.getPreferredMediaType(n.content);if(!r)return t;let i=this.resolveResponsePayloadSchema(r.schema,r.example).schema??r.schema;return i&&this.resolveSchemaType(i)===`array`?`${t}[]`:t}getSuccessResponseShape(e){let t=Object.entries(e).filter(([e])=>/^2\d\d$/.test(e)).sort(([e],[t])=>e.localeCompare(t))[0]?.[1];if(!t)return this.emptyObjectShape;let n=this.getPreferredMediaType(t.content);if(!n)return this.emptyObjectShape;let r=this.resolveResponsePayloadSchema(n.schema,n.example);return r.schema?this.resolveSchemaType(r.schema)===`array`?this.schemaToShape(r.schema.items,`Item`,this.extractExampleArrayItem(r.example)):this.schemaToShape(r.schema,`Response`,r.example):this.schemaToShape(n.schema,`Response`,n.example)}getRequestInputShape(e){if(!e)return this.emptyObjectShape;let t=this.getPreferredMediaType(e.content);return t?this.schemaToShape(t.schema,`Input`,t.example):this.emptyObjectShape}getResponseExampleShape(e){let t=Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).flatMap(([,e])=>{let t=this.getPreferredMediaType(e.content);if(!t)return[];let n=t.example??t.schema?.example;return t.schema?[this.schemaToShape(t.schema,`ResponseExample`,n)]:n===void 0?[]:[this.inferShapeFromExample(n,`ResponseExample`)]}),n=this.dedupeShapes(t);return n.length===0?{kind:`primitive`,type:`unknown`}:n.length===1?n[0]:{kind:`union`,types:n}}createParameterGroupShape(e,t,n){let r=(e??[]).filter(e=>e.in===t).sort((e,t)=>e.name.localeCompare(t.name)),i=[...r,...this.naming.getInferredPathParameters(n,t,r)].sort((e,t)=>e.name.localeCompare(t.name));return i.length===0?this.emptyObjectShape:this.createObjectShape(i.map(e=>({key:e.name,optional:!(e.required??!1),shape:this.schemaToShape(e.schema,e.name,e.example)})))}get emptyObjectShape(){return this.createObjectShape([])}findCompatibleObjectDeclaration(e,t,n){let r=this.naming.sanitizeTypeName(t);return n.declarations.find(t=>t.kind!==`interface`||t.baseName!==r?!1:this.isObjectShapeAssignableTo(e,t.rawShape)||this.isObjectShapeAssignableTo(t.rawShape,e)||this.canMergeObjectShapes(t.rawShape,e))}canMergeObjectShapes(e,t){let n=new Set([...e.properties.map(e=>e.key),...t.properties.map(e=>e.key)]);for(let r of n){let n=e.properties.find(e=>e.key===r),i=t.properties.find(e=>e.key===r);if(!n||!i){if(!(n??i)?.optional)return!1;continue}if(!this.canMergeShapes(n.shape,i.shape))return!1}return!0}isObjectShapeAssignableTo(e,t){let n=new Map(t.properties.map(e=>[e.key,e]));for(let t of e.properties){let e=n.get(t.key);if(!e||t.optional&&!e.optional||!this.isShapeAssignableTo(t.shape,e.shape))return!1}return t.properties.every(t=>e.properties.some(e=>e.key===t.key)||t.optional)}isShapeAssignableTo(e,t){if(t.kind===`union`)return t.types.some(t=>this.isShapeAssignableTo(e,t));switch(e.kind){case`primitive`:return t.kind===`primitive`?e.type===t.type:!1;case`array`:return t.kind===`array`?this.isShapeAssignableTo(e.item,t.item):!1;case`union`:return e.types.every(e=>this.isShapeAssignableTo(e,t));case`object`:return t.kind===`object`?this.isObjectShapeAssignableTo(e,t):!1}}canMergeShapes(e,t){return e.kind===`union`?e.types.every(e=>this.canMergeShapes(e,t)):t.kind===`union`?t.types.every(t=>this.canMergeShapes(e,t)):e.kind===`primitive`&&t.kind===`primitive`?!0:e.kind===`array`&&t.kind===`array`?this.canMergeShapes(e.item,t.item):e.kind===`object`&&t.kind===`object`?this.canMergeObjectShapes(e,t):!1}mergeObjectShapes(e,t){let n=new Set([...e.properties.map(e=>e.key),...t.properties.map(e=>e.key)]);return this.createObjectShape(Array.from(n).map(n=>{let r=e.properties.find(e=>e.key===n),i=t.properties.find(e=>e.key===n);return r&&i?{key:n,optional:r.optional||i.optional,shape:this.mergeShapes(r.shape,i.shape)}:{key:n,optional:!0,shape:(r??i).shape}}))}mergeShapes(e,t){if(e.kind===`union`||t.kind===`union`||e.kind!==t.kind)return this.createUnionShape(e,t);switch(e.kind){case`primitive`:return t.kind===`primitive`&&e.type===t.type?e:this.createUnionShape(e,t);case`array`:return t.kind===`array`?{kind:`array`,item:this.mergeShapes(e.item,t.item)}:e;case`object`:return t.kind===`object`?this.mergeObjectShapes(e,t):e}}createUnionShape(...e){let t=e.flatMap(e=>e.kind===`union`?e.types:[e]),n=this.dedupeShapes(t);return n.length===1?n[0]:{kind:`union`,types:n}}prepareNestedShape(e,t,n){if(e.kind===`object`)return{kind:`primitive`,type:this.registerObjectShape(e,this.naming.sanitizeTypeName(this.naming.singularize(t)),n,this.naming.sanitizeTypeName(t))};if(e.kind===`array`)return{kind:`array`,item:this.prepareNestedShape(e.item,this.naming.singularize(t),n)};if(e.kind===`union`){let r=this.dedupeShapes(e.types.map((e,r)=>this.prepareNestedShape(e,this.getUnionMemberKeyHint(t,r,e),n)));return r.length===1?r[0]:{kind:`union`,types:r}}return e}getUnionMemberKeyHint(e,t,n){if(n.kind!==`object`&&n.kind!==`array`)return e;let r=this.naming.sanitizeTypeName(e);return r.endsWith(`ResponseExample`)?`${r}Variant${t+1}`:e}schemaToShape(e,t,n){if(!e)return this.inferShapeFromExample(n,t);let r=this.resolveSchemaType(e);if(r===`array`)return{kind:`array`,item:this.schemaToShape(e.items,this.naming.singularize(t),this.extractExampleArrayItem(e.example)??this.extractExampleArrayItem(n))};if(r===`object`){let r=this.isRecord(e.example)?e.example:this.isRecord(n)?n:void 0,i=Object.entries(e.properties??{}).sort(([e],[t])=>e.localeCompare(t)).map(([t,n])=>({key:t,optional:!(e.required??[]).includes(t),shape:this.schemaToShape(n,t,r?.[t])}));return i.length>0?this.createObjectShape(i):this.inferShapeFromExample(e.example??n,t)}return r===`integer`||r===`number`?{kind:`primitive`,type:`number`}:r===`string`?{kind:`primitive`,type:`string`}:r===`boolean`?{kind:`primitive`,type:`boolean`}:e.example===null||n===null?{kind:`primitive`,type:`null`}:e.example!==void 0||n!==void 0?this.inferShapeFromExample(e.example??n,t):{kind:`primitive`,type:`unknown`}}dedupeShapes(e){let t=new Set;return e.filter(e=>{let n=this.getShapeSignature(e);return t.has(n)?!1:(t.add(n),!0)})}createObjectShape(e){let t=e.map(e=>({...e})).sort((e,t)=>e.key.localeCompare(t.key));return{kind:`object`,signature:JSON.stringify(t.map(e=>({key:e.key,optional:e.optional,shape:this.getShapeSignature(e.shape)}))),properties:t}}getShapeSignature(e){switch(e.kind){case`primitive`:return`primitive:${e.type}`;case`array`:return`array:${this.getShapeSignature(e.item)}`;case`union`:return`union:${e.types.map(e=>this.getShapeSignature(e)).join(`|`)}`;case`object`:return`object:${e.signature}`}}getPreferredMediaType(e){if(e)return e[`application/json`]??e[`application/*+json`]??Object.values(e)[0]}resolveResponsePayloadSchema(e,t){for(let n of[[`data`],[`meta`,`data`]]){let r=this.getSchemaCandidateAtPath(e,t,n);if(r)return r}return{}}getSchemaCandidateAtPath(e,t,n){let r=this.getSchemaAtPath(e,n),i=this.getExampleAtPath(t,n);if(!(!r&&i===void 0))return r?{schema:r.example===void 0&&i!==void 0?{...r,example:i}:r,example:i??r.example}:{schema:{...this.inferSchemaTypeFromExample(i),example:i},example:i}}getSchemaAtPath(e,t){let n=e;for(let e of t){if(!n?.properties?.[e])return;n=n.properties[e]}return n}getExampleAtPath(e,t){let n=e;for(let e of t){if(!this.isRecord(n)||!(e in n))return;n=n[e]}return n}inferSchemaTypeFromExample(e){return Array.isArray(e)?{type:`array`,items:e.map(e=>this.inferSchemaTypeFromExample(e)).find(e=>this.hasSchemaDetails(e))??{}}:this.isRecord(e)?{type:`object`,properties:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,this.inferSchemaTypeFromExample(t)]))}:typeof e==`string`?{type:`string`}:typeof e==`number`?{type:`number`}:typeof e==`boolean`?{type:`boolean`}:{}}hasSchemaDetails(e){return!!(e?.type||e?.properties||e?.items||e?.example!==void 0)}resolveSchemaType(e){return e.type??(e.properties?`object`:void 0)}extractExampleArrayItem(e){return Array.isArray(e)?e[0]:void 0}createUniqueTypeName(e,t,n){let r=this.naming.sanitizeTypeName(e)||`GeneratedEntity`,i=this.naming.sanitizeTypeName(n),a=r,o=2;if(!t.usedNames.has(a)||(a=this.naming.insertCollisionSuffix(r,i),!t.usedNames.has(a)))return t.usedNames.add(a),a;for(;t.usedNames.has(a);)a=`${r}${o}`,o+=1;return t.usedNames.add(a),a}isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}},X=class{naming=new ze;shapes=new Be(this.naming);createContext(){return this.shapes.createContext()}collectSemanticModels(e){let t=[];for(let[n,r]of Object.entries(e.paths)){let e=this.naming.deriveOperationNaming(n),i=e.baseName,a=Object.entries(r).sort(([,e],[,t])=>this.getOperationPriority(t)-this.getOperationPriority(e));for(let[r,o]of a){let a=e.collisionSuffix||this.naming.fallbackCollisionSuffix(r,n,i);t.push({path:n,method:r,name:i,role:`response`,shape:this.shapes.getSuccessResponseShape(o.responses),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}ResponseExample`,role:`responseExample`,shape:this.shapes.getResponseExampleShape(o.responses),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Input`,role:`input`,shape:this.shapes.getRequestInputShape(o.requestBody),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Query`,role:`query`,shape:this.shapes.createParameterGroupShape(o.parameters,`query`,n),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Header`,role:`header`,shape:this.shapes.createParameterGroupShape(o.parameters,`header`,n),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Params`,role:`params`,shape:this.shapes.createParameterGroupShape(o.parameters,`path`,n),collisionSuffix:a})}}return t}buildSdkManifest(e,t,n={}){let r=this.naming.deriveSdkGroupNamesBySignature(e,n.namespaceStrategy??`smart`),i=new Map,a=this.buildSecuritySchemes(e),o=this.buildSecurityRequirements(e.security);for(let[a,o]of Object.entries(e.paths)){let e=this.naming.getStaticPathSegments(a).join(`/`),s=r.get(e)??`Resource`,c=this.naming.toCamelCase(this.naming.pluralize(s)),l=i.get(c)??{className:s,propertyName:c,operations:[]};for(let[e,r]of Object.entries(o)){let i=t.get(`${a}::${e}`)??{response:`Record<string, never>`,responseExample:`unknown`,input:`Record<string, never>`,query:`Record<string, never>`,header:`Record<string, never>`,params:`Record<string, never>`};l.operations.push({path:a,method:e.toUpperCase(),methodName:this.naming.deriveSdkMethodName(e,a,r,n.methodStrategy??`smart`),summary:r.summary,description:r.description,operationId:r.operationId,requestBodyDescription:r.requestBody?.description,responseDescription:this.resolveSuccessResponseDescription(r.responses),responseType:this.shapes.resolveSdkResponseType(r.responses,i.response),inputType:i.input,queryType:i.query,headerType:i.header,paramsType:i.params,hasBody:!!r.requestBody,bodyRequired:r.requestBody?.required??!1,pathParams:this.naming.createSdkParameterManifest(r.parameters,`path`,a),queryParams:this.naming.createSdkParameterManifest(r.parameters,`query`,a),headerParams:this.naming.createSdkParameterManifest(r.parameters,`header`,a),security:this.buildSecurityRequirements(r.security)})}i.set(c,l)}return{groups:Array.from(i.values()).map(e=>({...e,operations:this.naming.ensureUniqueSdkMethodNames(e.operations)})).sort((e,t)=>e.propertyName.localeCompare(t.propertyName)),securitySchemes:a,security:o}}inferShapeFromExample(e,t){return this.shapes.inferShapeFromExample(e,t)}sanitizeTypeName(e){return this.naming.sanitizeTypeName(e)}registerNamedShape(e,t,n,r){return this.shapes.registerNamedShape(e,t,n,r)}namespaceTopLevelShape(e,t){return this.shapes.namespaceTopLevelShape(e,t)}registerObjectShape(e,t,n,r,i=!1){return this.shapes.registerObjectShape(e,t,n,r,i)}getOperationPriority(e){return Number(!!e.requestBody)*10}resolveSuccessResponseDescription(e){for(let t of[`200`,`201`,`202`,`204`]){let n=e[t]?.description?.trim();if(n)return n}for(let t of Object.values(e)){let e=t.description?.trim();if(e)return e}}buildSecuritySchemes(e){return Object.entries(e.components?.securitySchemes??{}).map(([e,t])=>this.createSecuritySchemeManifest(e,t)).filter(e=>e!==null).sort((e,t)=>e.name.localeCompare(t.name))}createSecuritySchemeManifest(e,t){let n=this.createSecurityHelperName(e);if(t.type===`apiKey`)return{name:e,helperName:n,description:t.description,type:`apiKey`,authType:`apiKey`,in:t.in,parameterName:t.name};if(t.type===`oauth2`)return{name:e,helperName:n,description:t.description,type:`oauth2`,authType:`oauth2`,scopes:this.collectSecurityScopes(t)};if(t.type===`openIdConnect`)return{name:e,helperName:n,description:t.description,type:`openIdConnect`,authType:`oauth2`,openIdConnectUrl:t.openIdConnectUrl};let r=t.scheme.toLowerCase();return{name:e,helperName:n,description:t.description,type:`http`,authType:r===`basic`?`basic`:`bearer`,scheme:t.scheme,bearerFormat:t.bearerFormat}}buildSecurityRequirements(e){if(!e||e.length===0)return;let t=e.map(e=>({schemes:Object.entries(e).map(([e,t])=>({name:e,scopes:[...t].sort()})).sort((e,t)=>e.name.localeCompare(t.name))})).filter(e=>e.schemes.length>0);return t.length>0?t:void 0}collectSecurityScopes(e){let t=new Set;for(let n of Object.values(e.flows??{}))for(let e of Object.keys(n.scopes??{}))t.add(e);return t.size>0?Array.from(t).sort():void 0}createSecurityHelperName(e){let t=this.naming.sanitizeTypeName(e);return t.endsWith(`Auth`)?`create${t}`:`create${t}Auth`}},Z=class e{typeBuilder=new X;moduleRenderer=new Re;static generateModule=(t,n=`GeneratedOutput`,r={})=>new e().generate(t,n,r);generate(e,t=`GeneratedOutput`,n={}){return this.isOpenApiDocumentLike(e)?this.generateModule(e,t,n):this.generateGenericModule(e,t)}generateModule(e,t,n={}){let r=this.typeBuilder.createContext(),i=new Map;for(let t of this.typeBuilder.collectSemanticModels(e)){let e=`${t.path}::${t.method}`,n=this.typeBuilder.registerNamedShape(this.typeBuilder.namespaceTopLevelShape(t.shape,t.role),t.name,r,t.collisionSuffix),a=i.get(e)??{response:`Record<string, never>`,responseExample:`unknown`,input:`Record<string, never>`,query:`Record<string, never>`,header:`Record<string, never>`,params:`Record<string, never>`};a[t.role]=n,i.set(e,a)}let a=r.declarations.map(e=>this.moduleRenderer.renderDeclaration(e)).join(`
|
|
139
|
+
`)}\n${i}}`}return`undefined`}normalizeOpenApiDocument(e){return this.normalizeObject(e,(e,t,n)=>{if(e===`example`&&n&&typeof n==`object`&&!Array.isArray(n)){let e=n;if(e.schema&&this.isPlainObject(e.schema))return this.normalizeExample(t,e.schema);if(this.isSchemaLike(e))return this.normalizeExample(t,e)}return t})}normalizeObject(e,t){if(Array.isArray(e))return e.map(e=>this.normalizeObject(e,t)).filter(e=>e!==void 0);if(!this.isPlainObject(e))return e;let n={};for(let[r,i]of Object.entries(e)){let a=t(r,i,e),o=this.normalizeObject(a,t);o!==void 0&&(n[r]=o)}return n}normalizeExample(e,t){if(e===void 0)return;let n=typeof t.type==`string`?t.type:void 0;if(n===`string`)return typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`?String(e):void 0;if(n===`number`||n===`integer`)return typeof e==`number`?e:typeof e==`string`&&e.trim()!==``&&Number.isFinite(Number(e))?Number(e):void 0;if(n===`boolean`)return typeof e==`boolean`?e:e===`true`?!0:e===`false`?!1:void 0;if(n===`array`){if(!Array.isArray(e))return;let n=this.isPlainObject(t.items)?t.items:void 0;return n?e.map(e=>this.normalizeExample(e,n)).filter(e=>e!==void 0):e}if(n===`object`){if(!this.isPlainObject(e))return;let n=this.isPlainObject(t.properties)?t.properties:{},r=Array.isArray(t.required)?t.required.filter(e=>typeof e==`string`):[],i={};for(let[t,r]of Object.entries(e)){let e=n[t],a=e?this.normalizeExample(r,e):r;a!==void 0&&(i[t]=a)}for(let e of r){if(e in i)continue;let t=n[e];if(!t)return;let r=t.default===void 0?t.example===void 0?void 0:this.normalizeExample(t.example,t):this.normalizeExample(t.default,t);if(r===void 0)return;i[e]=r}return i}return e}isPlainObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}isSchemaLike(e){return`type`in e||`properties`in e||`items`in e||`required`in e||`default`in e}indent(e){return` `.repeat(e)}escapeStringLiteral(e){return e.replace(/\\/g,String.raw`\\`).replace(/'/g,String.raw`\'`).replace(/\r/g,String.raw`\r`).replace(/\n/g,String.raw`\n`).replace(/\t/g,String.raw`\t`).replace(/\f/g,String.raw`\f`).replace(/\x08/g,String.raw`\b`).replace(/\u2028/g,String.raw`\u2028`).replace(/\u2029/g,String.raw`\u2029`)}isPathParam(e){return e.startsWith(`{`)&&e.endsWith(`}`)||/^:[A-Za-z0-9_]+$/.test(e)}stripPathParam(e){return e.replace(/^\{/,``).replace(/\}$/,``).replace(/^:/,``)}},Ye=class e{static contextualTailSegments=new Set([`history`,`status`,`detail`,`details`]);static irregularSingulars=new Map([[`analyses`,`analysis`],[`diagnoses`,`diagnosis`],[`parentheses`,`parenthesis`],[`statuses`,`status`],[`synopses`,`synopsis`],[`theses`,`thesis`]]);static invariantSingulars=new Set([`analysis`,`diagnosis`,`news`,`series`,`species`,`status`,`synopsis`,`thesis`]);static nestedContextSegments=new Set([`account`,`accounts`,`transaction`,`transactions`,`wallet`,`wallets`,`virtual-account`,`virtual-accounts`,`history`]);static roleSuffixes=[`Input`,`Query`,`Header`,`Params`];sanitizeTypeName(e){let t=e.replace(/[^A-Za-z0-9]+/g,` `).trim();if(!t)return`GeneratedEntity`;let n=t.split(/\s+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);return/^[A-Za-z_$]/.test(n)?n:`Type${n}`}isPathParam(e){return e.startsWith(`{`)&&e.endsWith(`}`)||/^:[A-Za-z0-9_]+$/.test(e)}stripPathParam(e){return e.replace(/^\{/,``).replace(/\}$/,``).replace(/^:/,``)}singularize(t){let n=t.toLowerCase();return e.irregularSingulars.has(n)?e.irregularSingulars.get(n)??t:e.invariantSingulars.has(n)?t:/ies$/i.test(t)?`${t.slice(0,-3)}y`:/(sses|shes|ches|xes|zes)$/i.test(t)||/uses$/i.test(t)?t.slice(0,-2):t.endsWith(`s`)&&!t.endsWith(`ss`)&&t.length>1?t.slice(0,-1):t}pluralize(e){return/y$/i.test(e)?`${e.slice(0,-1)}ies`:/s$/i.test(e)?e:`${e}s`}toCamelCase(e){let t=this.sanitizeTypeName(e);return t.charAt(0).toLowerCase()+t.slice(1)}deriveOperationNaming(t){let n=this.getNormalizedPathSegments(t),r=n.filter(e=>!this.isPathParam(e)).map(e=>this.singularize(e)),i=n.filter(e=>this.isPathParam(e)).map(e=>this.singularize(this.stripPathParam(e))),a=r[r.length-1]??`resource`,o=r[r.length-2]??null,s=n.slice(0,-1).some(e=>this.isPathParam(e)),c=!!(o&&(e.contextualTailSegments.has(a.toLowerCase())||s&&e.nestedContextSegments.has(a.toLowerCase())));return{baseName:this.sanitizeTypeName(c?`${o} ${a}`:a),collisionSuffix:i.length>0?`By ${i.map(e=>this.sanitizeTypeName(e)).join(` And `)}`:o&&!c?this.sanitizeTypeName(o):``}}fallbackCollisionSuffix(e,t,n){let r=this.getNormalizedPathSegments(t),i=r.filter(e=>!this.isPathParam(e)),a=i[i.length-1]??``,o=r.some(e=>this.isPathParam(e));return e===`get`&&!o&&/s$/i.test(a)?`List`:e===`post`&&!o?`Create`:(e===`put`||e===`patch`)&&o?`Update`:e===`delete`?`Delete`:`${this.sanitizeTypeName(e)}${n}`}insertCollisionSuffix(t,n){if(!n)return t;for(let r of e.roleSuffixes)if(t.endsWith(r)&&t.length>r.length)return`${t.slice(0,-r.length)}${n}${r}`;return`${t}${n}`}deriveSdkGroupNamesBySignature(e,t){let n=new Map;for(let t of Object.keys(e.paths)){let e=this.getStaticPathSignature(t);n.has(e)||n.set(e,t)}let r=Array.from(n.entries()).map(([e,n])=>({signature:e,staticSegments:e.split(`/`).filter(Boolean),candidates:this.buildSdkGroupNameCandidates(n,t)})).sort((e,t)=>e.staticSegments.length-t.staticSegments.length||e.signature.localeCompare(t.signature)),i=new Map,a=new Set;for(let e of r){let t=e.candidates.find(e=>!a.has(e))??this.createUniqueSdkGroupName(e.candidates[e.candidates.length-1]??`Resource`,a);a.add(t),i.set(e.signature,t)}return i}getStaticPathSegments(e){return this.getNormalizedPathSegments(e).filter(e=>!this.isPathParam(e)).map(e=>this.singularize(e))}getStaticPathSignature(e){return this.getStaticPathSegments(e).join(`/`)}getNormalizedPathSegments(e){return e.split(`/`).map(e=>e.trim()).filter(Boolean).filter(e=>!/^v\d+$/i.test(e))}deriveSdkMethodName(e,t,n,r){if(r===`operation-id`&&n.operationId)return this.toCamelCase(this.sanitizeTypeName(n.operationId));let i=this.getNormalizedPathSegments(t).some(e=>this.isPathParam(e));return e===`get`?this.endsWithPluralStaticSegment(t)?`list`:i?`get`:`list`:e===`post`?`create`:e===`patch`||e===`put`?`update`:e===`delete`?`delete`:this.toCamelCase(this.sanitizeTypeName(e))}ensureUniqueSdkMethodNames(e){let t=new Map;return e.map(e=>{let n=t.get(e.methodName)??0;if(t.set(e.methodName,n+1),n===0)return e;let r=this.sanitizeTypeName(this.fallbackCollisionSuffix(e.method.toLowerCase(),e.path,`Operation`));return{...e,methodName:`${e.methodName}${r}`}})}createSdkParameterManifest(e,t,n){return[...(e??[]).filter(e=>e.in===t).sort((e,t)=>e.name.localeCompare(t.name)),...this.getInferredPathParameters(n,t,(e??[]).filter(e=>e.in===t))].sort((e,t)=>e.name.localeCompare(t.name)).map(e=>({name:e.name,accessor:this.toParameterAccessor(e.name),in:t,required:e.required??!1,description:e.description}))}getInferredPathParameters(e,t,n){if(t!==`path`||!e)return[];let r=new Set(n.map(e=>e.name));return this.getNormalizedPathSegments(e).filter(e=>this.isPathParam(e)).map(e=>this.stripPathParam(e)).filter(e=>!r.has(e)).map(e=>({name:e,in:`path`,required:!0,schema:{type:`string`}}))}buildSdkGroupNameCandidates(e,t){let n=this.getNormalizedPathSegments(e),r=n.filter(e=>!this.isPathParam(e)),i=r.map(e=>this.singularize(e)),a=this.deriveOperationNaming(e).baseName,o=this.getPreferredSdkGroupName(n,r,i),s=i.map((e,t,n)=>this.sanitizeTypeName(n.slice(t).join(` `))).reverse();return t===`scoped`?Array.from(new Set([o??``,this.sanitizeTypeName(i.join(` `)),...s,a].filter(Boolean))):Array.from(new Set([o??``,a,...s].filter(Boolean)))}getPreferredSdkGroupName(e,t,n){let r=t[t.length-1],i=n[n.length-1],a=n[n.length-2],o=e.some(e=>this.isPathParam(e)),s=e.slice(0,-1).some(e=>this.isPathParam(e));return!r||!i||!a?null:t.length===2&&!o?this.sanitizeTypeName(`${i} ${a}`):s?this.singularize(r)===r?this.sanitizeTypeName(`${i} ${a}`):this.sanitizeTypeName(`${a} ${i}`):null}createUniqueSdkGroupName(e,t){let n=2,r=e;for(;t.has(r);)r=`${e}${n}`,n+=1;return r}endsWithPluralStaticSegment(e){let t=this.getNormalizedPathSegments(e).at(-1);return!t||this.isPathParam(t)?!1:this.singularize(t)!==t}toParameterAccessor(e){let t=e.replace(/[^A-Za-z0-9]+/g,` `).trim();if(!t)return`value`;let[n,...r]=t.split(/\s+/).filter(Boolean),i=[n.toLowerCase(),...r.map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase())].join(``);return/^[A-Za-z_$]/.test(i)?i:`value${i}`}},Xe=class{constructor(e){this.naming=e}createContext(){return{declarations:[],declarationByName:new Map,nameBySignature:new Map,usedNames:new Set}}namespaceTopLevelShape(e,t){return e.kind===`object`?{...e,signature:`${t}:${e.signature}`}:e}inferShapeFromExample(e,t){if(e===null)return{kind:`primitive`,type:`null`};if(Array.isArray(e)){if(e.length===0)return{kind:`array`,item:{kind:`primitive`,type:`unknown`}};let n=this.dedupeShapes(e.map(e=>this.inferShapeFromExample(e,this.naming.singularize(t))));return{kind:`array`,item:n.length===1?n[0]:{kind:`union`,types:n}}}if(this.isRecord(e))return this.createObjectShape(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>({key:e,optional:!1,shape:this.inferShapeFromExample(t,e)})));switch(typeof e){case`string`:return{kind:`primitive`,type:`string`};case`number`:return{kind:`primitive`,type:`number`};case`boolean`:return{kind:`primitive`,type:`boolean`};default:return{kind:`primitive`,type:`unknown`}}}registerNamedShape(e,t,n,r){if(e.kind===`object`)return this.registerObjectShape(e,t,n,r,!0);let i=this.createUniqueTypeName(t,n,r),a={kind:`shape-alias`,name:i,shape:this.prepareNestedShape(e,t,n)};return n.declarations.push(a),n.declarationByName.set(i,a),i}registerObjectShape(e,t,n,r,i=!1){let a=n.nameBySignature.get(e.signature),o=this.findCompatibleObjectDeclaration(e,t,n);if(a){if(i&&a!==t&&!n.declarationByName.has(t)){let e=this.createUniqueTypeName(t,n,r);if(e!==a){let t={kind:`interface-alias`,name:e,target:a};n.declarations.push(t),n.declarationByName.set(e,t)}}return a}if(o){if(this.isObjectShapeAssignableTo(e,o.rawShape))return n.nameBySignature.set(e.signature,o.name),o.name;let t=this.mergeObjectShapes(o.rawShape,e);return o.rawShape=t,o.properties=t.properties.map(e=>({...e,shape:this.prepareNestedShape(e.shape,e.key,n)})),n.nameBySignature.set(e.signature,o.name),n.nameBySignature.set(t.signature,o.name),o.name}let s=this.createUniqueTypeName(t,n,r),c={kind:`interface`,name:s,baseName:this.naming.sanitizeTypeName(t),rawShape:e,properties:[]};return n.nameBySignature.set(e.signature,s),n.declarations.push(c),n.declarationByName.set(s,c),c.properties=e.properties.map(e=>({...e,shape:this.prepareNestedShape(e.shape,e.key,n)})),s}resolveSdkResponseType(e,t){let n=Object.entries(e).filter(([e])=>/^2\d\d$/.test(e)).sort(([e],[t])=>e.localeCompare(t))[0]?.[1];if(!n)return t;let r=this.getPreferredMediaType(n.content);if(!r)return t;let i=this.resolveResponsePayloadSchema(r.schema,r.example).schema??r.schema;return i&&this.resolveSchemaType(i)===`array`?`${t}[]`:t}getSuccessResponseShape(e){let t=Object.entries(e).filter(([e])=>/^2\d\d$/.test(e)).sort(([e],[t])=>e.localeCompare(t))[0]?.[1];if(!t)return this.emptyObjectShape;let n=this.getPreferredMediaType(t.content);if(!n)return this.emptyObjectShape;let r=this.resolveResponsePayloadSchema(n.schema,n.example);return r.schema?this.resolveSchemaType(r.schema)===`array`?this.schemaToShape(r.schema.items,`Item`,this.extractExampleArrayItem(r.example)):this.schemaToShape(r.schema,`Response`,r.example):this.schemaToShape(n.schema,`Response`,n.example)}getRequestInputShape(e){if(!e)return this.emptyObjectShape;let t=this.getPreferredMediaType(e.content);return t?this.schemaToShape(t.schema,`Input`,t.example):this.emptyObjectShape}getResponseExampleShape(e){let t=Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).flatMap(([,e])=>{let t=this.getPreferredMediaType(e.content);if(!t)return[];let n=t.example??t.schema?.example;return t.schema?[this.schemaToShape(t.schema,`ResponseExample`,n)]:n===void 0?[]:[this.inferShapeFromExample(n,`ResponseExample`)]}),n=this.dedupeShapes(t);return n.length===0?{kind:`primitive`,type:`unknown`}:n.length===1?n[0]:{kind:`union`,types:n}}createParameterGroupShape(e,t,n){let r=(e??[]).filter(e=>e.in===t).sort((e,t)=>e.name.localeCompare(t.name)),i=[...r,...this.naming.getInferredPathParameters(n,t,r)].sort((e,t)=>e.name.localeCompare(t.name));return i.length===0?this.emptyObjectShape:this.createObjectShape(i.map(e=>({key:e.name,optional:!(e.required??!1),shape:this.schemaToShape(e.schema,e.name,e.example)})))}get emptyObjectShape(){return this.createObjectShape([])}findCompatibleObjectDeclaration(e,t,n){let r=this.naming.sanitizeTypeName(t);return n.declarations.find(t=>t.kind!==`interface`||t.baseName!==r?!1:this.isObjectShapeAssignableTo(e,t.rawShape)||this.isObjectShapeAssignableTo(t.rawShape,e)||this.canMergeObjectShapes(t.rawShape,e))}canMergeObjectShapes(e,t){let n=new Set([...e.properties.map(e=>e.key),...t.properties.map(e=>e.key)]);for(let r of n){let n=e.properties.find(e=>e.key===r),i=t.properties.find(e=>e.key===r);if(!n||!i){if(!(n??i)?.optional)return!1;continue}if(!this.canMergeShapes(n.shape,i.shape))return!1}return!0}isObjectShapeAssignableTo(e,t){let n=new Map(t.properties.map(e=>[e.key,e]));for(let t of e.properties){let e=n.get(t.key);if(!e||t.optional&&!e.optional||!this.isShapeAssignableTo(t.shape,e.shape))return!1}return t.properties.every(t=>e.properties.some(e=>e.key===t.key)||t.optional)}isShapeAssignableTo(e,t){if(t.kind===`union`)return t.types.some(t=>this.isShapeAssignableTo(e,t));switch(e.kind){case`primitive`:return t.kind===`primitive`?e.type===t.type:!1;case`array`:return t.kind===`array`?this.isShapeAssignableTo(e.item,t.item):!1;case`union`:return e.types.every(e=>this.isShapeAssignableTo(e,t));case`object`:return t.kind===`object`?this.isObjectShapeAssignableTo(e,t):!1}}canMergeShapes(e,t){return e.kind===`union`?e.types.every(e=>this.canMergeShapes(e,t)):t.kind===`union`?t.types.every(t=>this.canMergeShapes(e,t)):e.kind===`primitive`&&t.kind===`primitive`?!0:e.kind===`array`&&t.kind===`array`?this.canMergeShapes(e.item,t.item):e.kind===`object`&&t.kind===`object`?this.canMergeObjectShapes(e,t):!1}mergeObjectShapes(e,t){let n=new Set([...e.properties.map(e=>e.key),...t.properties.map(e=>e.key)]);return this.createObjectShape(Array.from(n).map(n=>{let r=e.properties.find(e=>e.key===n),i=t.properties.find(e=>e.key===n);return r&&i?{key:n,optional:r.optional||i.optional,shape:this.mergeShapes(r.shape,i.shape)}:{key:n,optional:!0,shape:(r??i).shape}}))}mergeShapes(e,t){if(e.kind===`union`||t.kind===`union`||e.kind!==t.kind)return this.createUnionShape(e,t);switch(e.kind){case`primitive`:return t.kind===`primitive`&&e.type===t.type?e:this.createUnionShape(e,t);case`array`:return t.kind===`array`?{kind:`array`,item:this.mergeShapes(e.item,t.item)}:e;case`object`:return t.kind===`object`?this.mergeObjectShapes(e,t):e}}createUnionShape(...e){let t=e.flatMap(e=>e.kind===`union`?e.types:[e]),n=this.dedupeShapes(t);return n.length===1?n[0]:{kind:`union`,types:n}}prepareNestedShape(e,t,n){if(e.kind===`object`)return{kind:`primitive`,type:this.registerObjectShape(e,this.naming.sanitizeTypeName(this.naming.singularize(t)),n,this.naming.sanitizeTypeName(t))};if(e.kind===`array`)return{kind:`array`,item:this.prepareNestedShape(e.item,this.naming.singularize(t),n)};if(e.kind===`union`){let r=this.dedupeShapes(e.types.map((e,r)=>this.prepareNestedShape(e,this.getUnionMemberKeyHint(t,r,e),n)));return r.length===1?r[0]:{kind:`union`,types:r}}return e}getUnionMemberKeyHint(e,t,n){if(n.kind!==`object`&&n.kind!==`array`)return e;let r=this.naming.sanitizeTypeName(e);return r.endsWith(`ResponseExample`)?`${r}Variant${t+1}`:e}schemaToShape(e,t,n){if(!e)return this.inferShapeFromExample(n,t);let r=this.resolveSchemaType(e);if(r===`array`)return{kind:`array`,item:this.schemaToShape(e.items,this.naming.singularize(t),this.extractExampleArrayItem(e.example)??this.extractExampleArrayItem(n))};if(r===`object`){let r=this.isRecord(e.example)?e.example:this.isRecord(n)?n:void 0,i=Object.entries(e.properties??{}).sort(([e],[t])=>e.localeCompare(t)).map(([t,n])=>({key:t,optional:!(e.required??[]).includes(t),shape:this.schemaToShape(n,t,r?.[t])}));return i.length>0?this.createObjectShape(i):this.inferShapeFromExample(e.example??n,t)}return r===`integer`||r===`number`?{kind:`primitive`,type:`number`}:r===`string`?{kind:`primitive`,type:`string`}:r===`boolean`?{kind:`primitive`,type:`boolean`}:e.example===null||n===null?{kind:`primitive`,type:`null`}:e.example!==void 0||n!==void 0?this.inferShapeFromExample(e.example??n,t):{kind:`primitive`,type:`unknown`}}dedupeShapes(e){let t=new Set;return e.filter(e=>{let n=this.getShapeSignature(e);return t.has(n)?!1:(t.add(n),!0)})}createObjectShape(e){let t=e.map(e=>({...e})).sort((e,t)=>e.key.localeCompare(t.key));return{kind:`object`,signature:JSON.stringify(t.map(e=>({key:e.key,optional:e.optional,shape:this.getShapeSignature(e.shape)}))),properties:t}}getShapeSignature(e){switch(e.kind){case`primitive`:return`primitive:${e.type}`;case`array`:return`array:${this.getShapeSignature(e.item)}`;case`union`:return`union:${e.types.map(e=>this.getShapeSignature(e)).join(`|`)}`;case`object`:return`object:${e.signature}`}}getPreferredMediaType(e){if(e)return e[`application/json`]??e[`application/*+json`]??Object.values(e)[0]}resolveResponsePayloadSchema(e,t){for(let n of[[`data`],[`meta`,`data`]]){let r=this.getSchemaCandidateAtPath(e,t,n);if(r)return r}return{}}getSchemaCandidateAtPath(e,t,n){let r=this.getSchemaAtPath(e,n),i=this.getExampleAtPath(t,n);if(!(!r&&i===void 0))return r?{schema:r.example===void 0&&i!==void 0?{...r,example:i}:r,example:i??r.example}:{schema:{...this.inferSchemaTypeFromExample(i),example:i},example:i}}getSchemaAtPath(e,t){let n=e;for(let e of t){if(!n?.properties?.[e])return;n=n.properties[e]}return n}getExampleAtPath(e,t){let n=e;for(let e of t){if(!this.isRecord(n)||!(e in n))return;n=n[e]}return n}inferSchemaTypeFromExample(e){return Array.isArray(e)?{type:`array`,items:e.map(e=>this.inferSchemaTypeFromExample(e)).find(e=>this.hasSchemaDetails(e))??{}}:this.isRecord(e)?{type:`object`,properties:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,this.inferSchemaTypeFromExample(t)]))}:typeof e==`string`?{type:`string`}:typeof e==`number`?{type:`number`}:typeof e==`boolean`?{type:`boolean`}:{}}hasSchemaDetails(e){return!!(e?.type||e?.properties||e?.items||e?.example!==void 0)}resolveSchemaType(e){return e.type??(e.properties?`object`:void 0)}extractExampleArrayItem(e){return Array.isArray(e)?e[0]:void 0}createUniqueTypeName(e,t,n){let r=this.naming.sanitizeTypeName(e)||`GeneratedEntity`,i=this.naming.sanitizeTypeName(n),a=r,o=2;if(!t.usedNames.has(a)||(a=this.naming.insertCollisionSuffix(r,i),!t.usedNames.has(a)))return t.usedNames.add(a),a;for(;t.usedNames.has(a);)a=`${r}${o}`,o+=1;return t.usedNames.add(a),a}isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}},X=class{naming=new Ye;shapes=new Xe(this.naming);createContext(){return this.shapes.createContext()}collectSemanticModels(e){let t=[];for(let[n,r]of Object.entries(e.paths)){let e=this.naming.deriveOperationNaming(n),i=e.baseName,a=Object.entries(r).sort(([,e],[,t])=>this.getOperationPriority(t)-this.getOperationPriority(e));for(let[r,o]of a){let a=e.collisionSuffix||this.naming.fallbackCollisionSuffix(r,n,i);t.push({path:n,method:r,name:i,role:`response`,shape:this.shapes.getSuccessResponseShape(o.responses),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}ResponseExample`,role:`responseExample`,shape:this.shapes.getResponseExampleShape(o.responses),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Input`,role:`input`,shape:this.shapes.getRequestInputShape(o.requestBody),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Query`,role:`query`,shape:this.shapes.createParameterGroupShape(o.parameters,`query`,n),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Header`,role:`header`,shape:this.shapes.createParameterGroupShape(o.parameters,`header`,n),collisionSuffix:a}),t.push({path:n,method:r,name:`${i}Params`,role:`params`,shape:this.shapes.createParameterGroupShape(o.parameters,`path`,n),collisionSuffix:a})}}return t}buildSdkManifest(e,t,n={}){let r=this.naming.deriveSdkGroupNamesBySignature(e,n.namespaceStrategy??`smart`),i=new Map,a=this.buildSecuritySchemes(e),o=this.buildSecurityRequirements(e.security);for(let[a,o]of Object.entries(e.paths)){let e=this.naming.getStaticPathSegments(a).join(`/`),s=r.get(e)??`Resource`,c=this.naming.toCamelCase(this.naming.pluralize(s)),l=i.get(c)??{className:s,propertyName:c,operations:[]};for(let[e,r]of Object.entries(o)){let i=t.get(`${a}::${e}`)??{response:`Record<string, never>`,responseExample:`unknown`,input:`Record<string, never>`,query:`Record<string, never>`,header:`Record<string, never>`,params:`Record<string, never>`};l.operations.push({path:a,method:e.toUpperCase(),methodName:this.naming.deriveSdkMethodName(e,a,r,n.methodStrategy??`smart`),summary:r.summary,description:r.description,operationId:r.operationId,requestBodyDescription:r.requestBody?.description,responseDescription:this.resolveSuccessResponseDescription(r.responses),responseType:this.shapes.resolveSdkResponseType(r.responses,i.response),inputType:i.input,queryType:i.query,headerType:i.header,paramsType:i.params,hasBody:!!r.requestBody,bodyRequired:r.requestBody?.required??!1,pathParams:this.naming.createSdkParameterManifest(r.parameters,`path`,a),queryParams:this.naming.createSdkParameterManifest(r.parameters,`query`,a),headerParams:this.naming.createSdkParameterManifest(r.parameters,`header`,a),security:this.buildSecurityRequirements(r.security)})}i.set(c,l)}return{groups:Array.from(i.values()).map(e=>({...e,operations:this.naming.ensureUniqueSdkMethodNames(e.operations)})).sort((e,t)=>e.propertyName.localeCompare(t.propertyName)),securitySchemes:a,security:o}}inferShapeFromExample(e,t){return this.shapes.inferShapeFromExample(e,t)}sanitizeTypeName(e){return this.naming.sanitizeTypeName(e)}registerNamedShape(e,t,n,r){return this.shapes.registerNamedShape(e,t,n,r)}namespaceTopLevelShape(e,t){return this.shapes.namespaceTopLevelShape(e,t)}registerObjectShape(e,t,n,r,i=!1){return this.shapes.registerObjectShape(e,t,n,r,i)}getOperationPriority(e){return Number(!!e.requestBody)*10}resolveSuccessResponseDescription(e){for(let t of[`200`,`201`,`202`,`204`]){let n=e[t]?.description?.trim();if(n)return n}for(let t of Object.values(e)){let e=t.description?.trim();if(e)return e}}buildSecuritySchemes(e){return Object.entries(e.components?.securitySchemes??{}).map(([e,t])=>this.createSecuritySchemeManifest(e,t)).filter(e=>e!==null).sort((e,t)=>e.name.localeCompare(t.name))}createSecuritySchemeManifest(e,t){let n=this.createSecurityHelperName(e);if(t.type===`apiKey`)return{name:e,helperName:n,description:t.description,type:`apiKey`,authType:`apiKey`,in:t.in,parameterName:t.name};if(t.type===`oauth2`)return{name:e,helperName:n,description:t.description,type:`oauth2`,authType:`oauth2`,scopes:this.collectSecurityScopes(t)};if(t.type===`openIdConnect`)return{name:e,helperName:n,description:t.description,type:`openIdConnect`,authType:`oauth2`,openIdConnectUrl:t.openIdConnectUrl};let r=t.scheme.toLowerCase();return{name:e,helperName:n,description:t.description,type:`http`,authType:r===`basic`?`basic`:`bearer`,scheme:t.scheme,bearerFormat:t.bearerFormat}}buildSecurityRequirements(e){if(!e||e.length===0)return;let t=e.map(e=>({schemes:Object.entries(e).map(([e,t])=>({name:e,scopes:[...t].sort()})).sort((e,t)=>e.name.localeCompare(t.name))})).filter(e=>e.schemes.length>0);return t.length>0?t:void 0}collectSecurityScopes(e){let t=new Set;for(let n of Object.values(e.flows??{}))for(let e of Object.keys(n.scopes??{}))t.add(e);return t.size>0?Array.from(t).sort():void 0}createSecurityHelperName(e){let t=this.naming.sanitizeTypeName(e);return t.endsWith(`Auth`)?`create${t}`:`create${t}Auth`}},Z=class e{typeBuilder=new X;moduleRenderer=new Je;static generateModule=(t,n=`GeneratedOutput`,r={})=>new e().generate(t,n,r);generate(e,t=`GeneratedOutput`,n={}){return this.isOpenApiDocumentLike(e)?this.generateModule(e,t,n):this.generateGenericModule(e,t)}generateModule(e,t,n={}){let r=this.typeBuilder.createContext(),i=new Map;for(let t of this.typeBuilder.collectSemanticModels(e)){let e=`${t.path}::${t.method}`,n=this.typeBuilder.registerNamedShape(this.typeBuilder.namespaceTopLevelShape(t.shape,t.role),t.name,r,t.collisionSuffix),a=i.get(e)??{response:`Record<string, never>`,responseExample:`unknown`,input:`Record<string, never>`,query:`Record<string, never>`,header:`Record<string, never>`,params:`Record<string, never>`};a[t.role]=n,i.set(e,a)}let a=r.declarations.map(e=>this.moduleRenderer.renderDeclaration(e)).join(`
|
|
140
140
|
|
|
141
141
|
`),o=this.moduleRenderer.toCamelCase(t),s=this.typeBuilder.buildSdkManifest(e,i,n);return[a,this.moduleRenderer.renderOpenApiDocumentDefinitions(t,e,i),this.moduleRenderer.renderSdkApiInterface(t,s),this.moduleRenderer.renderSdkManifest(o,s),`export const ${o}: ${t} = ${this.moduleRenderer.renderOpenApiDocumentValue(e)}`,this.moduleRenderer.renderSdkBundle(o,t),``,`export default ${o}`].filter(Boolean).join(`
|
|
142
142
|
|
|
@@ -144,7 +144,7 @@ import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"a
|
|
|
144
144
|
|
|
145
145
|
`),o,`export const ${s}: ${t} = ${this.moduleRenderer.renderValue(e)}`,``,`export default ${s}`].filter(Boolean).join(`
|
|
146
146
|
|
|
147
|
-
`)}isOpenApiDocumentLike(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e;if(typeof t.info!=`object`||t.info===null||Array.isArray(t.info))return!1;let n=t.info;return t.openapi===`3.1.0`&&typeof n.title==`string`&&typeof n.version==`string`&&typeof t.paths==`object`&&t.paths!==null&&!Array.isArray(t.paths)}},Q=class{static serializeOutput=async(e,t,n=`ExtractedApiDocument`,r={})=>t===`js`?l.format(`export default ${JSON.stringify(e,null,2)}`,{parser:`babel`,semi:!1,singleQuote:!0}):t===`ts`?l.format(Z.generateModule(e,n,r),{parser:`typescript`,semi:!1,singleQuote:!0}):JSON.stringify(e,null,t===`json`?0:2);static buildFilePath=(e,t,n,r)=>{let a={pretty:`txt`,json:`json`,js:`js`,ts:`ts`}[r],o=this.toSafeSourceName(t),s=n===`openapi`?`.openapi`:``;return i.join(e,`output`,`${o||`output`}${s}.${a}`)};static buildArtifactDirectory=(e,t,n)=>{let r=this.toSafeSourceName(t);return i.join(e,`output`,`${r||n}.${n}`)};static toSafeSourceName(e){return e.replace(/[^a-zA-Z0-9_-]+/g,`_`).replace(/^_+|_+$/g,``)}static getRootTypeName=e=>e===`openapi`?`ExtractedApiDocument`:`ExtractedPayload`},
|
|
147
|
+
`)}isOpenApiDocumentLike(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e;if(typeof t.info!=`object`||t.info===null||Array.isArray(t.info))return!1;let n=t.info;return t.openapi===`3.1.0`&&typeof n.title==`string`&&typeof n.version==`string`&&typeof t.paths==`object`&&t.paths!==null&&!Array.isArray(t.paths)}},Q=class{static serializeOutput=async(e,t,n=`ExtractedApiDocument`,r={})=>t===`js`?l.format(`export default ${JSON.stringify(e,null,2)}`,{parser:`babel`,semi:!1,singleQuote:!0}):t===`ts`?l.format(Z.generateModule(e,n,r),{parser:`typescript`,semi:!1,singleQuote:!0}):JSON.stringify(e,null,t===`json`?0:2);static buildFilePath=(e,t,n,r)=>{let a={pretty:`txt`,json:`json`,js:`js`,ts:`ts`}[r],o=this.toSafeSourceName(t),s=n===`openapi`?`.openapi`:``;return i.join(e,`output`,`${o||`output`}${s}.${a}`)};static buildArtifactDirectory=(e,t,n)=>{let r=this.toSafeSourceName(t);return i.join(e,`output`,`${r||n}.${n}`)};static toSafeSourceName(e){return e.replace(/[^a-zA-Z0-9_-]+/g,`_`).replace(/^_+|_+$/g,``)}static getRootTypeName=e=>e===`openapi`?`ExtractedApiDocument`:`ExtractedPayload`},Ze=class{typeBuilder=new X;typeScriptGenerator=new Z;generate(e,t={}){let n=t.outputMode??`both`,r=t.signatureStyle??`grouped`,i=t.rootTypeName??`ExtractedApiDocument`,a=t.schemaModule??this.typeScriptGenerator.generateModule(e,i,t),o=this.createOperationTypeRefs(e),s=this.typeBuilder.buildSdkManifest(e,o,t),c=s.groups.map(e=>e.className),l={"package.json":this.renderPackageJson(t),"README.md":this.renderReadme(s,t,n,r),"src/Schema.ts":a,"src/index.ts":this.renderIndexFile(c,n,i,s),"tsconfig.json":this.renderTsconfig(),"tsdown.config.ts":this.renderTsdownConfig(),"vitest.config.ts":this.renderVitestConfig(),"tests/exports.test.ts":this.renderExportsTest(i,n)};if(n!==`runtime`){l[`src/BaseApi.ts`]=this.renderBaseApi(),l[`src/ApiBinder.ts`]=this.renderApiBinder(s);for(let e of s.groups)l[`src/Apis/${e.className}.ts`]=this.renderApiClass(e,r);l[`src/Core.ts`]=this.renderCoreFile()}return l}createOperationTypeRefs(e){let t=this.typeBuilder.createContext(),n=new Map;for(let r of this.typeBuilder.collectSemanticModels(e)){let e=`${r.path}::${r.method}`,i=this.typeBuilder.registerNamedShape(this.typeBuilder.namespaceTopLevelShape(r.shape,r.role),r.name,t,r.collisionSuffix),a=n.get(e)??{response:`Record<string, never>`,responseExample:`unknown`,input:`Record<string, never>`,query:`Record<string, never>`,header:`Record<string, never>`,params:`Record<string, never>`};a[r.role]=i,n.set(e,a)}return n}renderBaseApi(){return[`import { BaseApi as KitBaseApi } from '@oapiex/sdk-kit'`,``,`export class BaseApi extends KitBaseApi {}`].join(`
|
|
148
148
|
`)}renderApiBinder(e){return[`import { BaseApi } from './BaseApi'`,``,...e.groups.map(e=>`import { ${e.className} } from './Apis/${e.className}'`),``,`export class ApiBinder extends BaseApi {`,...e.groups.map(e=>` ${e.propertyName}!: ${e.className}`),``,` protected override boot () {`,...e.groups.map(e=>` this.${e.propertyName} = new ${e.className}(this.core)`),` }`,`}`].join(`
|
|
149
149
|
`)}renderApiClass(e,t){let n=this.createTypeImportContext(e,t),r=[`import { BaseApi } from '../BaseApi'`,`import { Http } from '@oapiex/sdk-kit'`];return n.specifiers.length>0&&r.splice(1,0,`import type { ${n.specifiers.join(`, `)} } from '../Schema'`),[...r,``,`export class ${e.className} extends BaseApi {`,``,...e.operations.flatMap(e=>[this.renderApiMethod(e,t,n.aliasMap),``]).slice(0,-1),`}`].join(`
|
|
150
150
|
`)}renderApiMethod(e,t,n){let r=t===`flat`?this.renderFlatSignature(e,n):this.renderGroupedSignature(e,n),i=t===`flat`?this.renderFlatObjectLiteral(e.paramsType,e.pathParams):e.pathParams.length>0?`params`:`{}`,a=t===`flat`?this.renderFlatObjectLiteral(e.queryType,e.queryParams):e.queryParams.length>0?`query`:`{}`,o=t===`flat`?this.renderFlatHeaders(e):e.headerParams.length>0?`((headers ? { ...headers } : {}) as Record<string, string | undefined>)`:`{}`,s=t===`flat`?e.hasBody?`body`:`{}`:e.hasBody?`body ?? {}`:`{}`,c=this.renderMethodDocComment(e,t,n);return[...c?[c]:[],` async ${e.methodName} ${r}: Promise<${this.rewriteTypeReference(e.responseType,n)}> {`,` await this.core.validateAccess()`,``,` const { data } = await Http.send<${this.rewriteTypeReference(e.responseType,n)}>(`,` this.core.builder.buildTargetUrl('${e.path}', ${i}, ${a}),`,` '${e.method}',`,` ${s},`,` ${o}`,` )`,``,` return data`,` }`].join(`
|
|
@@ -160,7 +160,7 @@ import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"a
|
|
|
160
160
|
`)}renderVitestConfig(){return[`import { defineConfig } from 'vitest/config'`,``,`export default defineConfig({`,` test: {`,` name: 'generated-sdk',`,` environment: 'node',`,` include: ['tests/*.{test,spec}.?(c|m)[jt]s?(x)'],`,` },`,`})`].join(`
|
|
161
161
|
`)}renderExportsTest(e,t){let n=`${e.charAt(0).toLowerCase()}${e.slice(1)}`,r=[` expect(sdk.createClient).toBeTypeOf('function')`,` expect(sdk.createSdk).toBeTypeOf('function')`,` expect(sdk.${n}Sdk).toBeDefined()`,` expect(sdk.${n}Manifest).toBeDefined()`];return t!==`runtime`&&(r.unshift(` expect(sdk.Core).toBeTypeOf('function')`),r.unshift(` expect(sdk.BaseApi).toBeTypeOf('function')`)),[`import { describe, expect, it } from 'vitest'`,``,`import * as sdk from '../src/index'`,``,`describe('generated sdk exports', () => {`,` it('exposes the generated schema and runtime helpers', () => {`,...r,` })`,`})`].join(`
|
|
162
162
|
`)}renderIndexFile(e,t,n,r){let i=`${n.charAt(0).toLowerCase()}${n.slice(1)}`,a=[`import type { ${n}Api } from './Schema'`,`import { ${i}Manifest, ${i}Sdk } from './Schema'`,`import { createSdk as createBoundSdk } from '@oapiex/sdk-kit'`,`import type { AuthConfig, BaseApi as KitBaseApi, Core as KitCore, InitOptions } from '@oapiex/sdk-kit'`,``,`export * from './Schema'`];if(t!==`runtime`&&(a.push(`export { ApiBinder } from './ApiBinder'`),a.push(`export { BaseApi } from './BaseApi'`),a.push(...e.map(e=>`export { ${e} as ${e}Api } from './Apis/${e}'`)),a.push(`export { Core } from './Core'`)),a.push(``),a.push(`export const securitySchemes = ${i}Manifest.securitySchemes`),a.push(`export const security = ${i}Manifest.security`),r.securitySchemes.length>0){a.push(``);for(let e of r.securitySchemes)a.push(...this.renderSecurityHelper(e)),a.push(``)}return a.push(`export const createClient = (`),a.push(` options: InitOptions`),a.push(`): KitCore & { api: KitBaseApi & ${n}Api } =>`),a.push(` createBoundSdk(${i}Sdk, options) as KitCore & { api: KitBaseApi & ${n}Api }`),a.push(``),a.push(`export {`),a.push(` BadRequestException,`),a.push(` Builder,`),a.push(` ForbiddenRequestException,`),a.push(` Http,`),a.push(` HttpException,`),a.push(` UnauthorizedRequestException,`),a.push(` WebhookValidator,`),a.push(` createSdk,`),a.push(`} from '@oapiex/sdk-kit'`),a.push(``),a.push(`export type {`),a.push(` AuthConfig,`),a.push(` InitOptions,`),a.push(` UnifiedResponse,`),a.push(` XGenericObject,`),a.push(`} from '@oapiex/sdk-kit'`),a.join(`
|
|
163
|
-
`)}collectTypeIdentifiers(e){return Array.from(new Set((e.match(/\b[A-Z][A-Za-z0-9_]*/g)??[]).filter(e=>![`Record`,`Promise`].includes(e))))}renderSecurityHelper(e){return e.authType===`basic`?[`export const ${e.helperName} = (username: string, password: string): AuthConfig => ({`,` type: 'basic',`,` username,`,` password,`,`})`]:e.authType===`apiKey`?[`export const ${e.helperName} = (value: string): AuthConfig => ({`,` type: 'apiKey',`,` name: ${JSON.stringify(e.parameterName??e.name)},`,` value,`,` in: ${JSON.stringify(e.in??`header`)},`,`})`]:e.authType===`oauth2`?[`export const ${e.helperName} = (accessToken: string, tokenType = 'Bearer'): AuthConfig => ({`,` type: 'oauth2',`,` accessToken,`,` tokenType,`,`})`]:[`export const ${e.helperName} = (token: string): AuthConfig => ({`,` type: 'bearer',`,` token,`,...e.scheme&&e.scheme.toLowerCase()!==`bearer`?[` prefix: ${JSON.stringify(this.normalizeHttpAuthPrefix(e.scheme))},`]:[],`})`]}createSecurityHelperName(e){let t=this.typeBuilder.sanitizeTypeName(e);return t.endsWith(`Auth`)?`create${t}`:`create${t}Auth`}normalizeHttpAuthPrefix(e){return e.charAt(0).toUpperCase()+e.slice(1)}toConstantCase(e){return e.replace(/([a-z0-9])([A-Z])/g,`$1_$2`).replace(/[^A-Za-z0-9]+/g,`_`).replace(/^_+|_+$/g,``).toUpperCase()||`AUTH`}};const $=new class{createDocument(e,t=`Extracted API`,n=`0.0.0`){let r={};for(let t of e){let e=this.transformOperation(t);!e||this.shouldSkipNormalizedOperation(e)||(r[e.path]??={},r[e.path][e.method]=e.operation)}return{openapi:`3.1.0`,info:{title:t,version:n},paths:r}}transformOperation(e){if(!e.method||!e.url)return null;let t=new URL(e.url);if(this.shouldSkipPlaceholderOperation(t,e))return null;let n=e.method.toLowerCase(),r=this.decodeOpenApiPathname(t.pathname);return{path:r,method:n,operation:{summary:e.sidebarLinks.find(e=>e.active)?.label,description:e.description??void 0,operationId:this.buildOperationId(n,r),parameters:this.createParameters(e.requestParams),requestBody:this.createRequestBody(e.requestParams,e.requestExampleNormalized?.body,this.hasExtractedBodyParams(e.requestParams)?null:this.resolveFallbackRequestBodyExample(e)),responses:this.createResponses(e.responseSchemas,e.responseBodies)}}}shouldSkipNormalizedOperation(e){return e.path===`/`&&e.method===`get`&&e.operation.operationId===`get`&&Object.keys(e.operation.responses).length===0}shouldSkipPlaceholderOperation(e,t){return e.hostname!==`example.com`||e.pathname!==`/`?!1:t.requestParams.length===0&&t.responseSchemas.length===0&&t.responseBodies.length===0&&t.requestExampleNormalized?.url===`https://example.com/`}decodeOpenApiPathname(e){return e.split(`/`).map(e=>{if(!e)return e;try{return decodeURIComponent(e)}catch{return e}}).join(`/`)}hasExtractedBodyParams(e){return e.some(e=>e.in===`body`||e.in===null)}createParameters(e){let t=e.filter(e=>this.isOpenApiParameterLocation(e.in)).map(e=>this.createParameter(e));return t.length>0?t:void 0}createRequestBody(e,t,n){let r=e.filter(e=>e.in===`body`||e.in===null);if(r.length===0&&t==null)return;let i=this.buildRequestBodySchema(r,t,n);return{required:r.length>0?r.some(e=>e.required):!1,content:{"application/json":{schema:i,...t==null?{}:{example:t}}}}}buildRequestBodySchema(e,t,n){let r=this.mergeOpenApiSchemas(this.createExampleSchema(t),this.createExampleSchema(n))??{type:`object`};t==null?n!=null&&(r.example=n):r.example=t;for(let t of e)this.insertRequestBodyParam(r,t);return r}inferSchemaFromExample(e){if(Array.isArray(e))return{type:`array`,items:this.inferSchemaFromExample(e[0])??{},example:e};if(
|
|
163
|
+
`)}collectTypeIdentifiers(e){return Array.from(new Set((e.match(/\b[A-Z][A-Za-z0-9_]*/g)??[]).filter(e=>![`Record`,`Promise`].includes(e))))}renderSecurityHelper(e){return e.authType===`basic`?[`export const ${e.helperName} = (username: string, password: string): AuthConfig => ({`,` type: 'basic',`,` username,`,` password,`,`})`]:e.authType===`apiKey`?[`export const ${e.helperName} = (value: string): AuthConfig => ({`,` type: 'apiKey',`,` name: ${JSON.stringify(e.parameterName??e.name)},`,` value,`,` in: ${JSON.stringify(e.in??`header`)},`,`})`]:e.authType===`oauth2`?[`export const ${e.helperName} = (accessToken: string, tokenType = 'Bearer'): AuthConfig => ({`,` type: 'oauth2',`,` accessToken,`,` tokenType,`,`})`]:[`export const ${e.helperName} = (token: string): AuthConfig => ({`,` type: 'bearer',`,` token,`,...e.scheme&&e.scheme.toLowerCase()!==`bearer`?[` prefix: ${JSON.stringify(this.normalizeHttpAuthPrefix(e.scheme))},`]:[],`})`]}createSecurityHelperName(e){let t=this.typeBuilder.sanitizeTypeName(e);return t.endsWith(`Auth`)?`create${t}`:`create${t}Auth`}normalizeHttpAuthPrefix(e){return e.charAt(0).toUpperCase()+e.slice(1)}toConstantCase(e){return e.replace(/([a-z0-9])([A-Z])/g,`$1_$2`).replace(/[^A-Za-z0-9]+/g,`_`).replace(/^_+|_+$/g,``).toUpperCase()||`AUTH`}};const $=new class{createDocument(e,t=`Extracted API`,n=`0.0.0`){let r={};for(let t of e){let e=this.transformOperation(t);!e||this.shouldSkipNormalizedOperation(e)||(r[e.path]??={},r[e.path][e.method]=e.operation)}return{openapi:`3.1.0`,info:{title:t,version:n},paths:r}}transformOperation(e){if(!e.method||!e.url)return null;let t=new URL(e.url);if(this.shouldSkipPlaceholderOperation(t,e))return null;let n=e.method.toLowerCase(),r=this.decodeOpenApiPathname(t.pathname);return{path:r,method:n,operation:{summary:e.sidebarLinks.find(e=>e.active)?.label,description:e.description??void 0,operationId:this.buildOperationId(n,r),parameters:this.createParameters(e.requestParams),requestBody:this.createRequestBody(e.requestParams,e.requestExampleNormalized?.body,this.hasExtractedBodyParams(e.requestParams)?null:this.resolveFallbackRequestBodyExample(e)),responses:this.createResponses(e.responseSchemas,e.responseBodies)}}}shouldSkipNormalizedOperation(e){return e.path===`/`&&e.method===`get`&&e.operation.operationId===`get`&&Object.keys(e.operation.responses).length===0}shouldSkipPlaceholderOperation(e,t){return e.hostname!==`example.com`||e.pathname!==`/`?!1:t.requestParams.length===0&&t.responseSchemas.length===0&&t.responseBodies.length===0&&t.requestExampleNormalized?.url===`https://example.com/`}decodeOpenApiPathname(e){return e.split(`/`).map(e=>{if(!e)return e;try{return decodeURIComponent(e)}catch{return e}}).join(`/`)}hasExtractedBodyParams(e){return e.some(e=>e.in===`body`||e.in===null)}createParameters(e){let t=e.filter(e=>this.isOpenApiParameterLocation(e.in)).map(e=>this.createParameter(e));return t.length>0?t:void 0}createRequestBody(e,t,n){let r=e.filter(e=>e.in===`body`||e.in===null);if(r.length===0&&t==null)return;let i=this.buildRequestBodySchema(r,t,n);return{required:r.length>0?r.some(e=>e.required):!1,content:{"application/json":{schema:i,...t==null?{}:{example:t}}}}}buildRequestBodySchema(e,t,n){let r=this.mergeOpenApiSchemas(this.createExampleSchema(t),this.createExampleSchema(n))??{type:`object`};t==null?n!=null&&(r.example=n):r.example=t;for(let t of e)this.insertRequestBodyParam(r,t);return r}inferSchemaFromExample(e){if(Array.isArray(e))return{type:`array`,items:this.inferSchemaFromExample(e[0])??{},example:e};if(N(e))return{type:`object`,properties:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,this.inferSchemaFromExample(t)??{}])),example:e};if(typeof e==`string`)return{type:`string`,example:e};if(typeof e==`number`)return{type:Number.isInteger(e)?`integer`:`number`,example:e};if(typeof e==`boolean`)return{type:`boolean`,example:e};if(e===null)return{}}insertRequestBodyParam(e,t){let n=t.path.length>0?t.path:[t.name],r=e;for(let[e,i]of n.slice(0,-1).entries())r.properties??={},r.properties[i]??={type:`object`},t.required&&(r.required=Array.from(new Set([...r.required??[],i]))),r=r.properties[i],r.type??=`object`,e===n.length-2&&t.required&&(r.required??=[]);let i=n[n.length-1]??t.name;r.properties??={},r.properties[i]=this.createParameterSchema(t),t.required&&(r.required=Array.from(new Set([...r.required??[],i])))}createParameter(e){return{name:e.name,in:e.in,required:e.in===`path`?!0:e.required,description:e.description??void 0,schema:this.createParameterSchema(e),example:e.defaultValue??void 0}}createParameterSchema(e){return{type:e.type??void 0,description:e.description??void 0,default:e.defaultValue??void 0}}createResponses(e,t){let n={};for(let r of e){if(!r.statusCode)continue;let e=t.filter(e=>e.statusCode===r.statusCode),i=this.createResponseContent(e);n[r.statusCode]={description:r.description??r.statusCode,...i?{content:i}:{}}}for(let e of t){if(!e.statusCode||n[e.statusCode])continue;let t=this.createResponseContent([e]);n[e.statusCode]={description:e.label??e.statusCode,...t?{content:t}:{}}}return n}createResponseContent(e){if(e.length===0)return;let t={};for(let n of e){let e=n.contentType??(n.format===`json`?`application/json`:`text/plain`),r=this.normalizeResponseExampleValue(n.body,n.format);t[e]={schema:this.inferSchemaFromBody(r,n.format),example:r}}return t}inferSchemaFromBody(e,t){if(t===`json`)return this.inferSchemaFromExample(e);if(t===`text`)return{type:`string`,example:e}}normalizeResponseExampleValue(e,t){return t!==`json`||typeof e!=`string`?e:M.parsePossiblyTruncated(e)??G(e)??e}resolveFallbackRequestBodyExample(e){return e.responseBodies.find(e=>e.format===`json`)?.body??(typeof e.responseExample==`object`&&e.responseExample!==null?e.responseExample:typeof e.responseExampleRaw==`string`?M.parsePossiblyTruncated(e.responseExampleRaw):typeof e.responseExample==`string`?M.parsePossiblyTruncated(e.responseExample):null)}createExampleSchema(e){return e==null?null:this.inferSchemaFromExample(e)??null}mergeOpenApiSchemas(e,t){if(!e)return t;if(!t)return e;let n={...t,...e,...e.type||t.type?{type:e.type??t.type}:{},...e.description||t.description?{description:e.description??t.description}:{},...e.default!==void 0||t.default!==void 0?{default:e.default??t.default}:{},...e.example!==void 0||t.example!==void 0?{example:e.example??t.example}:{}};if(e.properties||t.properties){let r=new Set([...Object.keys(e.properties??{}),...Object.keys(t.properties??{})]);n.properties=Object.fromEntries(Array.from(r).map(n=>[n,this.mergeOpenApiSchemas(e.properties?.[n]??null,t.properties?.[n]??null)??{}]))}return(e.items||t.items)&&(n.items=this.mergeOpenApiSchemas(e.items??null,t.items??null)??{}),(e.required||t.required)&&(n.required=Array.from(new Set([...t.required??[],...e.required??[]]))),n}buildOperationId(e,t){return`${e}${t.replace(/\{([^}]+)\}/g,`$1`).split(`/`).filter(Boolean).map(e=>e.replace(/[^a-zA-Z0-9]+/g,` `)).map(e=>e.trim()).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1).replace(/\s+(.)/g,(e,t)=>t.toUpperCase())).join(``)}`}isOpenApiParameterLocation(e){return e===`query`||e===`header`||e===`path`||e===`cookie`}};var Qe=class extends s{signature=`generate
|
|
164
164
|
{artifact : Artifact to generate [sdk]}
|
|
165
165
|
{source? : Documentation URL/local source or parsed TypeScript output file}
|
|
166
166
|
{--d|dir? : Output directory for the generated artifact}
|
|
@@ -174,12 +174,12 @@ import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"a
|
|
|
174
174
|
{--t|timeout? : Request/browser timeout in milliseconds}
|
|
175
175
|
{--c|crawl : Crawl sidebar links and parse every discovered operation}
|
|
176
176
|
{--b|base-url? : Base URL used to resolve sidebar links when crawling from a local file}
|
|
177
|
-
`;description=`Generate artifacts such as SDK packages from documentation sources or parsed TypeScript outputs`;async handle(){let e=this.app.getConfig(),t=String(this.argument(`artifact`,``)).trim().toLowerCase(),n=String(this.argument(`source`,``)).trim(),i=String(this.option(`browser`,e.browser)).trim().toLowerCase(),a=String(this.option(`timeout`,``)).trim(),o=this.option(`crawl`),s=String(this.option(`baseUrl`,``)).trim()||null,c=await this.resolveOutputDirectory(n),l=this.spinner(`Generating ${t} artifact...`).start(),u=!1;try{let d=Date.now();if(!
|
|
177
|
+
`;description=`Generate artifacts such as SDK packages from documentation sources or parsed TypeScript outputs`;async handle(){let e=this.app.getConfig(),t=String(this.argument(`artifact`,``)).trim().toLowerCase(),n=String(this.argument(`source`,``)).trim(),i=String(this.option(`browser`,e.browser)).trim().toLowerCase(),a=String(this.option(`timeout`,``)).trim(),o=this.option(`crawl`),s=String(this.option(`baseUrl`,``)).trim()||null,c=await this.resolveOutputDirectory(n),l=this.spinner(`Generating ${t} artifact...`).start(),u=!1;try{let d=Date.now();if(!j(i))throw Error(`Unsupported browser: ${i}`);if(t!==`sdk`)throw Error(`Unsupported artifact: ${t}`);if(!n)throw Error(`The sdk artifact requires a source argument`);if(!this.isTypeScriptArtifactSource(n)&&!j(i))throw Error(`Unsupported browser: ${i}`);let f=this.resolveTimeoutOverride(a,e.requestTimeout),p=this.parseNamespaceStrategy(this.option(`namespaceStrategy`,`smart`)),m=this.parseMethodStrategy(this.option(`methodStrategy`,`smart`)),h=this.parseOutputMode(this.option(`outputMode`,`both`)),g=this.parseSignatureStyle(this.option(`signatureStyle`,`grouped`)),_=String(this.option(`rootTypeName`,`ExtractedApiDocument`)).trim()||`ExtractedApiDocument`;this.app.configure({browser:i,requestTimeout:f}),!this.isTypeScriptArtifactSource(n)&&o&&(await D(this.app.getConfig()),u=!0);let v=await this.resolveSdkSource({source:n,crawl:o,baseUrl:s,rootTypeName:_,namespaceStrategy:p,methodStrategy:m}),y=this.resolvePackageName(c),b=new Ze().generate(v.document,{outputMode:h,signatureStyle:g,rootTypeName:_,namespaceStrategy:p,methodStrategy:m,schemaModule:v.schemaModule,packageName:String(this.option(`name`,``)).trim()||y});await this.writePackageFiles(c,b);let x=Date.now()-d;r.twoColumnDetail(r.log([[`Generated`,`green`],[`${x/1e3}s`,`gray`]],` `,!1),c.replace(process.cwd(),`.`)),l.succeed(`Artifact generation completed`)}catch(e){let t=e instanceof Error?e.message:`Unknown error`;l.fail(`Failed to generate artifact: ${t}`),process.exitCode=1}finally{u&&await O()}}async resolveSdkSource(e){if(this.isTypeScriptArtifactSource(e.source))return this.loadSdkSourceFromTypeScriptArtifact(e.source);let t=P(await this.app.loadHtmlSource(e.source,!0)),n=e.crawl?await this.app.crawlReadmeOperations(e.source,t,e.baseUrl):t,r=this.buildOpenApiPayload(n);return{document:r,schemaModule:await l.format(Z.generateModule(r,e.rootTypeName,{namespaceStrategy:e.namespaceStrategy,methodStrategy:e.methodStrategy}),{parser:`typescript`,semi:!1,singleQuote:!0})}}async loadSdkSourceFromTypeScriptArtifact(e){let t=i.resolve(process.cwd(),e),n=await a.readFile(t,`utf8`),r=await import(`${u(t).href}?t=${Date.now()}`),o=r.default??Object.values(r).find(e=>this.isOpenApiDocumentLike(e));if(!this.isOpenApiDocumentLike(o))throw Error(`The provided TypeScript source does not export an OpenAPI document`);return{document:o,schemaModule:n}}buildOpenApiPayload(e){return`operations`in e?$.createDocument(e.operations,`Extracted API`,`0.0.0`):$.createDocument([e],`Extracted API`,`0.0.0`)}resolveTimeoutOverride(e,t){if(!e)return t;let n=Number(e);if(!Number.isFinite(n)||n<=0)throw Error(`Invalid timeout override: ${e}`);return n}async resolveOutputDirectory(e,t){t??=String(this.option(`dir`,``)).trim();let n=t?i.resolve(process.cwd(),t):Q.buildArtifactDirectory(process.cwd(),e,`sdk`);if(d(n)&&f(n).length>0)switch(await this.choice(`Output directory (${t}) already exists and is not empty, what would you like to do?`,[{name:`Overwrite`,value:`overwrite`},{name:`Try to merge`,value:`merge`},{name:`Choose a different directory`,value:`choose`},{name:`Cancel`,value:`cancel`}])){case`overwrite`:await a.rm(n,{recursive:!0,force:!0});break;case`choose`:{let n=await this.ask(`Please enter a new output directory (relative to current directory):`,t);return this.resolveOutputDirectory(e,n)}case`cancel`:return this.info(`Operation cancelled by user`),process.exit(0);default:break}return n}resolvePackageName(e){return String(this.option(`name`,``)).trim()||i.basename(e).replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`generated-sdk`}parseOutputMode(e){let t=String(e??`both`).trim().toLowerCase();if(t===`runtime`||t===`classes`||t===`both`)return t;throw Error(`Unsupported sdk output mode: ${t}`)}parseSignatureStyle(e){let t=String(e??`grouped`).trim().toLowerCase();if(t===`flat`||t===`grouped`)return t;throw Error(`Unsupported signature style: ${t}`)}parseNamespaceStrategy(e){let t=String(e??`smart`).trim().toLowerCase();if(t===`smart`||t===`scoped`)return t;throw Error(`Unsupported namespace strategy: ${t}`)}parseMethodStrategy(e){let t=String(e??`smart`).trim().toLowerCase();if(t===`smart`||t===`operation-id`)return t;throw Error(`Unsupported method strategy: ${t}`)}isTypeScriptArtifactSource(e){return/\.(?:[cm]?ts|[cm]?js)$/i.test(e)}isOpenApiDocumentLike(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e,n=t.info;return t.openapi===`3.1.0`&&typeof n==`object`&&!!n&&!Array.isArray(n)&&typeof n.title==`string`&&typeof n.version==`string`&&typeof t.paths==`object`&&t.paths!==null&&!Array.isArray(t.paths)}async writePackageFiles(e,t){await Promise.all(Object.entries(t).map(async([t,n])=>{let r=i.join(e,t);await a.mkdir(i.dirname(r),{recursive:!0}),await a.writeFile(r,n,`utf8`)}))}};const $e=p(import.meta.url);var et=class extends s{signature=`init
|
|
178
178
|
{--f|force : Overwrite existing config}
|
|
179
179
|
{--p|pkg? : Generate config for another package (e.g. sdk-kit) instead of oapiex [sdk]}
|
|
180
|
-
`;description=`Generate a default oapiex.config.ts in the current directory`;async handle(){let e=process.cwd(),t=i.join(e,`oapiex.config.js`),n=this.option(`force`,!1),r=this.option(`pkg`,`base`).trim().toLowerCase(),o={base:this.buildConfigTemplate(),sdk:this.buildSdkConfigTemplate()};if(![`base`,`sdk`].includes(r))return void this.error(`Invalid package option: ${r}`);try{await a.access(t),n||(this.error(`Config file already exists at ${t}. Use --force to overwrite.`),process.exit(1))}catch{}await a.writeFile(t,o[r],`utf8`),this.line(`Created ${t} `)}buildConfigTemplate(){let e=
|
|
180
|
+
`;description=`Generate a default oapiex.config.ts in the current directory`;async handle(){let e=process.cwd(),t=i.join(e,`oapiex.config.js`),n=this.option(`force`,!1),r=this.option(`pkg`,`base`).trim().toLowerCase(),o={base:this.buildConfigTemplate(),sdk:this.buildSdkConfigTemplate()};if(![`base`,`sdk`].includes(r))return void this.error(`Invalid package option: ${r}`);try{await a.access(t),n||(this.error(`Config file already exists at ${t}. Use --force to overwrite.`),process.exit(1))}catch{}await a.writeFile(t,o[r],`utf8`),this.line(`Created ${t} `)}buildConfigTemplate(){let e=g;return[`import { defineConfig } from '${$e.includes(`node_modules`)?`oapiex`:`./src/Manager`}'`,``,`/**`,` * See https://toneflix.github.io/oapiex/configuration for docs`,` */`,`export default defineConfig({`,` outputFormat: '${e.outputFormat}',`,` outputShape: '${e.outputShape}',`,` browser: '${e.browser}',`,` requestTimeout: ${e.requestTimeout},`,` maxRedirects: ${e.maxRedirects},`,` userAgent: '${e.userAgent}',`,` retryCount: ${e.retryCount},`,` retryDelay: ${e.retryDelay},`,`})`].join(`
|
|
181
181
|
`)}buildSdkConfigTemplate(){return[`import { defineConfig } from '@oapiex/sdk-kit'`,``,`/**`,` * See https://toneflix.github.io/oapiex/configuration for docs`,` */`,`export default defineConfig({`,` environment: 'sandbox',`,` urls: {`,` live: 'https://live.oapiex.com',`,` sandbox: 'https://sandbox.oapiex.com',`,` },`,`})`].join(`
|
|
182
|
-
`)}},
|
|
182
|
+
`)}},tt=class extends s{signature=`parse
|
|
183
183
|
{source : Local HTML file path or remote URL}
|
|
184
184
|
{--O|output=pretty : Output format [pretty,json,js,ts]}
|
|
185
185
|
{--S|shape=raw : Result shape [raw,openapi]}
|
|
@@ -187,4 +187,4 @@ import{JSDOM as e}from"jsdom";import{Window as t}from"happy-dom";import n from"a
|
|
|
187
187
|
{--t|timeout? : Request/browser timeout in milliseconds}
|
|
188
188
|
{--c|crawl : Crawl sidebar links and parse every discovered operation}
|
|
189
189
|
{--b|base-url? : Base URL used to resolve sidebar links when crawling from a local file}
|
|
190
|
-
`;description=`Parse a saved ReadMe page or remote documentation URL and print normalized output`;async handle(){let e=this.app.getConfig(),t=String(this.argument(`source`,``)).trim(),n=String(this.option(`output`,e.outputFormat)).trim().toLowerCase(),i=String(this.option(`shape`,e.outputShape)).trim().toLowerCase(),a=String(this.option(`browser`,e.browser)).trim().toLowerCase(),o=String(this.option(`timeout`,``)).trim(),s=this.option(`crawl`),c=String(this.option(`baseUrl`,``)).trim()||null,l=this.spinner(`${s?`Crawling and p`:`P`}arsing source...`).start(),u=!1;try{let d=Date.now();if(!
|
|
190
|
+
`;description=`Parse a saved ReadMe page or remote documentation URL and print normalized output`;async handle(){let e=this.app.getConfig(),t=String(this.argument(`source`,``)).trim(),n=String(this.option(`output`,e.outputFormat)).trim().toLowerCase(),i=String(this.option(`shape`,e.outputShape)).trim().toLowerCase(),a=String(this.option(`browser`,e.browser)).trim().toLowerCase(),o=String(this.option(`timeout`,``)).trim(),s=this.option(`crawl`),c=String(this.option(`baseUrl`,``)).trim()||null,l=this.spinner(`${s?`Crawling and p`:`P`}arsing source...`).start(),u=!1;try{let d=Date.now();if(!j(a))throw Error(`Unsupported browser: ${a}`);let f=this.resolveTimeoutOverride(o,e.requestTimeout);this.app.configure({browser:a,requestTimeout:f}),s&&(await D(this.app.getConfig()),u=!0);let p=P(await this.app.loadHtmlSource(t,!0)),m=s?await this.app.crawlReadmeOperations(t,p,c):p,h=i===`openapi`?this.buildOpenApiPayload(m):m,g=await Q.serializeOutput(h,n,Q.getRootTypeName(i)),_=await this.saveOutputToFile(g,t,i,n),v=Date.now()-d;r.twoColumnDetail(r.log([[`Output`,`green`],[`${v/1e3}s`,`gray`]],` `,!1),_.replace(process.cwd(),`.`)),l.succeed(`Parsing completed`)}catch(e){let t=e instanceof Error?e.message:`Unknown error`;l.fail(`Failed to parse source: ${t}`),process.exitCode=1}finally{u&&await O()}}resolveTimeoutOverride=(e,t)=>{if(!e)return t;let n=Number(e);if(!Number.isFinite(n)||n<=0)throw Error(`Invalid timeout override: ${e}`);return n};buildOpenApiPayload=e=>`operations`in e?$.createDocument(e.operations,`Extracted API`,`0.0.0`):$.createDocument([e],`Extracted API`,`0.0.0`);saveOutputToFile=async(e,t,n,r)=>{let o=Q.buildFilePath(process.cwd(),t,n,r),s=i.dirname(o);return await a.mkdir(s,{recursive:!0}),await a.writeFile(o,e,`utf8`),o}};const nt=[`oapiex.config.ts`,`oapiex.config.js`,`oapiex.config.cjs`];async function rt(e=process.cwd()){for(let t of nt){let n=i.join(e,t);try{await a.access(n);let e=await import(u(n).href),t=e.default||e.config||e;if(typeof t==`object`&&t)return t}catch{continue}}return null}async function it(e={}){let t=await rt();return{...g,...t,...e,happyDom:{...g.happyDom,...t?.happyDom||{},...e.happyDom||{}}}}const at=new qe(await it());await c.init(at,{name:`OAPIEX`,logo:``,allowRebuilds:!1,packages:[`@h3ravel/musket`],baseCommands:[et,Qe,tt],exceptionHandler(e){console.error(`An unexpected error occurred:`,e),process.exit(1)}});export{};
|
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,11 @@ let node_fs = require("node:fs");
|
|
|
43
43
|
let url = require("url");
|
|
44
44
|
|
|
45
45
|
//#region src/Manager.ts
|
|
46
|
+
const terminationSignals = [
|
|
47
|
+
"SIGINT",
|
|
48
|
+
"SIGTERM",
|
|
49
|
+
"SIGTSTP"
|
|
50
|
+
];
|
|
46
51
|
const supportedBrowsers = [
|
|
47
52
|
"axios",
|
|
48
53
|
"happy-dom",
|
|
@@ -68,7 +73,54 @@ const defaultConfig = {
|
|
|
68
73
|
}
|
|
69
74
|
};
|
|
70
75
|
let globalConfig = defaultConfig;
|
|
76
|
+
let cleanupInProgress = false;
|
|
77
|
+
let terminationHandlersInstalled = false;
|
|
78
|
+
const activeClosers = /* @__PURE__ */ new Set();
|
|
71
79
|
const getBrowserSession = () => globalThis.__oapieBrowserSession;
|
|
80
|
+
const removeTerminationHandlersWhenIdle = () => {
|
|
81
|
+
if (!getBrowserSession() && activeClosers.size === 0) removeTerminationHandlers();
|
|
82
|
+
};
|
|
83
|
+
const ensureTerminationHandlers = () => {
|
|
84
|
+
if (terminationHandlersInstalled) return;
|
|
85
|
+
for (const signal of terminationSignals) process.on(signal, handleTerminationSignal);
|
|
86
|
+
terminationHandlersInstalled = true;
|
|
87
|
+
};
|
|
88
|
+
const removeTerminationHandlers = () => {
|
|
89
|
+
if (!terminationHandlersInstalled) return;
|
|
90
|
+
for (const signal of terminationSignals) process.off(signal, handleTerminationSignal);
|
|
91
|
+
terminationHandlersInstalled = false;
|
|
92
|
+
};
|
|
93
|
+
const registerActiveBrowserCloser = (closer) => {
|
|
94
|
+
ensureTerminationHandlers();
|
|
95
|
+
activeClosers.add(closer);
|
|
96
|
+
return () => {
|
|
97
|
+
activeClosers.delete(closer);
|
|
98
|
+
removeTerminationHandlersWhenIdle();
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const closeActiveBrowserResources = async () => {
|
|
102
|
+
const closers = Array.from(activeClosers).reverse();
|
|
103
|
+
activeClosers.clear();
|
|
104
|
+
for (const closer of closers) await closer();
|
|
105
|
+
await endBrowserSession();
|
|
106
|
+
};
|
|
107
|
+
const handleTerminationSignal = async (signal) => {
|
|
108
|
+
if (cleanupInProgress) return;
|
|
109
|
+
cleanupInProgress = true;
|
|
110
|
+
removeTerminationHandlers();
|
|
111
|
+
try {
|
|
112
|
+
await closeActiveBrowserResources();
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("Failed to close active browser resources:", error);
|
|
115
|
+
} finally {
|
|
116
|
+
cleanupInProgress = false;
|
|
117
|
+
}
|
|
118
|
+
if (signal === "SIGTSTP") {
|
|
119
|
+
process.kill(process.pid, signal);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
123
|
+
};
|
|
72
124
|
const startBrowserSession = async (config = globalConfig) => {
|
|
73
125
|
const activeSession = getBrowserSession();
|
|
74
126
|
if (activeSession?.browser === config.browser) return activeSession;
|
|
@@ -81,6 +133,7 @@ const startBrowserSession = async (config = globalConfig) => {
|
|
|
81
133
|
headless: config.puppeteer?.headless ?? true,
|
|
82
134
|
args: config.puppeteer?.args ?? ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
83
135
|
});
|
|
136
|
+
ensureTerminationHandlers();
|
|
84
137
|
globalThis.__oapieBrowserSession = nextSession;
|
|
85
138
|
return nextSession;
|
|
86
139
|
};
|
|
@@ -90,6 +143,7 @@ const endBrowserSession = async () => {
|
|
|
90
143
|
globalThis.__oapieBrowserSession = void 0;
|
|
91
144
|
for (const closer of activeSession.closers.reverse()) await closer();
|
|
92
145
|
if (activeSession.puppeteerBrowser) await activeSession.puppeteerBrowser.close();
|
|
146
|
+
removeTerminationHandlersWhenIdle();
|
|
93
147
|
};
|
|
94
148
|
const registerDeferredCloser = (browserName, closer) => {
|
|
95
149
|
const activeSession = getBrowserSession();
|
|
@@ -136,17 +190,23 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
136
190
|
innerHeight: 768,
|
|
137
191
|
settings: config.happyDom
|
|
138
192
|
});
|
|
139
|
-
window.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
193
|
+
const unregisterWindowCloser = registerActiveBrowserCloser(() => window.happyDOM.close());
|
|
194
|
+
try {
|
|
195
|
+
window.document.write(data);
|
|
196
|
+
await window.happyDOM.waitUntilComplete();
|
|
197
|
+
const html = window.document.documentElement.outerHTML;
|
|
198
|
+
if (!html) {
|
|
199
|
+
await window.happyDOM.close();
|
|
200
|
+
throw new Error(`Unable to extract HTML from remote source: ${source}`);
|
|
201
|
+
}
|
|
202
|
+
if (!registerDeferredCloser("happy-dom", () => window.happyDOM.close())) await window.happyDOM.close();
|
|
203
|
+
return html;
|
|
204
|
+
} finally {
|
|
205
|
+
unregisterWindowCloser();
|
|
206
|
+
}
|
|
148
207
|
} else if (config.browser === "jsdom") {
|
|
149
208
|
let window;
|
|
209
|
+
let unregisterWindowCloser;
|
|
150
210
|
try {
|
|
151
211
|
({window} = new jsdom.JSDOM(data, {
|
|
152
212
|
url: source,
|
|
@@ -154,6 +214,7 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
154
214
|
runScripts: "dangerously",
|
|
155
215
|
includeNodeLocations: true
|
|
156
216
|
}));
|
|
217
|
+
unregisterWindowCloser = registerActiveBrowserCloser(() => window?.close());
|
|
157
218
|
const html = window.document.documentElement.outerHTML;
|
|
158
219
|
if (!html) throw new Error(`Unable to extract HTML from remote source: ${source}`);
|
|
159
220
|
const currentWindow = window;
|
|
@@ -163,6 +224,7 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
163
224
|
} else window = void 0;
|
|
164
225
|
return html;
|
|
165
226
|
} finally {
|
|
227
|
+
unregisterWindowCloser?.();
|
|
166
228
|
if (window) window.close();
|
|
167
229
|
}
|
|
168
230
|
} else if (config.browser === "puppeteer") {
|
|
@@ -171,6 +233,8 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
171
233
|
let browserInstance = activeSession?.browser === "puppeteer" ? activeSession.puppeteerBrowser : void 0;
|
|
172
234
|
let shouldClose = false;
|
|
173
235
|
let page;
|
|
236
|
+
let unregisterBrowserCloser;
|
|
237
|
+
let unregisterPageCloser;
|
|
174
238
|
try {
|
|
175
239
|
if (!browserInstance) {
|
|
176
240
|
browserInstance = await (await import("puppeteer")).launch({
|
|
@@ -178,8 +242,14 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
178
242
|
args: config.puppeteer?.args ?? ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
179
243
|
});
|
|
180
244
|
shouldClose = true;
|
|
245
|
+
unregisterBrowserCloser = registerActiveBrowserCloser(async () => {
|
|
246
|
+
if (browserInstance?.connected) await browserInstance.close();
|
|
247
|
+
});
|
|
181
248
|
}
|
|
182
249
|
page = await browserInstance.newPage();
|
|
250
|
+
unregisterPageCloser = registerActiveBrowserCloser(async () => {
|
|
251
|
+
if (page && !page.isClosed()) await page.close();
|
|
252
|
+
});
|
|
183
253
|
await page.setUserAgent({ userAgent: config.userAgent });
|
|
184
254
|
await page.setRequestInterception(true);
|
|
185
255
|
page.on("request", (e) => {
|
|
@@ -215,7 +285,9 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
215
285
|
}
|
|
216
286
|
return html;
|
|
217
287
|
} finally {
|
|
288
|
+
unregisterPageCloser?.();
|
|
218
289
|
if (page && !page.isClosed()) await page.close();
|
|
290
|
+
unregisterBrowserCloser?.();
|
|
219
291
|
if (shouldClose && browserInstance) await browserInstance.close();
|
|
220
292
|
}
|
|
221
293
|
} else throw new Error(`Unsupported browser specified in configuration: ${globalConfig.browser}`);
|
|
@@ -1366,14 +1438,32 @@ var TypeScriptModuleRenderer = class {
|
|
|
1366
1438
|
};
|
|
1367
1439
|
|
|
1368
1440
|
//#endregion
|
|
1369
|
-
//#region src/generator/
|
|
1370
|
-
var
|
|
1441
|
+
//#region src/generator/NamingSupport.ts
|
|
1442
|
+
var NamingSupport = class NamingSupport {
|
|
1371
1443
|
static contextualTailSegments = new Set([
|
|
1372
1444
|
"history",
|
|
1373
1445
|
"status",
|
|
1374
1446
|
"detail",
|
|
1375
1447
|
"details"
|
|
1376
1448
|
]);
|
|
1449
|
+
static irregularSingulars = new Map([
|
|
1450
|
+
["analyses", "analysis"],
|
|
1451
|
+
["diagnoses", "diagnosis"],
|
|
1452
|
+
["parentheses", "parenthesis"],
|
|
1453
|
+
["statuses", "status"],
|
|
1454
|
+
["synopses", "synopsis"],
|
|
1455
|
+
["theses", "thesis"]
|
|
1456
|
+
]);
|
|
1457
|
+
static invariantSingulars = new Set([
|
|
1458
|
+
"analysis",
|
|
1459
|
+
"diagnosis",
|
|
1460
|
+
"news",
|
|
1461
|
+
"series",
|
|
1462
|
+
"species",
|
|
1463
|
+
"status",
|
|
1464
|
+
"synopsis",
|
|
1465
|
+
"thesis"
|
|
1466
|
+
]);
|
|
1377
1467
|
static nestedContextSegments = new Set([
|
|
1378
1468
|
"account",
|
|
1379
1469
|
"accounts",
|
|
@@ -1404,8 +1494,12 @@ var TypeScriptNamingSupport = class TypeScriptNamingSupport {
|
|
|
1404
1494
|
return segment.replace(/^\{/, "").replace(/\}$/, "").replace(/^:/, "");
|
|
1405
1495
|
}
|
|
1406
1496
|
singularize(value) {
|
|
1497
|
+
const normalized = value.toLowerCase();
|
|
1498
|
+
if (NamingSupport.irregularSingulars.has(normalized)) return NamingSupport.irregularSingulars.get(normalized) ?? value;
|
|
1499
|
+
if (NamingSupport.invariantSingulars.has(normalized)) return value;
|
|
1407
1500
|
if (/ies$/i.test(value)) return `${value.slice(0, -3)}y`;
|
|
1408
1501
|
if (/(sses|shes|ches|xes|zes)$/i.test(value)) return value.slice(0, -2);
|
|
1502
|
+
if (/uses$/i.test(value)) return value.slice(0, -2);
|
|
1409
1503
|
if (value.endsWith("s") && !value.endsWith("ss") && value.length > 1) return value.slice(0, -1);
|
|
1410
1504
|
return value;
|
|
1411
1505
|
}
|
|
@@ -1425,7 +1519,7 @@ var TypeScriptNamingSupport = class TypeScriptNamingSupport {
|
|
|
1425
1519
|
const tailSegment = staticSegments[staticSegments.length - 1] ?? "resource";
|
|
1426
1520
|
const parentSegment = staticSegments[staticSegments.length - 2] ?? null;
|
|
1427
1521
|
const hasPathParamBeforeTail = pathSegments.slice(0, -1).some((segment) => this.isPathParam(segment));
|
|
1428
|
-
const shouldPrefixParent = Boolean(parentSegment && (
|
|
1522
|
+
const shouldPrefixParent = Boolean(parentSegment && (NamingSupport.contextualTailSegments.has(tailSegment.toLowerCase()) || hasPathParamBeforeTail && NamingSupport.nestedContextSegments.has(tailSegment.toLowerCase())));
|
|
1429
1523
|
return {
|
|
1430
1524
|
baseName: this.sanitizeTypeName(shouldPrefixParent ? `${parentSegment} ${tailSegment}` : tailSegment),
|
|
1431
1525
|
collisionSuffix: paramSegments.length > 0 ? `By ${paramSegments.map((segment) => this.sanitizeTypeName(segment)).join(" And ")}` : parentSegment && !shouldPrefixParent ? this.sanitizeTypeName(parentSegment) : ""
|
|
@@ -1444,7 +1538,7 @@ var TypeScriptNamingSupport = class TypeScriptNamingSupport {
|
|
|
1444
1538
|
}
|
|
1445
1539
|
insertCollisionSuffix(baseName, collisionName) {
|
|
1446
1540
|
if (!collisionName) return baseName;
|
|
1447
|
-
for (const roleSuffix of
|
|
1541
|
+
for (const roleSuffix of NamingSupport.roleSuffixes) if (baseName.endsWith(roleSuffix) && baseName.length > roleSuffix.length) return `${baseName.slice(0, -roleSuffix.length)}${collisionName}${roleSuffix}`;
|
|
1448
1542
|
return `${baseName}${collisionName}`;
|
|
1449
1543
|
}
|
|
1450
1544
|
deriveSdkGroupNamesBySignature(document, namespaceStrategy) {
|
|
@@ -2045,9 +2139,9 @@ var TypeScriptShapeBuilder = class {
|
|
|
2045
2139
|
};
|
|
2046
2140
|
|
|
2047
2141
|
//#endregion
|
|
2048
|
-
//#region src/generator/
|
|
2049
|
-
var
|
|
2050
|
-
naming = new
|
|
2142
|
+
//#region src/generator/TypeBuilder.ts
|
|
2143
|
+
var TypeBuilder = class {
|
|
2144
|
+
naming = new NamingSupport();
|
|
2051
2145
|
shapes = new TypeScriptShapeBuilder(this.naming);
|
|
2052
2146
|
/**
|
|
2053
2147
|
* Creates a new generator context, which holds the state of the type generation process.
|
|
@@ -2366,7 +2460,7 @@ var TypeScriptTypeBuilder = class {
|
|
|
2366
2460
|
//#endregion
|
|
2367
2461
|
//#region src/generator/TypeScriptGenerator.ts
|
|
2368
2462
|
var TypeScriptGenerator = class TypeScriptGenerator {
|
|
2369
|
-
typeBuilder = new
|
|
2463
|
+
typeBuilder = new TypeBuilder();
|
|
2370
2464
|
moduleRenderer = new TypeScriptModuleRenderer();
|
|
2371
2465
|
/**
|
|
2372
2466
|
* Static helper method to generate a TypeScript module string from a generic JSON-like
|
|
@@ -2533,7 +2627,7 @@ var OutputGenerator = class {
|
|
|
2533
2627
|
//#endregion
|
|
2534
2628
|
//#region src/generator/SdkPackageGenerator.ts
|
|
2535
2629
|
var SdkPackageGenerator = class {
|
|
2536
|
-
typeBuilder = new
|
|
2630
|
+
typeBuilder = new TypeBuilder();
|
|
2537
2631
|
typeScriptGenerator = new TypeScriptGenerator();
|
|
2538
2632
|
generate(document, options = {}) {
|
|
2539
2633
|
const outputMode = options.outputMode ?? "both";
|
|
@@ -3792,6 +3886,7 @@ exports.SdkPackageGenerator = SdkPackageGenerator;
|
|
|
3792
3886
|
exports.TypeScriptGenerator = TypeScriptGenerator;
|
|
3793
3887
|
exports.browser = browser;
|
|
3794
3888
|
exports.buildOperationUrl = buildOperationUrl;
|
|
3889
|
+
exports.closeActiveBrowserResources = closeActiveBrowserResources;
|
|
3795
3890
|
exports.defaultConfig = defaultConfig;
|
|
3796
3891
|
exports.defineConfig = defineConfig;
|
|
3797
3892
|
exports.endBrowserSession = endBrowserSession;
|
|
@@ -3850,6 +3945,7 @@ exports.parseLooseStructuredValue = parseLooseStructuredValue;
|
|
|
3850
3945
|
exports.readInputValue = readInputValue;
|
|
3851
3946
|
exports.readText = readText;
|
|
3852
3947
|
exports.readTexts = readTexts;
|
|
3948
|
+
exports.registerActiveBrowserCloser = registerActiveBrowserCloser;
|
|
3853
3949
|
exports.resolveConfig = resolveConfig;
|
|
3854
3950
|
exports.resolveOpenApiMediaExample = resolveOpenApiMediaExample;
|
|
3855
3951
|
exports.resolveParameterInput = resolveParameterInput;
|
package/dist/index.d.ts
CHANGED
|
@@ -624,6 +624,7 @@ interface BrowserSession {
|
|
|
624
624
|
closers: Array<() => Promise<void> | void>;
|
|
625
625
|
puppeteerBrowser?: Browser;
|
|
626
626
|
}
|
|
627
|
+
type CleanupCloser = () => Promise<void> | void;
|
|
627
628
|
declare global {
|
|
628
629
|
var __oapieBrowserSession: BrowserSession | undefined;
|
|
629
630
|
}
|
|
@@ -631,6 +632,8 @@ declare const supportedBrowsers: BrowserName[];
|
|
|
631
632
|
declare const defaultConfig: UserConfig;
|
|
632
633
|
declare let globalConfig: UserConfig;
|
|
633
634
|
declare const getBrowserSession: () => BrowserSession | undefined;
|
|
635
|
+
declare const registerActiveBrowserCloser: (closer: CleanupCloser) => (() => void);
|
|
636
|
+
declare const closeActiveBrowserResources: () => Promise<void>;
|
|
634
637
|
declare const startBrowserSession: (config?: UserConfig) => Promise<BrowserSession>;
|
|
635
638
|
declare const endBrowserSession: () => Promise<void>;
|
|
636
639
|
declare const defineConfig: (config: Partial<UserConfig>) => UserConfig;
|
|
@@ -684,4 +687,4 @@ interface ReadmeCrawledOperation extends ReadmeOperation {
|
|
|
684
687
|
}
|
|
685
688
|
declare const resolveReadmeSidebarUrls: (operation: Pick<ReadmeOperation, "sidebarLinks">, baseUrl: string) => string[];
|
|
686
689
|
//#endregion
|
|
687
|
-
export { Application, AttributeQueryNode, AttributedNode, BrowserName, Declaration, GenerateCommand, GeneratorContext, InitCommand, InterfaceAliasDeclaration, InterfaceDeclaration, JsonLike, JsonRepair, OpenApiDocumentLike, OpenApiMediaType, OpenApiOauthFlowLike, OpenApiOperationLike, OpenApiParameterLike, OpenApiResponse, OpenApiSchema, OpenApiSecurityRequirementLike, OpenApiSecuritySchemeLike, OpenApiTransformer, OperationTypeRefs, OutputGenerator, ParseCommand, PayloadSchemaCandidate, QueryableNode, ReadmeCodeSnippet, ReadmeCrawledOperation, ReadmeNormalizedRequestExample, ReadmeOperation, ReadmeParameter, ReadmeResponseBody, ReadmeResponseSchema, ReadmeSidebarLink, SdkGroupManifest, SdkManifest, SdkMethodNamingStrategy, SdkNamespaceNamingStrategy, SdkNamingStrategyOptions, SdkOperationManifest, SdkPackageGenerator, SdkPackageGeneratorOptions, SdkParameterManifest, SdkSecurityRequirementManifest, SdkSecurityRequirementSchemeManifest, SdkSecuritySchemeManifest, SemanticModel, ShapeAliasDeclaration, ShapeNode, ShapeProperty, TextNodeLike, TypeReferenceAliasDeclaration, TypeScriptGenerator, UserConfig, browser, buildOperationUrl, defaultConfig, defineConfig, endBrowserSession, escapeSelector, extractBalancedSegment, extractButtonText, extractCodeMirrorText, extractCodeSnippets, extractFetchBody, extractFetchHeaders, extractObjectPropertyValue, extractOperationDescription, extractOperationParametersFromOpenApi, extractParameterDescription, extractReadmeOperationFromHtml, extractReadmeOperationFromSsrProps, extractRequestCodeSnippets, extractRequestParams, extractRequestParamsFromOpenApi, extractRequestSnippetLabel, extractResponseBodies, extractResponseBodiesFromOpenApi, extractResponseContentTypes, extractResponseLabels, extractResponseSchemas, extractResponseSchemasFromOpenApi, extractSidebarLinkLabel, extractSidebarLinks, extractStablePageHtml, extractStringLiteralValue, findParameterRoot, flattenOpenApiSchemaProperties, getBrowserSession, globalConfig, inferParameterLocation, inferParameterLocationFromText, inferParameterPath, inferParameterType, isRecord, isRequiredParameter, isSupportedBrowser, loadUserConfig, mergeReadmeOperations, mergeSsrPropsIntoRenderedHtml, normalizeCurlSnippet, normalizeFetchSnippet, normalizeRequestCodeSnippet, normalizeResponseBody, normalizeStructuredRequestBody, parseLooseStructuredValue, readInputValue, readText, readTexts, resolveConfig, resolveOpenApiMediaExample, resolveParameterInput, resolveReadmeSidebarUrls, resolveSsrOperation, startBrowserSession, supportedBrowsers, transformer };
|
|
690
|
+
export { Application, AttributeQueryNode, AttributedNode, BrowserName, Declaration, GenerateCommand, GeneratorContext, InitCommand, InterfaceAliasDeclaration, InterfaceDeclaration, JsonLike, JsonRepair, OpenApiDocumentLike, OpenApiMediaType, OpenApiOauthFlowLike, OpenApiOperationLike, OpenApiParameterLike, OpenApiResponse, OpenApiSchema, OpenApiSecurityRequirementLike, OpenApiSecuritySchemeLike, OpenApiTransformer, OperationTypeRefs, OutputGenerator, ParseCommand, PayloadSchemaCandidate, QueryableNode, ReadmeCodeSnippet, ReadmeCrawledOperation, ReadmeNormalizedRequestExample, ReadmeOperation, ReadmeParameter, ReadmeResponseBody, ReadmeResponseSchema, ReadmeSidebarLink, SdkGroupManifest, SdkManifest, SdkMethodNamingStrategy, SdkNamespaceNamingStrategy, SdkNamingStrategyOptions, SdkOperationManifest, SdkPackageGenerator, SdkPackageGeneratorOptions, SdkParameterManifest, SdkSecurityRequirementManifest, SdkSecurityRequirementSchemeManifest, SdkSecuritySchemeManifest, SemanticModel, ShapeAliasDeclaration, ShapeNode, ShapeProperty, TextNodeLike, TypeReferenceAliasDeclaration, TypeScriptGenerator, UserConfig, browser, buildOperationUrl, closeActiveBrowserResources, defaultConfig, defineConfig, endBrowserSession, escapeSelector, extractBalancedSegment, extractButtonText, extractCodeMirrorText, extractCodeSnippets, extractFetchBody, extractFetchHeaders, extractObjectPropertyValue, extractOperationDescription, extractOperationParametersFromOpenApi, extractParameterDescription, extractReadmeOperationFromHtml, extractReadmeOperationFromSsrProps, extractRequestCodeSnippets, extractRequestParams, extractRequestParamsFromOpenApi, extractRequestSnippetLabel, extractResponseBodies, extractResponseBodiesFromOpenApi, extractResponseContentTypes, extractResponseLabels, extractResponseSchemas, extractResponseSchemasFromOpenApi, extractSidebarLinkLabel, extractSidebarLinks, extractStablePageHtml, extractStringLiteralValue, findParameterRoot, flattenOpenApiSchemaProperties, getBrowserSession, globalConfig, inferParameterLocation, inferParameterLocationFromText, inferParameterPath, inferParameterType, isRecord, isRequiredParameter, isSupportedBrowser, loadUserConfig, mergeReadmeOperations, mergeSsrPropsIntoRenderedHtml, normalizeCurlSnippet, normalizeFetchSnippet, normalizeRequestCodeSnippet, normalizeResponseBody, normalizeStructuredRequestBody, parseLooseStructuredValue, readInputValue, readText, readTexts, registerActiveBrowserCloser, resolveConfig, resolveOpenApiMediaExample, resolveParameterInput, resolveReadmeSidebarUrls, resolveSsrOperation, startBrowserSession, supportedBrowsers, transformer };
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,11 @@ import { existsSync, readdirSync } from "node:fs";
|
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
|
|
13
13
|
//#region src/Manager.ts
|
|
14
|
+
const terminationSignals = [
|
|
15
|
+
"SIGINT",
|
|
16
|
+
"SIGTERM",
|
|
17
|
+
"SIGTSTP"
|
|
18
|
+
];
|
|
14
19
|
const supportedBrowsers = [
|
|
15
20
|
"axios",
|
|
16
21
|
"happy-dom",
|
|
@@ -36,7 +41,54 @@ const defaultConfig = {
|
|
|
36
41
|
}
|
|
37
42
|
};
|
|
38
43
|
let globalConfig = defaultConfig;
|
|
44
|
+
let cleanupInProgress = false;
|
|
45
|
+
let terminationHandlersInstalled = false;
|
|
46
|
+
const activeClosers = /* @__PURE__ */ new Set();
|
|
39
47
|
const getBrowserSession = () => globalThis.__oapieBrowserSession;
|
|
48
|
+
const removeTerminationHandlersWhenIdle = () => {
|
|
49
|
+
if (!getBrowserSession() && activeClosers.size === 0) removeTerminationHandlers();
|
|
50
|
+
};
|
|
51
|
+
const ensureTerminationHandlers = () => {
|
|
52
|
+
if (terminationHandlersInstalled) return;
|
|
53
|
+
for (const signal of terminationSignals) process.on(signal, handleTerminationSignal);
|
|
54
|
+
terminationHandlersInstalled = true;
|
|
55
|
+
};
|
|
56
|
+
const removeTerminationHandlers = () => {
|
|
57
|
+
if (!terminationHandlersInstalled) return;
|
|
58
|
+
for (const signal of terminationSignals) process.off(signal, handleTerminationSignal);
|
|
59
|
+
terminationHandlersInstalled = false;
|
|
60
|
+
};
|
|
61
|
+
const registerActiveBrowserCloser = (closer) => {
|
|
62
|
+
ensureTerminationHandlers();
|
|
63
|
+
activeClosers.add(closer);
|
|
64
|
+
return () => {
|
|
65
|
+
activeClosers.delete(closer);
|
|
66
|
+
removeTerminationHandlersWhenIdle();
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const closeActiveBrowserResources = async () => {
|
|
70
|
+
const closers = Array.from(activeClosers).reverse();
|
|
71
|
+
activeClosers.clear();
|
|
72
|
+
for (const closer of closers) await closer();
|
|
73
|
+
await endBrowserSession();
|
|
74
|
+
};
|
|
75
|
+
const handleTerminationSignal = async (signal) => {
|
|
76
|
+
if (cleanupInProgress) return;
|
|
77
|
+
cleanupInProgress = true;
|
|
78
|
+
removeTerminationHandlers();
|
|
79
|
+
try {
|
|
80
|
+
await closeActiveBrowserResources();
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error("Failed to close active browser resources:", error);
|
|
83
|
+
} finally {
|
|
84
|
+
cleanupInProgress = false;
|
|
85
|
+
}
|
|
86
|
+
if (signal === "SIGTSTP") {
|
|
87
|
+
process.kill(process.pid, signal);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
91
|
+
};
|
|
40
92
|
const startBrowserSession = async (config = globalConfig) => {
|
|
41
93
|
const activeSession = getBrowserSession();
|
|
42
94
|
if (activeSession?.browser === config.browser) return activeSession;
|
|
@@ -49,6 +101,7 @@ const startBrowserSession = async (config = globalConfig) => {
|
|
|
49
101
|
headless: config.puppeteer?.headless ?? true,
|
|
50
102
|
args: config.puppeteer?.args ?? ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
51
103
|
});
|
|
104
|
+
ensureTerminationHandlers();
|
|
52
105
|
globalThis.__oapieBrowserSession = nextSession;
|
|
53
106
|
return nextSession;
|
|
54
107
|
};
|
|
@@ -58,6 +111,7 @@ const endBrowserSession = async () => {
|
|
|
58
111
|
globalThis.__oapieBrowserSession = void 0;
|
|
59
112
|
for (const closer of activeSession.closers.reverse()) await closer();
|
|
60
113
|
if (activeSession.puppeteerBrowser) await activeSession.puppeteerBrowser.close();
|
|
114
|
+
removeTerminationHandlersWhenIdle();
|
|
61
115
|
};
|
|
62
116
|
const registerDeferredCloser = (browserName, closer) => {
|
|
63
117
|
const activeSession = getBrowserSession();
|
|
@@ -104,17 +158,23 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
104
158
|
innerHeight: 768,
|
|
105
159
|
settings: config.happyDom
|
|
106
160
|
});
|
|
107
|
-
window.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
161
|
+
const unregisterWindowCloser = registerActiveBrowserCloser(() => window.happyDOM.close());
|
|
162
|
+
try {
|
|
163
|
+
window.document.write(data);
|
|
164
|
+
await window.happyDOM.waitUntilComplete();
|
|
165
|
+
const html = window.document.documentElement.outerHTML;
|
|
166
|
+
if (!html) {
|
|
167
|
+
await window.happyDOM.close();
|
|
168
|
+
throw new Error(`Unable to extract HTML from remote source: ${source}`);
|
|
169
|
+
}
|
|
170
|
+
if (!registerDeferredCloser("happy-dom", () => window.happyDOM.close())) await window.happyDOM.close();
|
|
171
|
+
return html;
|
|
172
|
+
} finally {
|
|
173
|
+
unregisterWindowCloser();
|
|
174
|
+
}
|
|
116
175
|
} else if (config.browser === "jsdom") {
|
|
117
176
|
let window;
|
|
177
|
+
let unregisterWindowCloser;
|
|
118
178
|
try {
|
|
119
179
|
({window} = new JSDOM(data, {
|
|
120
180
|
url: source,
|
|
@@ -122,6 +182,7 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
122
182
|
runScripts: "dangerously",
|
|
123
183
|
includeNodeLocations: true
|
|
124
184
|
}));
|
|
185
|
+
unregisterWindowCloser = registerActiveBrowserCloser(() => window?.close());
|
|
125
186
|
const html = window.document.documentElement.outerHTML;
|
|
126
187
|
if (!html) throw new Error(`Unable to extract HTML from remote source: ${source}`);
|
|
127
188
|
const currentWindow = window;
|
|
@@ -131,6 +192,7 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
131
192
|
} else window = void 0;
|
|
132
193
|
return html;
|
|
133
194
|
} finally {
|
|
195
|
+
unregisterWindowCloser?.();
|
|
134
196
|
if (window) window.close();
|
|
135
197
|
}
|
|
136
198
|
} else if (config.browser === "puppeteer") {
|
|
@@ -139,6 +201,8 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
139
201
|
let browserInstance = activeSession?.browser === "puppeteer" ? activeSession.puppeteerBrowser : void 0;
|
|
140
202
|
let shouldClose = false;
|
|
141
203
|
let page;
|
|
204
|
+
let unregisterBrowserCloser;
|
|
205
|
+
let unregisterPageCloser;
|
|
142
206
|
try {
|
|
143
207
|
if (!browserInstance) {
|
|
144
208
|
browserInstance = await (await import("puppeteer")).launch({
|
|
@@ -146,8 +210,14 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
146
210
|
args: config.puppeteer?.args ?? ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
147
211
|
});
|
|
148
212
|
shouldClose = true;
|
|
213
|
+
unregisterBrowserCloser = registerActiveBrowserCloser(async () => {
|
|
214
|
+
if (browserInstance?.connected) await browserInstance.close();
|
|
215
|
+
});
|
|
149
216
|
}
|
|
150
217
|
page = await browserInstance.newPage();
|
|
218
|
+
unregisterPageCloser = registerActiveBrowserCloser(async () => {
|
|
219
|
+
if (page && !page.isClosed()) await page.close();
|
|
220
|
+
});
|
|
151
221
|
await page.setUserAgent({ userAgent: config.userAgent });
|
|
152
222
|
await page.setRequestInterception(true);
|
|
153
223
|
page.on("request", (e) => {
|
|
@@ -183,7 +253,9 @@ const browser = async (source, config = globalConfig, initial = false) => {
|
|
|
183
253
|
}
|
|
184
254
|
return html;
|
|
185
255
|
} finally {
|
|
256
|
+
unregisterPageCloser?.();
|
|
186
257
|
if (page && !page.isClosed()) await page.close();
|
|
258
|
+
unregisterBrowserCloser?.();
|
|
187
259
|
if (shouldClose && browserInstance) await browserInstance.close();
|
|
188
260
|
}
|
|
189
261
|
} else throw new Error(`Unsupported browser specified in configuration: ${globalConfig.browser}`);
|
|
@@ -1334,14 +1406,32 @@ var TypeScriptModuleRenderer = class {
|
|
|
1334
1406
|
};
|
|
1335
1407
|
|
|
1336
1408
|
//#endregion
|
|
1337
|
-
//#region src/generator/
|
|
1338
|
-
var
|
|
1409
|
+
//#region src/generator/NamingSupport.ts
|
|
1410
|
+
var NamingSupport = class NamingSupport {
|
|
1339
1411
|
static contextualTailSegments = new Set([
|
|
1340
1412
|
"history",
|
|
1341
1413
|
"status",
|
|
1342
1414
|
"detail",
|
|
1343
1415
|
"details"
|
|
1344
1416
|
]);
|
|
1417
|
+
static irregularSingulars = new Map([
|
|
1418
|
+
["analyses", "analysis"],
|
|
1419
|
+
["diagnoses", "diagnosis"],
|
|
1420
|
+
["parentheses", "parenthesis"],
|
|
1421
|
+
["statuses", "status"],
|
|
1422
|
+
["synopses", "synopsis"],
|
|
1423
|
+
["theses", "thesis"]
|
|
1424
|
+
]);
|
|
1425
|
+
static invariantSingulars = new Set([
|
|
1426
|
+
"analysis",
|
|
1427
|
+
"diagnosis",
|
|
1428
|
+
"news",
|
|
1429
|
+
"series",
|
|
1430
|
+
"species",
|
|
1431
|
+
"status",
|
|
1432
|
+
"synopsis",
|
|
1433
|
+
"thesis"
|
|
1434
|
+
]);
|
|
1345
1435
|
static nestedContextSegments = new Set([
|
|
1346
1436
|
"account",
|
|
1347
1437
|
"accounts",
|
|
@@ -1372,8 +1462,12 @@ var TypeScriptNamingSupport = class TypeScriptNamingSupport {
|
|
|
1372
1462
|
return segment.replace(/^\{/, "").replace(/\}$/, "").replace(/^:/, "");
|
|
1373
1463
|
}
|
|
1374
1464
|
singularize(value) {
|
|
1465
|
+
const normalized = value.toLowerCase();
|
|
1466
|
+
if (NamingSupport.irregularSingulars.has(normalized)) return NamingSupport.irregularSingulars.get(normalized) ?? value;
|
|
1467
|
+
if (NamingSupport.invariantSingulars.has(normalized)) return value;
|
|
1375
1468
|
if (/ies$/i.test(value)) return `${value.slice(0, -3)}y`;
|
|
1376
1469
|
if (/(sses|shes|ches|xes|zes)$/i.test(value)) return value.slice(0, -2);
|
|
1470
|
+
if (/uses$/i.test(value)) return value.slice(0, -2);
|
|
1377
1471
|
if (value.endsWith("s") && !value.endsWith("ss") && value.length > 1) return value.slice(0, -1);
|
|
1378
1472
|
return value;
|
|
1379
1473
|
}
|
|
@@ -1393,7 +1487,7 @@ var TypeScriptNamingSupport = class TypeScriptNamingSupport {
|
|
|
1393
1487
|
const tailSegment = staticSegments[staticSegments.length - 1] ?? "resource";
|
|
1394
1488
|
const parentSegment = staticSegments[staticSegments.length - 2] ?? null;
|
|
1395
1489
|
const hasPathParamBeforeTail = pathSegments.slice(0, -1).some((segment) => this.isPathParam(segment));
|
|
1396
|
-
const shouldPrefixParent = Boolean(parentSegment && (
|
|
1490
|
+
const shouldPrefixParent = Boolean(parentSegment && (NamingSupport.contextualTailSegments.has(tailSegment.toLowerCase()) || hasPathParamBeforeTail && NamingSupport.nestedContextSegments.has(tailSegment.toLowerCase())));
|
|
1397
1491
|
return {
|
|
1398
1492
|
baseName: this.sanitizeTypeName(shouldPrefixParent ? `${parentSegment} ${tailSegment}` : tailSegment),
|
|
1399
1493
|
collisionSuffix: paramSegments.length > 0 ? `By ${paramSegments.map((segment) => this.sanitizeTypeName(segment)).join(" And ")}` : parentSegment && !shouldPrefixParent ? this.sanitizeTypeName(parentSegment) : ""
|
|
@@ -1412,7 +1506,7 @@ var TypeScriptNamingSupport = class TypeScriptNamingSupport {
|
|
|
1412
1506
|
}
|
|
1413
1507
|
insertCollisionSuffix(baseName, collisionName) {
|
|
1414
1508
|
if (!collisionName) return baseName;
|
|
1415
|
-
for (const roleSuffix of
|
|
1509
|
+
for (const roleSuffix of NamingSupport.roleSuffixes) if (baseName.endsWith(roleSuffix) && baseName.length > roleSuffix.length) return `${baseName.slice(0, -roleSuffix.length)}${collisionName}${roleSuffix}`;
|
|
1416
1510
|
return `${baseName}${collisionName}`;
|
|
1417
1511
|
}
|
|
1418
1512
|
deriveSdkGroupNamesBySignature(document, namespaceStrategy) {
|
|
@@ -2013,9 +2107,9 @@ var TypeScriptShapeBuilder = class {
|
|
|
2013
2107
|
};
|
|
2014
2108
|
|
|
2015
2109
|
//#endregion
|
|
2016
|
-
//#region src/generator/
|
|
2017
|
-
var
|
|
2018
|
-
naming = new
|
|
2110
|
+
//#region src/generator/TypeBuilder.ts
|
|
2111
|
+
var TypeBuilder = class {
|
|
2112
|
+
naming = new NamingSupport();
|
|
2019
2113
|
shapes = new TypeScriptShapeBuilder(this.naming);
|
|
2020
2114
|
/**
|
|
2021
2115
|
* Creates a new generator context, which holds the state of the type generation process.
|
|
@@ -2334,7 +2428,7 @@ var TypeScriptTypeBuilder = class {
|
|
|
2334
2428
|
//#endregion
|
|
2335
2429
|
//#region src/generator/TypeScriptGenerator.ts
|
|
2336
2430
|
var TypeScriptGenerator = class TypeScriptGenerator {
|
|
2337
|
-
typeBuilder = new
|
|
2431
|
+
typeBuilder = new TypeBuilder();
|
|
2338
2432
|
moduleRenderer = new TypeScriptModuleRenderer();
|
|
2339
2433
|
/**
|
|
2340
2434
|
* Static helper method to generate a TypeScript module string from a generic JSON-like
|
|
@@ -2501,7 +2595,7 @@ var OutputGenerator = class {
|
|
|
2501
2595
|
//#endregion
|
|
2502
2596
|
//#region src/generator/SdkPackageGenerator.ts
|
|
2503
2597
|
var SdkPackageGenerator = class {
|
|
2504
|
-
typeBuilder = new
|
|
2598
|
+
typeBuilder = new TypeBuilder();
|
|
2505
2599
|
typeScriptGenerator = new TypeScriptGenerator();
|
|
2506
2600
|
generate(document, options = {}) {
|
|
2507
2601
|
const outputMode = options.outputMode ?? "both";
|
|
@@ -3749,4 +3843,4 @@ async function resolveConfig(cliOverrides = {}) {
|
|
|
3749
3843
|
}
|
|
3750
3844
|
|
|
3751
3845
|
//#endregion
|
|
3752
|
-
export { Application, GenerateCommand, InitCommand, JsonRepair, OpenApiTransformer, OutputGenerator, ParseCommand, SdkPackageGenerator, TypeScriptGenerator, browser, buildOperationUrl, defaultConfig, defineConfig, endBrowserSession, escapeSelector, extractBalancedSegment, extractButtonText, extractCodeMirrorText, extractCodeSnippets, extractFetchBody, extractFetchHeaders, extractObjectPropertyValue, extractOperationDescription, extractOperationParametersFromOpenApi, extractParameterDescription, extractReadmeOperationFromHtml, extractReadmeOperationFromSsrProps, extractRequestCodeSnippets, extractRequestParams, extractRequestParamsFromOpenApi, extractRequestSnippetLabel, extractResponseBodies, extractResponseBodiesFromOpenApi, extractResponseContentTypes, extractResponseLabels, extractResponseSchemas, extractResponseSchemasFromOpenApi, extractSidebarLinkLabel, extractSidebarLinks, extractStablePageHtml, extractStringLiteralValue, findParameterRoot, flattenOpenApiSchemaProperties, getBrowserSession, globalConfig, inferParameterLocation, inferParameterLocationFromText, inferParameterPath, inferParameterType, isRecord, isRequiredParameter, isSupportedBrowser, loadUserConfig, mergeReadmeOperations, mergeSsrPropsIntoRenderedHtml, normalizeCurlSnippet, normalizeFetchSnippet, normalizeRequestCodeSnippet, normalizeResponseBody, normalizeStructuredRequestBody, parseLooseStructuredValue, readInputValue, readText, readTexts, resolveConfig, resolveOpenApiMediaExample, resolveParameterInput, resolveReadmeSidebarUrls, resolveSsrOperation, startBrowserSession, supportedBrowsers, transformer };
|
|
3846
|
+
export { Application, GenerateCommand, InitCommand, JsonRepair, OpenApiTransformer, OutputGenerator, ParseCommand, SdkPackageGenerator, TypeScriptGenerator, browser, buildOperationUrl, closeActiveBrowserResources, defaultConfig, defineConfig, endBrowserSession, escapeSelector, extractBalancedSegment, extractButtonText, extractCodeMirrorText, extractCodeSnippets, extractFetchBody, extractFetchHeaders, extractObjectPropertyValue, extractOperationDescription, extractOperationParametersFromOpenApi, extractParameterDescription, extractReadmeOperationFromHtml, extractReadmeOperationFromSsrProps, extractRequestCodeSnippets, extractRequestParams, extractRequestParamsFromOpenApi, extractRequestSnippetLabel, extractResponseBodies, extractResponseBodiesFromOpenApi, extractResponseContentTypes, extractResponseLabels, extractResponseSchemas, extractResponseSchemasFromOpenApi, extractSidebarLinkLabel, extractSidebarLinks, extractStablePageHtml, extractStringLiteralValue, findParameterRoot, flattenOpenApiSchemaProperties, getBrowserSession, globalConfig, inferParameterLocation, inferParameterLocationFromText, inferParameterPath, inferParameterType, isRecord, isRequiredParameter, isSupportedBrowser, loadUserConfig, mergeReadmeOperations, mergeSsrPropsIntoRenderedHtml, normalizeCurlSnippet, normalizeFetchSnippet, normalizeRequestCodeSnippet, normalizeResponseBody, normalizeStructuredRequestBody, parseLooseStructuredValue, readInputValue, readText, readTexts, registerActiveBrowserCloser, resolveConfig, resolveOpenApiMediaExample, resolveParameterInput, resolveReadmeSidebarUrls, resolveSsrOperation, startBrowserSession, supportedBrowsers, transformer };
|
package/package.json
CHANGED