UNPKG

buddy-bot

Version:

Automated & optimized dependency updates for JavaScript & TypeScript projects. Like Renovate & Dependabot.

187 lines (115 loc) 28.2 kB
// @bun import{z as I}from"./chunk-b5gyp8hn.js";import"./chunk-3j3vxazw.js";import"./chunk-f5bh0qas.js";import"./chunk-72vxav2c.js";import"./chunk-knkqp939.js";import"./chunk-jah7r3bb.js";import"./chunk-32dgq3f4.js";import"./chunk-8wk4nc61.js";import"./chunk-vd04qkw4.js";import"./chunk-pvk7bcsa.js";import m from"process";import U from"process";class C{userAgent="Buddy-Bot/1.0.0 (https://github.com/stacksjs/buddy)";async fetchPackageInfo(Z,H,X){if(U.env.APP_ENV==="test"){let K={stripe:{owner:"stripe",repo:"stripe-node"},typescript:{owner:"microsoft",repo:"TypeScript"},react:{owner:"facebook",repo:"react"}}[Z]||{owner:"example",repo:Z};return{packageInfo:{name:Z,description:`Package ${Z} description`,repository:{type:"git",url:`https://github.com/${K.owner}/${K.repo}`}},releaseNotes:[],changelog:[],compareUrl:`https://github.com/${K.owner}/${K.repo}/compare/v${H}...v${X}`}}try{let $=await this.fetchNpmPackageInfo(Z),K=[],R=[],W;if($.repository?.url){let L=this.parseGitHubUrl($.repository.url);if(L)K=await this.fetchGitHubReleases(L.owner,L.repo,H,X),W=this.generateCompareUrl(L.owner,L.repo,H,X),R=await this.fetchChangelog(L.owner,L.repo)}return{packageInfo:$,releaseNotes:K,changelog:R,compareUrl:W}}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 Error(`NPM registry responded with ${H.status}`);let X=await H.json(),$=X["dist-tags"]?.latest,K;try{let R=await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(Z)}`,{headers:{"User-Agent":this.userAgent}});if(R.ok)K=(await R.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:K,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 K=await fetch(`https://api.github.com/repos/${Z}/${H}/releases?per_page=50`,{headers:{"User-Agent":this.userAgent}});if(!K.ok)return[];return(await K.json()).filter((W)=>{let L=W.tag_name.replace(/^v/,"");return this.isVersionBetween(L,X,$)}).map((W)=>({version:W.tag_name,date:W.published_at,title:W.name||W.tag_name,body:W.body||"",htmlUrl:W.html_url,compareUrl:this.generateCompareUrl(Z,H,X,W.tag_name),author:W.author?.login,isPrerelease:W.prerelease,assets:W.assets?.map((L)=>({name:L.name,downloadUrl:L.browser_download_url,size:L.size,contentType:L.content_type}))||[]}))}catch(K){return console.warn(`Failed to fetch GitHub releases for ${Z}/${H}:`,K),[]}}async fetchChangelog(Z,H){let X=["CHANGELOG.md","CHANGELOG.rst","HISTORY.md","RELEASES.md"];for(let $ of X)try{let K=await fetch(`https://api.github.com/repos/${Z}/${H}/contents/${$}`,{headers:{"User-Agent":this.userAgent}});if(K.ok){let R=await K.json();if(R.content){let W=atob(R.content);return this.parseChangelog(W)}}}catch{}return[]}parseChangelog(Z){let H=[],X=Z.split(` `),$=null,K=null;for(let R of X){let W=R.match(/^##\s*\[?([^\]]+)\]?\s*-?\s*(.*)/);if(W){if($)H.push($);$={version:W[1],date:W[2].trim(),changes:{}},K=null;continue}let L=R.match(/^###\s*(.+)/);if(L&&$){K=L[1].toLowerCase();continue}let G=R.match(/^[-*]\s*(.+)/);if(G&&$&&K){if(!$.changes)$.changes={};let M=K;if(!$.changes[M])$.changes[M]=[];$.changes[M].push(G[1])}}if($)H.push($);return H.slice(0,10)}generateCompareUrl(Z,H,X,$){let K=X.startsWith("v")?X:`v${X}`,R=$.startsWith("v")?$:`v${$}`;return`https://github.com/${Z}/${H}/compare/${K}...${R}`}isVersionBetween(Z,H,X){let $=Z.replace(/^v/,""),K=H.replace(/^v/,""),R=X.replace(/^v/,"");return $===R||$>K}generatePackageBadges(Z,H,X){let $=encodeURIComponent(Z.name),K=this.normalizeVersionForBadges(H),R=this.normalizeVersionForBadges(X),W=encodeURIComponent(K),L=encodeURIComponent(R);return{age:`[![age](https://developer.mend.io/api/mc/badges/age/npm/${$}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,adoption:`[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/${$}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,passing:`[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/${$}/${W}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,confidence:`[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/${$}/${W}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`}}async fetchComposerPackageInfo(Z){if(U.env.APP_ENV==="test")return{name:Z,description:`PHP package ${Z} description`,homepage:`https://packagist.org/packages/${Z}`,repository:{type:"git",url:`https://github.com/example/${Z}`},license:"MIT"};try{let H=await fetch(`https://packagist.org/packages/${encodeURIComponent(Z)}.json`,{headers:{"User-Agent":this.userAgent}});if(!H.ok)throw Error(`Packagist responded with ${H.status}`);let $=(await H.json()).package;if(!$)return{name:Z};let K=Object.keys($.versions||{}),R=K.find((L)=>!L.includes("dev")&&!L.includes("alpha")&&!L.includes("beta"))||K[0],W=$.versions[R]||{};return{name:$.name,description:W.description,homepage:W.homepage,repository:W.source?{type:"git",url:W.source.url}:void 0,license:W.license?.[0]||W.license,author:W.authors?.[0],keywords:W.keywords,lastPublish:W.time}}catch(H){return console.warn(`Failed to fetch Packagist info for ${Z}:`,H),{name:Z}}}generateComposerBadges(Z,H,X){let $=encodeURIComponent(Z.name),K=this.normalizeVersionForBadges(H),R=this.normalizeVersionForBadges(X),W=encodeURIComponent(K),L=encodeURIComponent(R);return{age:`[![age](https://developer.mend.io/api/mc/badges/age/packagist/${$}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,adoption:`[![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/${$}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,passing:`[![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/${$}/${W}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,confidence:`[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/${$}/${W}/${L}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`}}normalizeVersionForBadges(Z){let H=Z.replace(/^v/,"");if(H.split(".").length===2)H=`${H}.0`;return H}}class y{config;releaseNotesFetcher=new C;verbose=!1;constructor(Z){this.config=Z;this.verbose=Z?.verbose||m.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),K=await this.generateBody(X),R={number:0,title:$,body:K,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(R)}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((L)=>L.file==="package.json"||L.file.endsWith("/package.json")||L.file.endsWith("\\package.json")).length,$=Z.updates.filter((L)=>L.file.endsWith("composer.json")||L.file.endsWith("composer.lock")).length,K=Z.updates.filter((L)=>L.file.endsWith("build.zig.zon")).length,R=Z.updates.filter((L)=>(L.file.includes(".yaml")||L.file.includes(".yml"))&&!L.file.includes(".github/workflows/")).length,W=Z.updates.filter((L)=>L.file.includes(".github/workflows/")).length;if(X>0)H.push("npm");if($>0)H.push("composer");if(K>0)H.push("zig");if(R>0)H.push("system");if(W>0)H.push("github-actions");if(Z.updates.length===1){let L=Z.updates[0];H.push(L.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,K=Z.updates.filter((Q)=>Q.file.endsWith("composer.json")||Q.file.endsWith("composer.lock")).length,R=Z.updates.filter((Q)=>Q.file.endsWith("build.zig.zon")).length;if(this.log("\uD83D\uDCCA Package type counts",{packageJsonCount:H,dependencyFileCount:X,githubActionsCount:$,composerCount:K,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: `,L=Z.updates.length>1;if(L){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(K>0)W+=`| \uD83D\uDC18 Composer Packages | ${K} | `;if(R>0)W+=`| \u26A1 Zig Packages | ${R} | `;W+=`| **Total** | **${Z.updates.length}** | `}let G=Z.updates.filter((Q)=>Q.file==="package.json"||Q.file.endsWith("/package.json")||Q.file.endsWith("\\package.json")),M=Z.updates.filter((Q)=>Q.file.endsWith("composer.json")||Q.file.endsWith("composer.lock")),D=Z.updates.filter((Q)=>Q.file.endsWith("build.zig.zon")),A=Z.updates.filter((Q)=>Q.file.includes(".yaml")||Q.file.includes(".yml")).filter((Q)=>!Q.file.includes(".github/workflows/")),v=Z.updates.filter((Q)=>Q.file.includes(".github/workflows/"));this.log("\uD83D\uDCDD Filtered updates by type",{packageJsonUpdates:{count:G.length,items:G.map((Q)=>({name:Q.name,file:Q.file}))},composerUpdates:{count:M.length,items:M.map((Q)=>({name:Q.name,file:Q.file}))},dependencyFileUpdates:{count:A.length,items:A.map((Q)=>({name:Q.name,file:Q.file}))},githubActionsUpdates:{count:v.length,items:v.map((Q)=>({name:Q.name,file:Q.file}))}});let F=v.reduce((Q,Y)=>{if(!Q.find((j)=>j.name===Y.name&&j.currentVersion===Y.currentVersion&&j.newVersion===Y.newVersion)){let j=[...new Set(v.filter((_)=>_.name===Y.name&&_.currentVersion===Y.currentVersion&&_.newVersion===Y.newVersion).map((_)=>_.file))];Q.push({...Y,file:j.join(", ")})}return Q},[]),E=M.reduce((Q,Y)=>{if(!Q.find((j)=>j.name===Y.name&&j.currentVersion===Y.currentVersion&&j.newVersion===Y.newVersion))Q.push(Y);return Q},[]),J=new Map,h=new Map;if(G.length>0){if(this.log("\u2705 Generating npm Dependencies section",{packageCount:G.length,packages:G.map((_)=>_.name)}),L||H<Z.updates.length)W+=`## \uD83D\uDCE6 npm Dependencies `;if(W+=`![npm](https://img.shields.io/badge/npm-CB3837?style=flat&logo=npm&logoColor=white) `,G.length>1)W+=`*${G.length} packages will be updated* `;W+=`| Package | Change | Age | Adoption | Passing | Confidence | `,W+=`|---|---|---|---|---|---| `;let Q=Date.now();this.log(`\u23F1\uFE0F Starting to fetch info for ${G.length} npm packages in parallel...`);let Y=G.map(async(_)=>{let z=Date.now();try{let S=_.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,""),O=await this.releaseNotesFetcher.fetchPackageInfo(S,_.currentVersion,_.newVersion);return this.log(`\u23F1\uFE0F Fetched ${S} in ${Date.now()-z}ms`),{name:_.name,result:O}}catch(S){return console.warn(`Failed to fetch info for ${_.name}:`,S),null}});(await Promise.all(Y)).forEach((_)=>{if(_)J.set(_.name,_.result)});let j=Date.now()-Q;this.log(`\u23F1\uFE0F Total npm package info fetch took ${j}ms for ${G.length} packages (avg: ${Math.round(j/G.length)}ms per package)`),console.log(`\u23F1\uFE0F [PR-GEN] npm package info fetch: ${j}ms for ${G.length} packages (PARALLEL)`);for(let _ of G){let S=J.get(_.name)?.packageInfo||{name:_.name},O=_.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,""),x;if(S.repository?.url){let P=this.getRepositorySourceUrl(S.repository.url,O),V=this.getRepositorySourceUrl(S.repository.url,O,"HEAD");x=`[${O}](${P}) ([source](${V}))`}else if(O.startsWith("@types/")){let P=O.replace("@types/",""),V=`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/${P}`,b=`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/${P}`;x=`[${O}](${V}) ([source](${b}))`}else x=`[${O}](https://www.npmjs.com/package/${encodeURIComponent(O)})`;let q=`https://renovatebot.com/diffs/npm/${encodeURIComponent(O)}/${_.currentVersion}/${_.newVersion}`,k=`[\`${_.currentVersion}\` -> \`${_.newVersion}\`](${q})`,T=this.releaseNotesFetcher.generatePackageBadges({...S,name:O},_.currentVersion,_.newVersion);W+=`| ${x} | ${k} | ${T.age} | ${T.adoption} | ${T.passing} | ${T.confidence} | `}W+=` `}if(E.length>0){for(let Q of E)try{let Y=await this.releaseNotesFetcher.fetchComposerPackageInfo(Q.name);h.set(Q.name,Y)}catch(Y){console.warn(`Failed to fetch Composer info for ${Q.name}:`,Y)}if(L||K<Z.updates.length)W+=`## \uD83D\uDC18 PHP/Composer Dependencies `;if(W+=`![composer](https://img.shields.io/badge/composer-885630?style=flat&logo=composer&logoColor=white) `,E.length>1)W+=`*${E.length} packages will be updated* `;W+=`| Package | Change | Age | Adoption | Passing | Confidence | Type | Update | `,W+=`|---|---|---|---|---|---|---|---| `;for(let Q of E){let Y=h.get(Q.name)||{name:Q.name},B;if(Y.homepage&&Y.repository?.url){let q=this.getComposerRedirectSourceUrl(Y.repository.url,Q.name);B=`[${Q.name}](${Y.homepage}) ([source](${q}))`}else if(Y.repository?.url){let q=this.getComposerRedirectSourceUrl(Y.repository.url,Q.name);B=`[${Q.name}](${q})`}else B=`[${Q.name}](https://packagist.org/packages/${encodeURIComponent(Q.name)})`;let j=this.getConstraintStyleChange(Q.currentVersion,Q.newVersion),_=`https://renovatebot.com/diffs/packagist/${encodeURIComponent(Q.name)}/${Q.currentVersion}/${Q.newVersion}`,z=`[\`${j}\`](${_})`,S=this.releaseNotesFetcher.generateComposerBadges(Y,Q.currentVersion,Q.newVersion),O=Q.dependencyType||"require",x=Q.updateType||"minor";W+=`| ${B} | ${z} | ${S.age} | ${S.adoption} | ${S.passing} | ${S.confidence} | ${O} | ${x} | `}W+=` `}if(D.length>0){if(L||R<Z.updates.length)W+=`## \u26A1 Zig Dependencies `;if(W+=`![zig](https://img.shields.io/badge/Zig-F7A41D?style=flat&logo=zig&logoColor=white) `,D.length>1)W+=`*${D.length} packages will be updated* `;W+=`| Package | Change | Type | File | `,W+=`|---|---|---|---| `;for(let Q of D){let Y,B=Q.metadata;if(B?.url){let x=B.url.match(/https?:\/\/github\.com\/([^/]+\/[^/]+)/);if(x){let q=`https://github.com/${x[1]}`;Y=`[${Q.name}](${q})`}else Y=Q.name}else Y=Q.name;let j=I(Q.currentVersion,Q.newVersion),_=j==="major"?"\uD83D\uDD34":j==="minor"?"\uD83D\uDFE1":"\uD83D\uDFE2",z=this.formatVersionChange(Q.currentVersion,Q.newVersion),S=Q.file.split("/").pop()||Q.file,O=this.config?.repository?.owner&&this.config?.repository?.name?`[\`${S}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Q.file})`:`\`${S}\``;W+=`| ${Y} | ${z} | ${_} ${j} | ${O} | `}W+=` `}if(A.length>0){if(L||X<Z.updates.length)W+=`## \uD83D\uDD27 System Dependencies `;W+=`![system](https://img.shields.io/badge/system-4CAF50?style=flat&logo=linux&logoColor=white) `;let Q=[...new Set(A.map((Y)=>Y.file))];if(A.length>1)W+=`*${A.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 A){let B=Y.name==="bun.sh"?"bun.com":Y.name,j=Y.name==="bun.sh"?"https://bun.sh":`https://pkgx.com/pkg/${encodeURIComponent(Y.name)}`,_=`[${B}](${j})`,z=I(Y.currentVersion,Y.newVersion),S=z==="major"?"\uD83D\uDD34":z==="minor"?"\uD83D\uDFE1":"\uD83D\uDFE2",O=this.formatVersionChange(Y.currentVersion,Y.newVersion),x=Y.file.split("/").pop()||Y.file,q=this.config?.repository?.owner&&this.config?.repository?.name?`[\`${x}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Y.file})`:`\`${x}\``;W+=`| ${_} | ${O} | ${S} ${z} | ${q} | `}W+=` `}if(F.length>0){if(L||$<Z.updates.length)W+=`## \uD83D\uDE80 GitHub Actions `;if(W+=`![github-actions](https://img.shields.io/badge/GitHub%20Actions-2088FF?style=flat&logo=github-actions&logoColor=white) `,F.length>1)W+=`*${F.length} actions will be updated* `;W+=`| Action | Change | Type | Files | `,W+=`|---|---|---|---| `;for(let Q of F){let Y=`https://github.com/${Q.name}`,B=`[${Q.name}](${Y})`,j=I(Q.currentVersion,Q.newVersion),_=j==="major"?"\uD83D\uDD34":j==="minor"?"\uD83D\uDFE1":"\uD83D\uDFE2",z=this.formatVersionChange(Q.currentVersion,Q.newVersion),S=Q.file.includes(", ")?Q.file.split(", ").map((O)=>{let x=O.split("/").pop()||O;return this.config?.repository?.owner&&this.config?.repository?.name?`[\`${x}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${O})`:`\`${x}\``}).join(", "):(()=>{let O=Q.file.split("/").pop()||Q.file;return this.config?.repository?.owner&&this.config?.repository?.name?`[\`${O}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Q.file})`:`\`${O}\``})();W+=`| ${B} | ${z} | ${_} ${j} | ${S} | `}W+=` `}W+=` --- `,W+=`### Release Notes `;for(let Q of G){let Y=J.get(Q.name),B=Y?.packageInfo||{name:Q.name},j=Y?.releaseNotes||[],_=Y?.compareUrl;W+=`<details> `;let z,S=Q.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,"");if(B.repository?.url)z=`${this.getRepositoryName(B.repository.url)} (${S})`;else z=S;if(W+=`<summary>${z}</summary> `,W+=`**${Q.currentVersion} -> ${Q.newVersion}** `,j.length>0)for(let O of j.slice(0,3)){if(W+=`### [\`${O.version}\`](${O.htmlUrl}) `,_)W+=`[Compare Source](${_}) `;if(O.body){let x=this.cleanReleaseBody(O.body);W+=`${x} `}if(O.author)W+=`*Released by [${O.author}](https://github.com/${O.author}) on ${new Date(O.date).toLocaleDateString()}* `}else{if(_)W+=`[Compare Source](${_}) `;if(B.description)W+=`${B.description} `;if(B.repository?.url){let O=this.getRepositoryName(B.repository.url);if(O)W+=`\uD83D\uDCD6 [View Release Notes](https://github.com/${O}/releases) `,W+=`\uD83D\uDD17 [View Changelog](https://github.com/${O}/blob/main/CHANGELOG.md) `}else W+=`\uD83D\uDCE6 [View on npm](https://www.npmjs.com/package/${encodeURIComponent(S)}) `;if(Q.releaseNotesUrl)W+=`[Release Notes](${Q.releaseNotesUrl}) `;if(Q.changelogUrl)W+=`[Changelog](${Q.changelogUrl}) `}W+=`</details> `}let w=A.filter((Q)=>!G.some((Y)=>Y.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,"")===Q.name));for(let Q of w){let Y=Q.name==="bun.sh"?"bun.com":Q.name;W+=`<details> `,W+=`<summary>${Y}</summary> `;let B=this.formatVersionChange(Q.currentVersion,Q.newVersion).replace(/`/g,"");if(W+=`**${B}** `,Q.file){let j=Q.file.split("/").pop()||Q.file,_=this.config?.repository?.owner&&this.config?.repository?.name?`\uD83D\uDCC1 **File**: [\`${j}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${Q.file}) `:`\uD83D\uDCC1 **File**: \`${j}\` `;W+=_}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 j=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**: [${j}](https://${j}) `}else W+=`\uD83D\uDD17 **Package Info**: [pkgx.com](https://pkgx.com/pkg/${encodeURIComponent(Q.name)}) `;W+=`</details> `}for(let Q of E){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 F){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+=`--- `,J.size>0||E.length>0||w.length>0||F.length>0){W+=`### \uD83D\uDCCA Package Statistics `;for(let Q of G){let B=J.get(Q.name)?.packageInfo;if(B?.weeklyDownloads)W+=`- **${Q.name}**: ${B.weeklyDownloads.toLocaleString()} weekly downloads `;else W+=`- **${Q.name}**: npm package with weekly downloads tracked on npmjs.com `}for(let Q of E)W+=`- **${Q.name}**: PHP package available on Packagist `;for(let Q of w){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 F)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 N=Z.updates.length===1?"this update again":"these updates again";W+=`\uD83D\uDD15 **Ignore**: Close this PR and you won't be reminded about ${N}. `,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 f=60000;if(W.length>f){let Q=W.substring(0,f),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/"),K=new URL($);if(H.startsWith("@types/")&&K.pathname.includes("DefinitelyTyped")){let R=H.replace("@types/","");return`https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/${X}/types/${R}`}if(K.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/,""),K=`^${X}`,R=`^${$}`;return`${K} -> ${R}`}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 K=$.pathname.split("/").filter((R)=>R);if(K.length>=2){let R=K[0],W=K[1];return`https://redirect.github.com/${R}/${W}`}}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,(K)=>{return H.push(K),`__FENCE_PLACEHOLDER_${H.length-1}__`});return $=$.replace(/`[^`\n]+`/g,(K)=>{return X.push(K),`__INLINE_CODE_PLACEHOLDER_${X.length-1}__`}),$=$.replace(/(?<![\w./])@([A-Z0-9-]+)(?![A-Z0-9-])/gi,(K,R)=>{return`[${R}](https://github.com/${R})`}),$=$.replace(/__INLINE_CODE_PLACEHOLDER_(\d+)__/g,(K,R)=>X[Number(R)]),$=$.replace(/__FENCE_PLACEHOLDER_(\d+)__/g,(K,R)=>H[Number(R)]),$}generateCustomTemplate(Z,H,X={}){let $=H,R={...{"{title}":this.generateTitle(Z),"{package_count}":Z.updates.length.toString(),"{update_type}":Z.updateType,"{packages}":Z.updates.map((W)=>W.name).join(", "),"{date}":new Date().toISOString().split("T")[0]},...X};for(let[W,L]of Object.entries(R))$=$.replace(new RegExp(W,"g"),L);return $}formatVersionChange(Z,H){let X=Z.match(/^(\D+)/),$=X?X[1]:"";if($){let K=Z.replace(/^\D+/,""),R=H.replace(/^\D+/,"");return`\`${$}${K}\` \u2192 \`${$}${R}\``}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{y as PullRequestGenerator}; export{C as p,y as q}; //# debugId=911CAD440E66343D64756E2164756E21