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 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},R=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},z=e=>e&&e.getAttribute(`value`)?.trim()||null,he=(e,t)=>J(e.querySelectorAll(`span`)).filter(e=>!t||e.toUpperCase()!==t).sort((e,t)=>t.length-e.length)[0]??q(e)??``,ge=e=>J(e.querySelectorAll(`div`)).filter(e=>/^[\w.+-]+\/[\w.+-]+$/i.test(e)),_e=e=>Array.from(e.querySelectorAll(`button, [role="button"]`)).map(e=>Y(e)).filter(e=>!!e).filter(e=>/\b\d{3}\b/.test(e)),ve=(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},ye=(e,t)=>{let n=t.getAttribute(`for`),r=n?e.querySelector(`#${K(n)}`):null,i=e.querySelector(`input, textarea, select`);return r??i},be=(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},xe=(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]:[]},Se=(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},Ce=(e,t)=>t?.hasAttribute(`required`)?!0:J(e.querySelectorAll(`*`)).some(e=>e.toLowerCase()===`required`),we=e=>q(e.querySelector(`[id$="__description"]`)||(Array.from(e.querySelectorAll(`[data-testid="RDMD"]`))[0]??null)),V=(e,t)=>{let n=e.trim();if(t?.toLowerCase().includes(`json`)||/^(?:\{|\[)/.test(n)){let t=w.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}},Te=e=>e?Ee(e)??De(e)??{sourceLabel:e.label,method:null,url:null,headers:{},bodyFormat:null,body:null,rawBody:null}:null,Ee=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?V(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}},De=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=Oe(r),o=H(r),s=a[`content-type`]??a[`Content-Type`]??null,c=o?ke(o,s):null;return{sourceLabel:e.label,method:i,url:n,headers:a,bodyFormat:c?.format??null,body:c?.body??null,rawBody:o}},Oe=e=>{let t=U(e,`headers`);if(!t)return{};let n=G(t);return T(n)?Object.fromEntries(Object.entries(n).map(([e,t])=>[e,String(t)])):{}},H=e=>U(e,`body`),ke=(e,t)=>{let n=G(e);return n!==null&&(t?.toLowerCase().includes(`json`)||/^[[{]/.test(e.trim()))?{format:`json`,body:n}:V(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]===`'`?Ae(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},Ae=(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=Me(e).trim();if(!/^[[{]/.test(t))return null;let n=je(t.replace(/([{,]\s*)([A-Za-z_$][\w$-]*)(\s*:)/g,`$1"$2"$3`).replace(/,\s*([}\]])/g,`$1`));try{return JSON.parse(n)}catch{return null}},je=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},Me=e=>{let t=Ne(e);return t=Pe(t),t=Fe(t),t},Ne=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},Pe=e=>e.replace(/([[{]\s*)([A-Za-z_$][\w$-]*)(\s+)(?=")/g,`$1`),Fe=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),Ie=(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 Le=class{config;constructor(e={}){this.config=x(e)}getConfig(e={}){return{...this.config,...e}}configure(e){return this.config=x({...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=!!_();a||await v(this.config);try{let e=Ie(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=E(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 y()}}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)?C(e,this.config,t):o(i.resolve(process.cwd(),e),`utf8`)}},Re=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 {
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`},Ve=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(`
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(T(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:w.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`?w.parsePossiblyTruncated(e.responseExampleRaw):typeof e.responseExample==`string`?w.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 He=class extends s{signature=`generate
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(!S(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)&&!S(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 v(this.app.getConfig()),u=!0);let y=await this.resolveSdkSource({source:n,crawl:o,baseUrl:s,rootTypeName:_,namespaceStrategy:p,methodStrategy:m}),b=this.resolvePackageName(c),x=new Ve().generate(y.document,{outputMode:h,signatureStyle:g,rootTypeName:_,namespaceStrategy:p,methodStrategy:m,schemaModule:y.schemaModule,packageName:String(this.option(`name`,``)).trim()||b});await this.writePackageFiles(c,x);let C=Date.now()-d;r.twoColumnDetail(r.log([[`Generated`,`green`],[`${C/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 y()}}async resolveSdkSource(e){if(this.isTypeScriptArtifactSource(e.source))return this.loadSdkSourceFromTypeScriptArtifact(e.source);let t=E(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 Ue=p(import.meta.url);var We=class extends s{signature=`init
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=h;return[`import { defineConfig } from '${Ue.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(`
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
- `)}},Ge=class extends s{signature=`parse
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(!S(a))throw Error(`Unsupported browser: ${a}`);let f=this.resolveTimeoutOverride(o,e.requestTimeout);this.app.configure({browser:a,requestTimeout:f}),s&&(await v(this.app.getConfig()),u=!0);let p=E(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),y=Date.now()-d;r.twoColumnDetail(r.log([[`Output`,`green`],[`${y/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 y()}}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 Ke=[`oapiex.config.ts`,`oapiex.config.js`,`oapiex.config.cjs`];async function qe(e=process.cwd()){for(let t of Ke){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 Je(e={}){let t=await qe();return{...h,...t,...e,happyDom:{...h.happyDom,...t?.happyDom||{},...e.happyDom||{}}}}const Ye=new Le(await Je());await c.init(Ye,{name:`OAPIEX`,logo:``,allowRebuilds:!1,packages:[`@h3ravel/musket`],baseCommands:[We,He,Ge],exceptionHandler(e){console.error(`An unexpected error occurred:`,e),process.exit(1)}});export{};
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.document.write(data);
140
- await window.happyDOM.waitUntilComplete();
141
- const html = window.document.documentElement.outerHTML;
142
- if (!html) {
143
- await window.happyDOM.close();
144
- throw new Error(`Unable to extract HTML from remote source: ${source}`);
145
- }
146
- if (!registerDeferredCloser("happy-dom", () => window.happyDOM.close())) await window.happyDOM.close();
147
- return html;
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/TypeScriptNamingSupport.ts
1370
- var TypeScriptNamingSupport = class TypeScriptNamingSupport {
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 && (TypeScriptNamingSupport.contextualTailSegments.has(tailSegment.toLowerCase()) || hasPathParamBeforeTail && TypeScriptNamingSupport.nestedContextSegments.has(tailSegment.toLowerCase())));
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 TypeScriptNamingSupport.roleSuffixes) if (baseName.endsWith(roleSuffix) && baseName.length > roleSuffix.length) return `${baseName.slice(0, -roleSuffix.length)}${collisionName}${roleSuffix}`;
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/TypeScriptTypeBuilder.ts
2049
- var TypeScriptTypeBuilder = class {
2050
- naming = new TypeScriptNamingSupport();
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 TypeScriptTypeBuilder();
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 TypeScriptTypeBuilder();
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.document.write(data);
108
- await window.happyDOM.waitUntilComplete();
109
- const html = window.document.documentElement.outerHTML;
110
- if (!html) {
111
- await window.happyDOM.close();
112
- throw new Error(`Unable to extract HTML from remote source: ${source}`);
113
- }
114
- if (!registerDeferredCloser("happy-dom", () => window.happyDOM.close())) await window.happyDOM.close();
115
- return html;
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/TypeScriptNamingSupport.ts
1338
- var TypeScriptNamingSupport = class TypeScriptNamingSupport {
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 && (TypeScriptNamingSupport.contextualTailSegments.has(tailSegment.toLowerCase()) || hasPathParamBeforeTail && TypeScriptNamingSupport.nestedContextSegments.has(tailSegment.toLowerCase())));
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 TypeScriptNamingSupport.roleSuffixes) if (baseName.endsWith(roleSuffix) && baseName.length > roleSuffix.length) return `${baseName.slice(0, -roleSuffix.length)}${collisionName}${roleSuffix}`;
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/TypeScriptTypeBuilder.ts
2017
- var TypeScriptTypeBuilder = class {
2018
- naming = new TypeScriptNamingSupport();
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 TypeScriptTypeBuilder();
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 TypeScriptTypeBuilder();
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oapiex",
3
3
  "type": "module",
4
- "version": "0.3.1",
4
+ "version": "0.3.3",
5
5
  "description": "CLI and TypeScript toolkit for turning documentation pages into usable developer artifacts.",
6
6
  "main": "./dist/index.cjs",
7
7
  "private": false,