buddy-bot
Version:
Automated & optimized dependency updates for JavaScript & TypeScript projects. Like Renovate & Dependabot.
175 lines (106 loc) • 25.5 kB
JavaScript
// @bun
import{z as v}from"./chunk-rjfbmp1q.js";import"./chunk-q3tv2y4q.js";import"./chunk-zmck1ahy.js";import"./chunk-jcxe2rnh.js";import C from"process";class T{userAgent="Buddy-Bot/1.0.0 (https://github.com/stacksjs/buddy)";async fetchPackageInfo(Z,H,X){try{let $=await this.fetchNpmPackageInfo(Z),L=[],W=[],K;if($.repository?.url){let R=this.parseGitHubUrl($.repository.url);if(R)L=await this.fetchGitHubReleases(R.owner,R.repo,H,X),K=this.generateCompareUrl(R.owner,R.repo,H,X),W=await this.fetchChangelog(R.owner,R.repo)}return{packageInfo:$,releaseNotes:L,changelog:W,compareUrl:K}}catch($){return console.error(`\u274C Failed to fetch package info for ${Z}:`,$),{packageInfo:{name:Z,description:`Package ${Z} - see npm for details`,repository:{type:"git",url:`https://github.com/search?q=${encodeURIComponent(Z)}&type=repositories`}},releaseNotes:[],changelog:[]}}}async fetchNpmPackageInfo(Z){try{let H=await fetch(`https://registry.npmjs.org/${encodeURIComponent(Z)}`,{headers:{"User-Agent":this.userAgent}});if(!H.ok)throw new Error(`NPM registry responded with ${H.status}`);let X=await H.json(),$=X["dist-tags"]?.latest,L;try{let W=await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(Z)}`,{headers:{"User-Agent":this.userAgent}});if(W.ok)L=(await W.json()).downloads}catch{}return{name:Z,description:X.description,homepage:X.homepage,repository:X.repository,license:X.license,author:X.author,keywords:X.keywords,weeklyDownloads:L,lastPublish:X.time?.[$],maintainers:X.maintainers}}catch(H){return console.warn(`\u26A0\uFE0F Failed to fetch npm info for ${Z}:`,H),{name:Z,description:`NPM package ${Z}`,homepage:`https://www.npmjs.com/package/${encodeURIComponent(Z)}`}}}parseGitHubUrl(Z){try{let H=Z.replace(/^git\+/,"").replace(/\.git$/,"").replace(/^git:\/\//,"https://").replace(/^ssh:\/\/git@/,"https://").replace(/^git@github\.com:/,"https://github.com/"),X=new URL(H);if(X.hostname!=="github.com")return null;let $=X.pathname.split("/").filter(Boolean);if($.length>=2)return{owner:$[0],repo:$[1]};return null}catch{return null}}async fetchGitHubReleases(Z,H,X,$){try{let L=await fetch(`https://api.github.com/repos/${Z}/${H}/releases?per_page=50`,{headers:{"User-Agent":this.userAgent}});if(!L.ok)return[];return(await L.json()).filter((K)=>{let R=K.tag_name.replace(/^v/,"");return this.isVersionBetween(R,X,$)}).map((K)=>({version:K.tag_name,date:K.published_at,title:K.name||K.tag_name,body:K.body||"",htmlUrl:K.html_url,compareUrl:this.generateCompareUrl(Z,H,X,K.tag_name),author:K.author?.login,isPrerelease:K.prerelease,assets:K.assets?.map((R)=>({name:R.name,downloadUrl:R.browser_download_url,size:R.size,contentType:R.content_type}))||[]}))}catch(L){return console.warn(`Failed to fetch GitHub releases for ${Z}/${H}:`,L),[]}}async fetchChangelog(Z,H){let X=["CHANGELOG.md","CHANGELOG.rst","HISTORY.md","RELEASES.md"];for(let $ of X)try{let L=await fetch(`https://api.github.com/repos/${Z}/${H}/contents/${$}`,{headers:{"User-Agent":this.userAgent}});if(L.ok){let W=await L.json();if(W.content){let K=atob(W.content);return this.parseChangelog(K)}}}catch{}return[]}parseChangelog(Z){let H=[],X=Z.split(`
`),$=null,L=null;for(let W of X){let K=W.match(/^##\s*\[?([^\]]+)\]?\s*-?\s*(.*)/);if(K){if($)H.push($);$={version:K[1],date:K[2].trim(),changes:{}},L=null;continue}let R=W.match(/^###\s*(.+)/);if(R&&$){L=R[1].toLowerCase();continue}let A=W.match(/^[-*]\s*(.+)/);if(A&&$&&L){if(!$.changes)$.changes={};let x=L;if(!$.changes[x])$.changes[x]=[];$.changes[x].push(A[1])}}if($)H.push($);return H.slice(0,10)}generateCompareUrl(Z,H,X,$){let L=X.startsWith("v")?X:`v${X}`,W=$.startsWith("v")?$:`v${$}`;return`https://github.com/${Z}/${H}/compare/${L}...${W}`}isVersionBetween(Z,H,X){let $=Z.replace(/^v/,""),L=H.replace(/^v/,""),W=X.replace(/^v/,"");return $===W||$>L}generatePackageBadges(Z,H,X){let $=encodeURIComponent(Z.name),L=this.normalizeVersionForBadges(H),W=this.normalizeVersionForBadges(X),K=encodeURIComponent(L),R=encodeURIComponent(W);return{age:`[](https://docs.renovatebot.com/merge-confidence/)`,adoption:`[](https://docs.renovatebot.com/merge-confidence/)`,passing:`[](https://docs.renovatebot.com/merge-confidence/)`,confidence:`[](https://docs.renovatebot.com/merge-confidence/)`}}async fetchComposerPackageInfo(Z){try{let H=await fetch(`https://packagist.org/packages/${encodeURIComponent(Z)}.json`,{headers:{"User-Agent":this.userAgent}});if(!H.ok)throw new Error(`Packagist responded with ${H.status}`);let $=(await H.json()).package;if(!$)return{name:Z};let L=Object.keys($.versions||{}),W=L.find((R)=>!R.includes("dev")&&!R.includes("alpha")&&!R.includes("beta"))||L[0],K=$.versions[W]||{};return{name:$.name,description:K.description,homepage:K.homepage,repository:K.source?{type:"git",url:K.source.url}:void 0,license:K.license?.[0]||K.license,author:K.authors?.[0],keywords:K.keywords,lastPublish:K.time}}catch(H){return console.warn(`Failed to fetch Packagist info for ${Z}:`,H),{name:Z}}}generateComposerBadges(Z,H,X){let $=encodeURIComponent(Z.name),L=this.normalizeVersionForBadges(H),W=this.normalizeVersionForBadges(X),K=encodeURIComponent(L),R=encodeURIComponent(W);return{age:`[](https://docs.renovatebot.com/merge-confidence/)`,adoption:`[](https://docs.renovatebot.com/merge-confidence/)`,passing:`[](https://docs.renovatebot.com/merge-confidence/)`,confidence:`[](https://docs.renovatebot.com/merge-confidence/)`}}normalizeVersionForBadges(Z){let H=Z.replace(/^v/,"");if(H.split(".").length===2)H=`${H}.0`;return H}}class h{config;releaseNotesFetcher=new T;verbose=!1;constructor(Z){this.config=Z;this.verbose=Z?.verbose||C.env.BUDDY_BOT_VERBOSE==="true"}log(Z,H){if(this.verbose){let X=new Date().toISOString();if(console.log(`[${X}] PR-GEN: ${Z}`),H)console.log(JSON.stringify(H,null,2))}}async generatePullRequests(Z){let H=[];for(let X of Z){let $=this.generateTitle(X),L=await this.generateBody(X),W={number:0,title:$,body:L,head:`buddy/update-${X.name.toLowerCase().replace(/\s+/g,"-")}`,base:"main",state:"open",url:"",createdAt:new Date,updatedAt:new Date,author:"github-actions[bot]",reviewers:[],assignees:[],labels:this.generateLabels(X),draft:!1};H.push(W)}return H}generateLabels(Z){let H=["dependencies"];if(Z.updateType==="major")H.push("major");else if(Z.updateType==="minor")H.push("minor");else if(Z.updateType==="patch")H.push("patch");let X=Z.updates.filter((K)=>K.file==="package.json"||K.file.endsWith("/package.json")||K.file.endsWith("\\package.json")).length,$=Z.updates.filter((K)=>K.file.endsWith("composer.json")||K.file.endsWith("composer.lock")).length,L=Z.updates.filter((K)=>(K.file.includes(".yaml")||K.file.includes(".yml"))&&!K.file.includes(".github/workflows/")).length,W=Z.updates.filter((K)=>K.file.includes(".github/workflows/")).length;if(X>0)H.push("npm");if($>0)H.push("composer");if(L>0)H.push("system");if(W>0)H.push("github-actions");if(Z.updates.length===1){let K=Z.updates[0];H.push(K.name)}return H}generateTitle(Z){if(Z.updates.length===1){let $=Z.updates[0];return`chore(deps): update dependency ${$.name} to v${$.newVersion}`}let H=Z.updates.filter(($)=>$.updateType==="major").length,X=Z.updates.filter(($)=>$.updateType==="minor").length;if(H>0)return`chore(deps): update ${Z.updates.length} dependencies (major)`;else if(X>0)return`chore(deps): update ${Z.updates.length} dependencies (minor)`;else return`chore(deps): update ${Z.updates.length} dependencies (patch)`}async generateBody(Z){if(this.log("\uD83D\uDE80 Starting PR body generation",{groupName:Z.name,totalUpdates:Z.updates.length,updates:Z.updates.map((Q)=>({name:Q.name,file:Q.file,dependencyType:Q.dependencyType,currentVersion:Q.currentVersion,newVersion:Q.newVersion,updateType:Q.updateType}))}),Z.updates.length===0)this.log("\u26A0\uFE0F WARNING: No updates in group - this will result in sparse PR body",{groupName:Z.name,groupTitle:Z.title});let H=Z.updates.filter((Q)=>Q.file==="package.json"||Q.file.endsWith("/package.json")||Q.file.endsWith("\\package.json")).length,X=Z.updates.filter((Q)=>(Q.file.includes(".yaml")||Q.file.includes(".yml"))&&!Q.file.includes(".github/workflows/")).length,$=Z.updates.filter((Q)=>Q.file.includes(".github/workflows/")).length,L=Z.updates.filter((Q)=>Q.file.endsWith("composer.json")||Q.file.endsWith("composer.lock")).length;if(this.log("\uD83D\uDCCA Package type counts",{packageJsonCount:H,dependencyFileCount:X,githubActionsCount:$,composerCount:L,total:Z.updates.length}),Z.updates.length>0)this.log("\uD83D\uDD0D File path analysis",{files:Z.updates.map((Q)=>({name:Q.name,file:Q.file,isExactMatch:Q.file==="package.json",endsWithSlash:Q.file.endsWith("/package.json"),endsWithBackslash:Q.file.endsWith("\\package.json"),matchesAny:Q.file==="package.json"||Q.file.endsWith("/package.json")||Q.file.endsWith("\\package.json")}))});let W=`This PR contains the following updates:
`,K=Z.updates.length>1;if(K){if(W+=`## Package Updates Summary
`,W+=`| Type | Count |
`,W+=`|------|-------|
`,H>0)W+=`| \uD83D\uDCE6 NPM Packages | ${H} |
`;if(X>0)W+=`| \uD83D\uDD27 System Dependencies | ${X} |
`;if($>0)W+=`| \uD83D\uDE80 GitHub Actions | ${$} |
`;if(L>0)W+=`| \uD83D\uDC18 Composer Packages | ${L} |
`;W+=`| **Total** | **${Z.updates.length}** |
`}let R=Z.updates.filter((Q)=>Q.file==="package.json"||Q.file.endsWith("/package.json")||Q.file.endsWith("\\package.json")),A=Z.updates.filter((Q)=>Q.file.endsWith("composer.json")||Q.file.endsWith("composer.lock")),x=Z.updates.filter((Q)=>Q.file.includes(".yaml")||Q.file.includes(".yml")).filter((Q)=>!Q.file.includes(".github/workflows/")),D=Z.updates.filter((Q)=>Q.file.includes(".github/workflows/"));this.log("\uD83D\uDCDD Filtered updates by type",{packageJsonUpdates:{count:R.length,items:R.map((Q)=>({name:Q.name,file:Q.file}))},composerUpdates:{count:A.length,items:A.map((Q)=>({name:Q.name,file:Q.file}))},dependencyFileUpdates:{count:x.length,items:x.map((Q)=>({name:Q.name,file:Q.file}))},githubActionsUpdates:{count:D.length,items:D.map((Q)=>({name:Q.name,file:Q.file}))}});let q=D.reduce((Q,Y)=>{if(!Q.find((S)=>S.name===Y.name&&S.currentVersion===Y.currentVersion&&S.newVersion===Y.newVersion)){let S=[...new Set(D.filter((O)=>O.name===Y.name&&O.currentVersion===Y.currentVersion&&O.newVersion===Y.newVersion).map((O)=>O.file))];Q.push({...Y,file:S.join(", ")})}return Q},[]),M=A.reduce((Q,Y)=>{if(!Q.find((S)=>S.name===Y.name&&S.currentVersion===Y.currentVersion&&S.newVersion===Y.newVersion))Q.push(Y);return Q},[]),E=new Map,I=new Map;if(R.length>0){if(this.log("\u2705 Generating npm Dependencies section",{packageCount:R.length,packages:R.map((Q)=>Q.name)}),K||H<Z.updates.length)W+=`## \uD83D\uDCE6 npm Dependencies
`;if(W+=`
`,R.length>1)W+=`*${R.length} packages will be updated*
`;W+=`| Package | Change | Age | Adoption | Passing | Confidence |
`,W+=`|---|---|---|---|---|---|
`;for(let Q of R)try{let Y=Q.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,""),_=await this.releaseNotesFetcher.fetchPackageInfo(Y,Q.currentVersion,Q.newVersion);E.set(Q.name,_)}catch(Y){console.warn(`Failed to fetch info for ${Q.name}:`,Y)}for(let Q of R){let _=E.get(Q.name)?.packageInfo||{name:Q.name},S=Q.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,""),O;if(_.repository?.url){let z=this.getRepositorySourceUrl(_.repository.url,S),J=this.getRepositorySourceUrl(_.repository.url,S,"HEAD");O=`[${S}](${z}) ([source](${J}))`}else if(S.startsWith("@types/")){let z=S.replace("@types/",""),J=`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/${z}`,V=`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/${z}`;O=`[${S}](${J}) ([source](${V}))`}else O=`[${S}](https://www.npmjs.com/package/${encodeURIComponent(S)})`;let G=`https://renovatebot.com/diffs/npm/${encodeURIComponent(S)}/${Q.currentVersion}/${Q.newVersion}`,B=`[\`${Q.currentVersion}\` -> \`${Q.newVersion}\`](${G})`,j=this.releaseNotesFetcher.generatePackageBadges({..._,name:S},Q.currentVersion,Q.newVersion);W+=`| ${O} | ${B} | ${j.age} | ${j.adoption} | ${j.passing} | ${j.confidence} |
`}W+=`
`}if(M.length>0){for(let Q of M)try{let Y=await this.releaseNotesFetcher.fetchComposerPackageInfo(Q.name);I.set(Q.name,Y)}catch(Y){console.warn(`Failed to fetch Composer info for ${Q.name}:`,Y)}if(K||L<Z.updates.length)W+=`## \uD83D\uDC18 PHP/Composer Dependencies
`;if(W+=`
`,M.length>1)W+=`*${M.length} packages will be updated*
`;W+=`| Package | Change | Age | Adoption | Passing | Confidence | Type | Update |
`,W+=`|---|---|---|---|---|---|---|---|
`;for(let Q of M){let Y=I.get(Q.name)||{name:Q.name},_;if(Y.homepage&&Y.repository?.url){let J=this.getComposerRedirectSourceUrl(Y.repository.url,Q.name);_=`[${Q.name}](${Y.homepage}) ([source](${J}))`}else if(Y.repository?.url){let J=this.getComposerRedirectSourceUrl(Y.repository.url,Q.name);_=`[${Q.name}](${J})`}else _=`[${Q.name}](https://packagist.org/packages/${encodeURIComponent(Q.name)})`;let S=this.getConstraintStyleChange(Q.currentVersion,Q.newVersion),O=`https://renovatebot.com/diffs/packagist/${encodeURIComponent(Q.name)}/${Q.currentVersion}/${Q.newVersion}`,G=`[\`${S}\`](${O})`,B=this.releaseNotesFetcher.generateComposerBadges(Y,Q.currentVersion,Q.newVersion),j=Q.dependencyType||"require",z=Q.updateType||"minor";W+=`| ${_} | ${G} | ${B.age} | ${B.adoption} | ${B.passing} | ${B.confidence} | ${j} | ${z} |
`}W+=`
`}if(x.length>0){if(K||X<Z.updates.length)W+=`## \uD83D\uDD27 System Dependencies
`;W+=`
`;let Q=[...new Set(x.map((Y)=>Y.file))];if(x.length>1)W+=`*${x.length} packages will be updated across ${Q.length} file(s): ${Q.map((Y)=>`\`${Y.split("/").pop()}\``).join(", ")}*
`;W+=`| Package | Change | Type | File |
`,W+=`|---|---|---|---|
`;for(let Y of x){let _=Y.name==="bun.sh"?"bun.com":Y.name,S=Y.name==="bun.sh"?"https://bun.sh":`https://pkgx.com/pkg/${encodeURIComponent(Y.name)}`,O=`[${_}](${S})`,G=v(Y.currentVersion,Y.newVersion),B=G==="major"?"\uD83D\uDD34":G==="minor"?"\uD83D\uDFE1":"\uD83D\uDFE2",j=this.formatVersionChange(Y.currentVersion,Y.newVersion),z=Y.file.split("/").pop()||Y.file,J=this.config?.repository?.owner&&this.config?.repository?.name?`[\`${z}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Y.file})`:`\`${z}\``;W+=`| ${O} | ${j} | ${B} ${G} | ${J} |
`}W+=`
`}if(q.length>0){if(K||$<Z.updates.length)W+=`## \uD83D\uDE80 GitHub Actions
`;if(W+=`
`,q.length>1)W+=`*${q.length} actions will be updated*
`;W+=`| Action | Change | Type | Files |
`,W+=`|---|---|---|---|
`;for(let Q of q){let Y=`https://github.com/${Q.name}`,_=`[${Q.name}](${Y})`,S=v(Q.currentVersion,Q.newVersion),O=S==="major"?"\uD83D\uDD34":S==="minor"?"\uD83D\uDFE1":"\uD83D\uDFE2",G=this.formatVersionChange(Q.currentVersion,Q.newVersion),B=Q.file.includes(", ")?Q.file.split(", ").map((j)=>{let z=j.split("/").pop()||j;return this.config?.repository?.owner&&this.config?.repository?.name?`[\`${z}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${j})`:`\`${z}\``}).join(", "):(()=>{let j=Q.file.split("/").pop()||Q.file;return this.config?.repository?.owner&&this.config?.repository?.name?`[\`${j}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Q.file})`:`\`${j}\``})();W+=`| ${_} | ${G} | ${O} ${S} | ${B} |
`}W+=`
`}W+=`
---
`,W+=`### Release Notes
`;for(let Q of R){let Y=E.get(Q.name),_=Y?.packageInfo||{name:Q.name},S=Y?.releaseNotes||[],O=Y?.compareUrl;W+=`<details>
`;let G,B=Q.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,"");if(_.repository?.url)G=`${this.getRepositoryName(_.repository.url)} (${B})`;else G=B;if(W+=`<summary>${G}</summary>
`,W+=`**${Q.currentVersion} -> ${Q.newVersion}**
`,S.length>0)for(let j of S.slice(0,3)){if(W+=`### [\`${j.version}\`](${j.htmlUrl})
`,O)W+=`[Compare Source](${O})
`;if(j.body){let z=this.cleanReleaseBody(j.body);W+=`${z}
`}if(j.author)W+=`*Released by [${j.author}](https://github.com/${j.author}) on ${new Date(j.date).toLocaleDateString()}*
`}else{if(O)W+=`[Compare Source](${O})
`;if(_.description)W+=`${_.description}
`;if(_.repository?.url){let j=this.getRepositoryName(_.repository.url);if(j)W+=`\uD83D\uDCD6 [View Release Notes](https://github.com/${j}/releases)
`,W+=`\uD83D\uDD17 [View Changelog](https://github.com/${j}/blob/main/CHANGELOG.md)
`}else W+=`\uD83D\uDCE6 [View on npm](https://www.npmjs.com/package/${encodeURIComponent(B)})
`;if(Q.releaseNotesUrl)W+=`[Release Notes](${Q.releaseNotesUrl})
`;if(Q.changelogUrl)W+=`[Changelog](${Q.changelogUrl})
`}W+=`</details>
`}let F=x.filter((Q)=>!R.some((Y)=>Y.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,"")===Q.name));for(let Q of F){let Y=Q.name==="bun.sh"?"bun.com":Q.name;W+=`<details>
`,W+=`<summary>${Y}</summary>
`;let _=this.formatVersionChange(Q.currentVersion,Q.newVersion).replace(/`/g,"");if(W+=`**${_}**
`,Q.file){let S=Q.file.split("/").pop()||Q.file,O=this.config?.repository?.owner&&this.config?.repository?.name?`\uD83D\uDCC1 **File**: [\`${S}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Q.file})
`:`\uD83D\uDCC1 **File**: \`${S}\`
`;W+=O}if(Q.name==="bun.sh")W+=`\uD83D\uDD17 **Release Notes**: [bun.sh](https://bun.sh)
`;else if(Q.name.includes(".org")||Q.name.includes(".net")||Q.name.includes(".com")){let S=Q.name.split("/")[0]||Q.name;W+=`\uD83D\uDD17 **Package Info**: [pkgx.com](https://pkgx.com/pkg/${encodeURIComponent(Q.name)})
`,W+=`\uD83C\uDF10 **Official Site**: [${S}](https://${S})
`}else W+=`\uD83D\uDD17 **Package Info**: [pkgx.com](https://pkgx.com/pkg/${encodeURIComponent(Q.name)})
`;W+=`</details>
`}for(let Q of M){W+=`<details>
`,W+=`<summary>${Q.name}</summary>
`;let Y=this.formatVersionChange(Q.currentVersion,Q.newVersion).replace(/`/g,"");W+=`**${Y}**
`,W+=`Visit [${Q.name}](https://packagist.org/packages/${encodeURIComponent(Q.name)}) on Packagist for more information.
`,W+=`</details>
`}for(let Q of q){W+=`<details>
`,W+=`<summary>${Q.name}</summary>
`;let Y=this.formatVersionChange(Q.currentVersion,Q.newVersion).replace(/`/g,"");W+=`**${Y}**
`,W+=`Visit [${Q.name}](https://github.com/${Q.name}/releases) for release notes.
`,W+=`</details>
`}if(W+=`---
`,E.size>0||M.length>0||F.length>0||q.length>0){W+=`### \uD83D\uDCCA Package Statistics
`;for(let Q of R){let _=E.get(Q.name)?.packageInfo;if(_?.weeklyDownloads)W+=`- **${Q.name}**: ${_.weeklyDownloads.toLocaleString()} weekly downloads
`}for(let Q of M)W+=`- **${Q.name}**: PHP package available on Packagist
`;for(let Q of F){let Y=Q.name==="bun.sh"?"bun.com":Q.name;if(Q.name==="bun.sh")W+=`- **${Y}**: Popular JavaScript runtime and package manager
`;else W+=`- **${Y}**: Available via pkgx package manager
`}for(let Q of q)W+=`- **${Q.name}**: GitHub Action for workflow automation
`;W+=`
---
`}W+=`### Configuration
`,W+=`\uD83D\uDCC5 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
`,W+=`\uD83D\uDEA6 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
`,W+=`\u267B **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
`;let w=Z.updates.length===1?"this update again":"these updates again";W+=`\uD83D\uDD15 **Ignore**: Close this PR and you won't be reminded about ${w}.
`,W+=`---
`,W+=` - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
`,W+=`---
`,W+="This PR was generated by [Buddy](https://github.com/stacksjs/buddy-bot) \uD83E\uDD16";let P=60000;if(W.length>P){let Q=W.substring(0,P),Y=Q.lastIndexOf("</details>");if(Y>0)W=`${Q.substring(0,Y+10)}
---
**Note**: This PR body was truncated due to GitHub's character limit. View the full details in the individual commits.
`;else W=`${Q}
---
**Note**: This PR body was truncated due to GitHub's character limit.
`;W+="This PR was generated by [Buddy](https://github.com/stacksjs/buddy-bot) \uD83E\uDD16"}if(this.log("\u2705 PR body generation complete",{bodyLength:W.length,hasNpmSection:W.includes("## \uD83D\uDCE6 npm Dependencies"),hasComposerSection:W.includes("## \uD83D\uDC18 PHP/Composer Dependencies"),hasSystemSection:W.includes("## \uD83D\uDD27 System Dependencies"),hasGitHubActionsSection:W.includes("## \uD83D\uDE80 GitHub Actions"),hasPackageTable:W.includes("| Package | Change | Age | Adoption | Passing | Confidence |"),hasReleaseNotes:W.includes("### Release Notes"),isSparse:W.length<1000,sections:{hasNpmPackagesInSummary:W.includes("\uD83D\uDCE6 NPM Packages"),hasComposerInSummary:W.includes("\uD83D\uDC18 Composer Packages"),hasSystemInSummary:W.includes("\uD83D\uDD27 System Dependencies"),hasGitHubActionsInSummary:W.includes("\uD83D\uDE80 GitHub Actions")}}),W.length<1000)this.log("\uD83D\uDEA8 WARNING: Generated PR body appears sparse (under 1000 chars)!");return W}getRepositoryName(Z){try{let H=Z.replace(/^git\+/,"").replace(/\.git$/,"").replace(/^git:\/\//,"https://").replace(/^ssh:\/\/git@/,"https://").replace(/^git@github\.com:/,"https://github.com/"),$=new URL(H).pathname.split("/").filter(Boolean);if($.length>=2)return`${$[0]}/${$[1]}`;return Z}catch{return Z}}getRepositorySourceUrl(Z,H,X="master"){try{let $=Z.replace(/^git\+/,"").replace(/\.git$/,"").replace(/^git:\/\//,"https://").replace(/^ssh:\/\/git@/,"https://").replace(/^git@github\.com:/,"https://github.com/"),L=new URL($);if(H.startsWith("@types/")&&L.pathname.includes("DefinitelyTyped")){let W=H.replace("@types/","");return`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/${X}/types/${W}`}if(L.hostname==="github.com")return`${$}/tree/${X}`;return $}catch{return Z}}getComposerSourceUrl(Z,H){try{let X=Z.replace(/^git\+/,"").replace(/\.git$/,"").replace(/^git:\/\//,"https://").replace(/^ssh:\/\/git@/,"https://").replace(/^git@github\.com:/,"https://github.com/"),$=new URL(X);if(H.startsWith("@types/")&&$.pathname.includes("DefinitelyTyped"))return`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/${H.replace("@types/","")}`;if($.hostname==="github.com")return`${X}/tree/master`;return X}catch{return Z}}getConstraintStyleChange(Z,H){let X=Z.replace(/^v/,""),$=H.replace(/^v/,""),L=`^${X}`,W=`^${$}`;return`${L} -> ${W}`}getComposerRedirectSourceUrl(Z,H){try{let X=Z.replace(/^git\+/,"").replace(/\.git$/,"").replace(/^git:\/\//,"https://").replace(/^ssh:\/\/git@/,"https://").replace(/^git@github\.com:/,"https://github.com/"),$=new URL(X);if(H.startsWith("@types/")&&$.pathname.includes("DefinitelyTyped"))return`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/${H.replace("@types/","")}`;if($.hostname==="github.com"){let L=$.pathname.split("/").filter((W)=>W);if(L.length>=2){let W=L[0],K=L[1];return`https://redirect.github.com/${W}/${K}`}}return X}catch{return Z}}cleanReleaseBody(Z){let H=Z.replace(/\r\n/g,`
`).replace(/\n{3,}/g,`
`).trim();if(H=this.sanitizeMentions(H),H.length>1000)H=`${H.substring(0,1000)}...
*[View full release notes]*`;return H}sanitizeMentions(Z){let H=[],X=[],$=Z.replace(/```[\s\S]*?```/g,(L)=>{return H.push(L),`__FENCE_PLACEHOLDER_${H.length-1}__`});return $=$.replace(/`[^`\n]+`/g,(L)=>{return X.push(L),`__INLINE_CODE_PLACEHOLDER_${X.length-1}__`}),$=$.replace(/(?<![\w./])@([A-Z0-9-]+)(?![A-Z0-9-])/gi,(L,W)=>{return`[${W}](https://github.com/${W})`}),$=$.replace(/__INLINE_CODE_PLACEHOLDER_(\d+)__/g,(L,W)=>X[Number(W)]),$=$.replace(/__FENCE_PLACEHOLDER_(\d+)__/g,(L,W)=>H[Number(W)]),$}generateCustomTemplate(Z,H,X={}){let $=H,W={...{"{title}":this.generateTitle(Z),"{package_count}":Z.updates.length.toString(),"{update_type}":Z.updateType,"{packages}":Z.updates.map((K)=>K.name).join(", "),"{date}":new Date().toISOString().split("T")[0]},...X};for(let[K,R]of Object.entries(W))$=$.replace(new RegExp(K,"g"),R);return $}formatVersionChange(Z,H){let X=Z.match(/^(\D+)/),$=X?X[1]:"";if($){let L=Z.replace(/^\D+/,""),W=H.replace(/^\D+/,"");return`\`${$}${L}\` \u2192 \`${$}${W}\``}return`\`${Z}\` \u2192 \`${H}\``}getVersionChangeDescription(Z,H,X){return{major:"\uD83D\uDD34 Breaking changes possible",minor:"\uD83D\uDFE1 New features added",patch:"\uD83D\uDFE2 Bug fixes & patches"}[X]}}export{h as PullRequestGenerator};
export{T as k,h as l};
//# debugId=7BF98C8E849168AC64756E2164756E21