@stacksjs/launchpad
Version:
Like Homebrew, but faster.
59 lines (49 loc) • 24.3 kB
JavaScript
// @bun
import{P as E}from"./chunk-kd525svk.js";import{va as V}from"./chunk-6qxbw28k.js";import{ab as j}from"./chunk-b9hx8gsj.js";import{bb as R,cb as M}from"./chunk-8pxdwzvm.js";import{Buffer as B}from"buffer";import{execSync as N}from"child_process";import*as X from"fs";import*as K from"path";import J from"process";class L{installPath;GITHUB_REPO="stacksjs/launchpad";GITHUB_API="https://api.github.com";BINARY_CACHE_DIR;constructor(Q){this.installPath=Q;this.BINARY_CACHE_DIR=K.join(Q,".cache","precompiled-binaries")}getPlatform(){let Q=J.platform;switch(Q){case"darwin":return"darwin";case"linux":return"linux";case"win32":return"windows";default:throw new Error(`Unsupported platform: ${Q}`)}}getArchitecture(){let Q=J.arch;switch(Q){case"x64":return"x86_64";case"arm64":return"arm64";default:throw new Error(`Unsupported architecture: ${Q}`)}}getOptimalConfiguration(){return"full-stack"}async hasCustomExtensions(){let Q=j.services?.php;if(Q?.strategy==="auto-detect")return{hasCustom:!1,customExtensions:[],reason:"Using auto-detection strategy"};if(Q?.strategy==="precompiled-binary")return{hasCustom:!1,customExtensions:[],reason:"Using precompiled binary strategy"};if(Q?.extensions)return console.warn("\u26A0\uFE0F Manual extension configuration is deprecated. Use auto-detection or manual configuration instead."),{hasCustom:!1,customExtensions:[],reason:"Legacy extension configuration detected"};return{hasCustom:!1,customExtensions:[],reason:"No custom extensions configured"}}isCompatibleWithFullStack(Q){let z=["cli","fpm","mbstring","opcache","intl","exif","bcmath","calendar","ftp","sysvmsg","sysvsem","sysvshm","wddx","pdo-mysql","pdo-pgsql","pdo-sqlite","mysqli","pgsql","sqlite3","curl","openssl","gd","soap","sockets","zip","bz2","readline","libxml","zlib","pcntl","posix","gettext","gmp","ldap","xsl","sodium","iconv","fileinfo","json","phar","filter","hash","session","tokenizer","ctype","dom","simplexml","xml","xmlreader","xmlwriter","shmop"];return Q.every((W)=>z.includes(W))}async detectFrameworkAndDatabase(){let Q=j.services?.php;if(Q?.configuration){if(j.verbose)console.log(`\uD83D\uDD27 Using manual configuration: ${Q.configuration}`);return Q.configuration}if(Q?.strategy==="auto-detect")try{let{PHPAutoDetector:z}=await import("./chunk-94hvyere.js"),W=new z,T=await W.analyzeProject();return console.log(W.getConfigurationExplanation(T)),T.recommendedConfig}catch(z){console.warn(`\u26A0\uFE0F Auto-detection failed: ${z instanceof Error?z.message:String(z)}`),console.warn("\uD83D\uDD04 Falling back to basic detection...")}try{let z=M("fs");if(z.existsSync("artisan")&&z.existsSync("composer.json"))try{let W=JSON.parse(z.readFileSync("composer.json","utf-8"));if(W.require?.["laravel/framework"]){if(z.existsSync(".env")){let Z=z.readFileSync(".env","utf-8");if(Z.includes("DB_CONNECTION=pgsql")||Z.includes("DB_CONNECTION=postgres"))return"laravel-postgres";else if(Z.includes("DB_CONNECTION=sqlite"))return"laravel-sqlite";return"full-stack"}return"full-stack"}if(W.require?.["laravel/lumen-framework"])return"api-only";if(z.existsSync("wp-config.php")||z.existsSync("wp-config-sample.php"))return"wordpress"}catch{}try{if(z.existsSync(".env")){let W=z.readFileSync(".env","utf-8");if(W.includes("DB_CONNECTION=pgsql")||W.includes("DB_CONNECTION=postgres"))return"laravel-postgres";if(W.includes("DB_CONNECTION=sqlite"))return"laravel-sqlite"}}catch{}if(z.existsSync("wp-config.php")||z.existsSync("wp-config-sample.php"))return"wordpress";return"full-stack"}catch{return"full-stack"}}async downloadManifest(){console.log("\uD83D\uDCCB Downloading precompiled binary manifest...");let Q=`${this.GITHUB_API}/repos/${this.GITHUB_REPO}/releases`;try{let z=await fetch(Q,{headers:{"User-Agent":"Launchpad Binary Downloader",Accept:"application/vnd.github.v3+json"}});if(!z.ok){if(z.status===403)throw new Error("GitHub API rate limit exceeded. Please try again later.");throw new Error(`GitHub API error: ${z.status} ${z.statusText}`)}let T=(await z.json()).filter((Y)=>Y.tag_name.startsWith("binaries-")).sort((Y,H)=>{let F=Number.parseInt(Y.tag_name.replace("binaries-",""),10);return Number.parseInt(H.tag_name.replace("binaries-",""),10)-F});if(T.length===0)throw new Error("No binaries releases found. The precompile workflow may not have run yet.");let Z=T[0];console.log(`\uD83D\uDCE6 Found binaries release: ${Z.tag_name}`);let A=Z.assets?.find((Y)=>Y.name==="manifest.json");if(!A)throw new Error("No manifest.json found in binaries release");let $=await fetch(A.browser_download_url);if(!$.ok)throw new Error(`Failed to download manifest: ${$.statusText}`);let G=await $.json();if(G.binaries=G.binaries.map((Y)=>{let H=Z.assets?.find((F)=>F.name===Y.filename);if(!H)console.warn(`\u26A0\uFE0F No asset found for binary: ${Y.filename}`);return{...Y,download_url:H?.browser_download_url||""}}).filter((Y)=>Y.download_url),console.log(`\uD83D\uDCCB Manifest loaded: ${G.binaries.length} binaries available`),G.binaries.length===0)throw new Error("No valid binaries found in manifest. The release may be incomplete.");return G}catch(z){throw new Error(`Failed to fetch binary manifest: ${z instanceof Error?z.message:String(z)}`)}}async findMatchingBinary(Q,z){let W=this.getPlatform(),T=this.getArchitecture(),Z=await this.detectFrameworkAndDatabase();console.log(`\uD83D\uDD0D Detected configuration: ${Z}`);let A=Q.binaries.filter(($)=>$.platform===W&&$.architecture===T&&$.configuration===Z);if(A.length===0){console.log(`\u26A0\uFE0F No exact match for ${Z}, trying fallbacks...`);let $=this.getFallbackConfigurations(Z);for(let G of $)if(A=Q.binaries.filter((Y)=>Y.platform===W&&Y.architecture===T&&Y.configuration===G),A.length>0){console.log(`\u2705 Using fallback configuration: ${G}`);break}}if(A.length===0)return null;if(z){let $=A.find((G)=>G.php_version===z);if($)return $}return A.sort(($,G)=>{let Y=$.php_version.split(".").map(Number),H=G.php_version.split(".").map(Number);for(let F=0;F<Math.max(Y.length,H.length);F++){let _=Y[F]||0,U=H[F]||0;if(_!==U)return U-_}return 0}),A[0]||null}getFallbackConfigurations(Q){switch(Q){case"laravel-mysql":return["enterprise","full-stack","laravel-postgres","laravel-sqlite"];case"laravel-postgres":return["enterprise","full-stack","laravel-mysql","laravel-sqlite"];case"laravel-sqlite":return["laravel-mysql","enterprise","full-stack","laravel-postgres"];case"api-only":return["laravel-mysql","enterprise","full-stack","laravel-postgres","laravel-sqlite"];case"wordpress":return["laravel-mysql","enterprise","full-stack"];case"enterprise":return["full-stack","laravel-mysql","laravel-postgres"];case"full-stack":return["enterprise","laravel-mysql","laravel-postgres"];default:return["laravel-mysql","enterprise","full-stack","laravel-postgres","laravel-sqlite"]}}generateDiscordErrorMessage(Q,z,W){return`
\uD83D\uDEA8 **PHP Binary Not Available**
We don't have a precompiled binary for your setup yet. Please help us improve!
**Your Setup:**
- Configuration: \`${Q}\`
- Platform: \`${z}\`
- Architecture: \`${W}\`
**How to help:**
1. Join our Discord: https://discord.gg/stacksjs
2. Share this error in the #launchpad channel
3. Tell us about your project setup so we can add support
**Available Configurations:**
- laravel-mysql: Laravel with MySQL/MariaDB
- laravel-postgres: Laravel with PostgreSQL
- laravel-sqlite: Laravel with SQLite
- api-only: Minimal API applications
- enterprise: Full-featured enterprise build
- wordpress: WordPress-optimized
- full-stack: Complete PHP with all major extensions
**Next Steps:**
- Try a different PHP version if available
- Check if your platform/architecture is supported
- Request support for your specific configuration
Thanks for helping us make Launchpad better! \uD83D\uDE4F
`.trim()}async downloadBinary(Q){await X.promises.mkdir(this.BINARY_CACHE_DIR,{recursive:!0});let z=K.join(this.BINARY_CACHE_DIR,Q.filename);if(X.existsSync(z))return console.log(`\uD83D\uDCE6 Using cached binary: ${Q.filename}`),z;if(j.verbose)console.log(`\u2B07\uFE0F Downloading precompiled PHP ${Q.php_version} (${Q.configuration})...`),console.log(`\uD83D\uDCCA Size: ${Math.round(Q.size/1024/1024*10)/10}MB`);let W=await fetch(Q.download_url,{headers:{"User-Agent":"Launchpad Binary Downloader"}});if(!W.ok||!W.body)throw new Error(`Download failed: ${W.status} ${W.statusText}`);let T=W.headers.get("content-length"),Z=T?Number.parseInt(T,10):0;if(Z>0){let A=W.body.getReader(),$=[],G=0,Y=0;while(!0){let{done:U,value:O}=await A.read();if(U)break;if(O){$.push(O),G+=O.length;let q=Date.now(),S=G/Z*100,I=Math.floor(S/5)*5;if(q-Y>100||S>=100||G===O.length){let w=j.verbose?`\u2B07\uFE0F ${G}/${Z} bytes (${I}%) - php.net v${Q.php_version}`:`\u2B07\uFE0F ${G}/${Z} bytes (${I}%)`;if(J.env.LAUNCHPAD_SHELL_INTEGRATION==="1"){if(J.stderr.write(`\r${w}`),J.stderr.isTTY)try{X.writeSync(J.stderr.fd,"")}catch{}}else if(J.stdout.write(`\r${w}`),J.stdout.isTTY)try{X.writeSync(J.stdout.fd,"")}catch{}Y=q}}}if(J.env.LAUNCHPAD_SHELL_INTEGRATION==="1"){if(J.stderr.write("\r\x1B[K"),J.stderr.isTTY)try{X.writeSync(J.stderr.fd,"")}catch{}}else if(J.stdout.write("\r\x1B[K"),J.stdout.isTTY)try{X.writeSync(J.stdout.fd,"")}catch{}let H=$.reduce((U,O)=>U+O.length,0),F=new Uint8Array(H),_=0;for(let U of $)F.set(U,_),_+=U.length;await X.promises.writeFile(z,F)}else{let A=j.verbose?`\u2B07\uFE0F Downloading php.net v${Q.php_version} (size unknown)...`:`\u2B07\uFE0F Downloading php.net v${Q.php_version}...`;if(J.env.LAUNCHPAD_SHELL_INTEGRATION==="1"){if(J.stderr.write(`\r${A}`),J.stderr.isTTY)try{X.writeSync(J.stderr.fd,"")}catch{}}else if(J.stdout.write(`\r${A}`),J.stdout.isTTY)try{X.writeSync(J.stdout.fd,"")}catch{}let $=await W.arrayBuffer();if(await X.promises.writeFile(z,B.from($)),J.env.LAUNCHPAD_SHELL_INTEGRATION==="1"){if(J.stderr.write("\r\x1B[K"),J.stderr.isTTY)try{X.writeSync(J.stderr.fd,"")}catch{}}else if(J.stdout.write("\r\x1B[K"),J.stdout.isTTY)try{X.writeSync(J.stdout.fd,"")}catch{}}if(j.verbose)console.log(`\u2705 Downloaded: ${Q.filename}`);return z}async getPhpDependenciesFromPantry(){try{let{pantry:Q}=await import("./chunk-fhet42vs.js"),z=Q.phpnet;if(!z)return console.warn("\u26A0\uFE0F PHP package not found in pantry"),[];return(z.dependencies||[]).map((T)=>{let Z="";if(typeof T==="string")Z=T;else if(T&&typeof T==="object"&&"project"in T)Z=T.project;else Z=String(T);return Z=Z.replace(/^[^:]+:/,""),Z=Z.replace(/[~^<>=].*$/,""),Z}).filter(Boolean)}catch(Q){return console.warn(`\u26A0\uFE0F Failed to get PHP dependencies from pantry: ${Q instanceof Error?Q.message:String(Q)}`),["gnu.org/readline","openssl.org","curl.se","zlib.net"]}}async createPhpShims(Q,z){try{let W=K.join(Q,"bin");if(!X.existsSync(W))return;try{await this.ensurePhpDependenciesInstalled()}catch(Y){console.warn(`\u26A0\uFE0F Failed to install PHP dependencies: ${Y instanceof Error?Y.message:String(Y)}`)}let T=await this.findLaunchpadLibraryPaths(),Z=K.join(this.installPath,"gnu.org/readline"),A=K.join(J.env.HOME||"",".local","share","launchpad","global","gnu.org/readline");for(let Y of[Z,A])if(X.existsSync(Y)){let H=X.readdirSync(Y).filter((F)=>F.startsWith("v"));for(let F of H){let _=K.join(Y,F,"lib");if(X.existsSync(_)&&!T.includes(_))T.push(_)}}let $=X.readdirSync(W).filter((Y)=>{let H=K.join(W,Y),F=X.statSync(H);return F.isFile()&&F.mode&73}),G=K.join(this.installPath,"php.ini");try{let Y=await this.detectFrameworkAndDatabase(),H=[];if(Y==="laravel-postgres")H.push("pdo_pgsql","pgsql");else if(Y==="laravel-mysql")H.push("pdo_mysql","mysqli");else if(Y==="laravel-sqlite")H.push("pdo_sqlite","sqlite3");let F="";try{let U=K.join(Q,"lib","php","extensions");if(X.existsSync(U)){let O=X.readdirSync(U).filter((q)=>X.statSync(K.join(U,q)).isDirectory());if(O.length>0){let q=K.join(U,O[0]);F=`extension_dir = ${q}`;let S=new Set(X.readdirSync(q));for(let I=H.length-1;I>=0;I--){let v=`${H[I]}.so`;if(!S.has(v))H.splice(I,1)}}}}catch{}let _=["; Launchpad php.ini (auto-generated)","memory_limit = 512M","max_execution_time = 300","upload_max_filesize = 64M","post_max_size = 64M","display_errors = On","error_reporting = E_ALL","",...F?[F,""]:[],"; Enable database extensions based on project detection",...H.map((U)=>`extension=${U}`),""].join(`
`);X.writeFileSync(G,_,"utf8")}catch(Y){console.warn(`\u26A0\uFE0F Could not write project php.ini: ${Y instanceof Error?Y.message:String(Y)}`)}for(let Y of $){let H=K.join(W,Y),F=K.join(W,`${Y}.original`);X.renameSync(H,F);try{try{await this.fixMacOSDylibs(Q,T)}catch{}}catch(O){console.warn(`\u26A0\uFE0F Could not fix macOS dylib references: ${O instanceof Error?O.message:String(O)}`)}let _="/usr/local/lib:/usr/lib:/lib";if(T.length>0)_=`${T.join(":")}:${_}`;let U=`#!/bin/sh
# Launchpad PHP binary wrapper with library paths
# Auto-generated wrapper for ${Y} v${z}
# Set up library paths to find Launchpad-installed libraries first, then system libraries
export DYLD_LIBRARY_PATH="${_}:$DYLD_LIBRARY_PATH"
export DYLD_FALLBACK_LIBRARY_PATH="${_}:$DYLD_FALLBACK_LIBRARY_PATH"
export LD_LIBRARY_PATH="${_}:$LD_LIBRARY_PATH"
# Prefer project-level php.ini when present
if [ -f "${G.replace(/"/g,"\\\"")}" ]; then
export PHPRC="${G.replace(/"/g,"\\\"")}"
fi
# Execute the original binary
exec "${F}" "$@"
`;X.writeFileSync(H,U),X.chmodSync(H,493)}console.log(`\uD83D\uDD17 Created PHP shims with Launchpad library paths for ${$.length} binaries`)}catch(W){console.warn(`\u26A0\uFE0F Failed to create PHP shims: ${W instanceof Error?W.message:String(W)}`)}}async fixMacOSDylibs(Q,z){if(J.platform!=="darwin")return;let W=K.join(Q,"bin","php.original");if(!X.existsSync(W))return;try{let{execSync:T}=await import("child_process"),A=T(`otool -L "${W}"`,{encoding:"utf8"}).split(`
`).slice(1).map((Y)=>Y.trim()).filter(Boolean),$=[];for(let Y of A){let H=Y.match(/^(\S+) \(/);if(!H)continue;let F=H[1];if(F.includes("/opt/homebrew/")||F.includes("/usr/local/opt/")||F.includes("/Cellar/")){let _=K.basename(F);$.push({oldPath:F,libName:_})}}if($.length===0)return;let G=new Set;for(let Y of z)G.add(Y);try{let Y=X.readdirSync(this.installPath);for(let H of Y){let F=K.join(this.installPath,H);if(!X.existsSync(F)||!X.statSync(F).isDirectory())continue;try{let _=X.readdirSync(F).filter((U)=>U.startsWith("v"));for(let U of _){let O=K.join(F,U,"lib");if(X.existsSync(O))G.add(O)}}catch{}}}catch{}for(let{oldPath:Y,libName:H}of $){let F=null;for(let _ of G){let U=K.join(_,H);if(X.existsSync(U)){F=U;break}}if(F)try{if(T(`install_name_tool -change "${Y}" "${F}" "${W}"`),j.verbose)console.log(`\uD83D\uDD27 Rewrote dylib reference: ${Y} \u2192 ${F}`)}catch(_){if(j.verbose)console.warn(`\u26A0\uFE0F install_name_tool failed for ${H}: ${_ instanceof Error?_.message:String(_)}`)}}}catch(T){if(j.verbose)console.warn(`\u26A0\uFE0F otool/install_name_tool unavailable: ${T instanceof Error?T.message:String(T)}`)}}async findLaunchpadLibraryPaths(){let Q=[];try{let z=await this.getPhpDependenciesFromPantry();console.log(`\uD83D\uDD0D Scanning library paths for ${z.length} PHP dependencies...`);for(let W of z){let T=K.join(this.installPath,W);if(X.existsSync(T)){let A=X.readdirSync(T).filter(($)=>{let G=K.join(T,$);return X.statSync(G).isDirectory()&&$.startsWith("v")});for(let $ of A){let G=K.join(T,$,"lib");if(X.existsSync(G))Q.push(G),console.log(`\uD83D\uDCDA Found local library: ${G}`)}}let Z=K.join(J.env.HOME||"",".local","share","launchpad","global",W);if(X.existsSync(Z)){let A=X.readdirSync(Z).filter(($)=>$.startsWith("v")).sort().reverse();for(let $ of A){let G=K.join(Z,$,"lib");if(X.existsSync(G))Q.push(G)}}}if(Q.length>0)console.log(`\uD83D\uDD0D Found Launchpad library paths for PHP: ${Q.length} paths`);else console.warn("\u26A0\uFE0F No Launchpad libraries found for PHP dependencies")}catch(z){console.warn(`\u26A0\uFE0F Error finding Launchpad libraries: ${z instanceof Error?z.message:String(z)}`)}return Q}async ensurePhpDependenciesInstalled(){try{let Q=await this.getPhpDependenciesFromPantry(),z=new Set(Q),W=await this.detectRequiredIcuMajor();if(W)z.add(`unicode.org@^${W}.0.0`);let T=Array.from(z);if(T.length===0)return;let{install:Z}=await import("./chunk-83b6axd0.js"),A=J.env.LAUNCHPAD_SUPPRESS_INSTALL_SUMMARY;J.env.LAUNCHPAD_SUPPRESS_INSTALL_SUMMARY="true";try{await Z(T,this.installPath)}finally{if(A===void 0)delete J.env.LAUNCHPAD_SUPPRESS_INSTALL_SUMMARY;else J.env.LAUNCHPAD_SUPPRESS_INSTALL_SUMMARY=A}}catch(Q){if(j.verbose)console.warn(`\u26A0\uFE0F Could not ensure PHP dependencies: ${Q instanceof Error?Q.message:String(Q)}`)}}async detectRequiredIcuMajor(){if(J.platform!=="darwin")return null;try{let Q=K.join(this.installPath,"php.net");if(!X.existsSync(Q))return null;let z=X.readdirSync(Q).filter((G)=>G.startsWith("v"));if(z.length===0)return null;let W=z.sort().reverse()[0],T=K.join(Q,W,"bin","php.original");if(!X.existsSync(T))return null;let{execSync:Z}=await import("child_process"),$=Z(`otool -L "${T}"`,{encoding:"utf8"}).match(/libicu\w+\.(\d+)\.dylib/);if($){let G=Number.parseInt($[1],10);return Number.isFinite(G)?G:null}return null}catch{return null}}async createHomebrewCompatSymlinks(Q){if(Q.length===0){console.warn("\u26A0\uFE0F No Launchpad readline found, skipping Homebrew compatibility symlinks");return}try{let z="";for(let Z of Q){let A=[K.join(Z,"libreadline.8.dylib"),K.join(Z,"libreadline.dylib"),K.join(Z,"libreadline.so.8"),K.join(Z,"libreadline.so")];for(let $ of A)if(X.existsSync($)){z=$;break}if(z)break}if(!z){if(j.verbose)console.warn("\u26A0\uFE0F No readable readline library file found");return}let W=K.join(this.installPath,".homebrew-compat","opt","homebrew","opt","readline","lib"),T=K.join(W,"libreadline.8.dylib");try{if(await X.promises.mkdir(W,{recursive:!0}),!X.existsSync(T))await X.promises.symlink(z,T),console.log(`\uD83D\uDD17 Created Homebrew-compat readline symlink: ${T} -> ${z}`)}catch(Z){console.warn(`\u26A0\uFE0F Could not create Homebrew compatibility symlinks: ${Z instanceof Error?Z.message:String(Z)}`)}}catch(z){console.warn(`\u26A0\uFE0F Failed to create Homebrew compatibility symlinks: ${z instanceof Error?z.message:String(z)}`)}}async validatePhpInstallation(Q,z){try{let W=K.join(Q,"bin","php");if(!X.existsSync(W))throw new Error("PHP binary not found after installation");let T=N(`"${W}" --version`,{encoding:"utf-8",timeout:1e4});if(j.verbose)console.log(`\u2705 PHP ${z} installed and validated successfully`),console.log(`\uD83D\uDC18 Version: ${T.split(`
`)[0]}`)}catch(W){if(j.verbose)console.warn(`\u26A0\uFE0F PHP validation failed: ${W instanceof Error?W.message:String(W)}`),console.warn("\uD83D\uDCA1 The binary was installed but may have library dependency issues")}}async extractBinary(Q,z){let W=K.join(this.installPath,"php.net",`v${z.php_version}`);if(X.existsSync(W))await X.promises.rm(W,{recursive:!0,force:!0});await X.promises.mkdir(W,{recursive:!0}),console.log(`\uD83D\uDCC2 Extracting PHP ${z.php_version} to ${W}...`);try{N(`tar -xzf "${Q}" -C "${W}" --strip-components=1`,{stdio:"pipe",timeout:60000});let T=K.join(W,"bin","php");if(!X.existsSync(T))throw new Error("PHP binary not found after extraction");return console.log(`\uD83D\uDCC2 PHP ${z.php_version} extracted successfully`),W}catch(T){if(X.existsSync(W))await X.promises.rm(W,{recursive:!0,force:!0});throw new Error(`Binary extraction failed: ${T instanceof Error?T.message:String(T)}`)}}provideSuggestions(Q){if(Q.hasCustom&&j.verbose)console.log(`
\uD83D\uDEA8 Custom Extensions Detected`),console.log(`\u274C Missing: ${Q.customExtensions.join(", ")}`),console.log(`
\uD83D\uDCA1 Options:`),console.log("1. \uD83D\uDCE6 Use source build (automatic fallback)"),console.log("2. \uD83D\uDD27 Modify your launchpad.config.ts to use standard extensions"),console.log("3. \uD83D\uDCAC Request these extensions on Discord: https://discord.gg/stacksjs"),console.log(`
\uD83D\uDCCB Available precompiled configs:`),console.log("- laravel-mysql: MySQL-focused Laravel"),console.log("- laravel-postgres: PostgreSQL-focused Laravel"),console.log("- laravel-sqlite: SQLite-focused Laravel"),console.log("- api-only: Minimal API applications"),console.log("- enterprise: All common extensions"),console.log("- wordpress: WordPress-optimized"),console.log("")}async downloadAndInstallPHP(Q){try{if(j.verbose)console.log("\uD83D\uDE80 Installing PHP from precompiled binaries..."),console.log(`\uD83D\uDD0D Target: ${this.getPlatform()}-${this.getArchitecture()}`);let z=await this.hasCustomExtensions();if(z.hasCustom)throw this.provideSuggestions(z),new Error(`Custom extensions required: ${z.reason}`);let W=await this.downloadManifest();console.log(`\uD83D\uDCCB Found ${W.binaries.length} available binaries`);let T=await this.findMatchingBinary(W,Q);if(!T){let $=this.getPlatform(),G=this.getArchitecture(),Y=await this.detectFrameworkAndDatabase(),H=W.binaries.filter((_)=>_.platform===$&&_.architecture===G);if(console.log(`
\uD83D\uDD0D Available binaries for ${$}-${G}:`),H.length>0)H.forEach((_)=>{console.log(` - PHP ${_.php_version} (${_.configuration})`)});else console.log(` \u274C No binaries available for ${$}-${G}`);let F=this.generateDiscordErrorMessage(Y,$,G);throw console.log(`
${F}
`),new Error(`No precompiled binary found for ${$}-${G} with ${Y} configuration${Q?` and PHP ${Q}`:""}. See available binaries above and Discord message for help.`)}if(j.verbose)console.log(`\uD83C\uDFAF Selected: PHP ${T.php_version} (${T.configuration})`),console.log(`\uD83D\uDCE6 Extensions: ${T.extensions.split(",").length} included`);let Z=await this.downloadBinary(T),A=await this.extractBinary(Z,T);if(j.verbose)console.log(`\uD83D\uDCE6 PHP ${T.php_version} binary installed, shims will be created after dependencies are ready`);return{success:!0,packageDir:A,version:T.php_version,configuration:T.configuration,extensions:T.extensions.split(",").filter(Boolean)}}catch(z){let W=z instanceof Error?z.message:String(z);return console.error(`\u274C Failed to install precompiled PHP: ${W}`),{success:!1,packageDir:"",version:"",configuration:"",extensions:[],error:W}}}async isSupported(){try{let Q=await this.hasCustomExtensions();if(Q.hasCustom){if(j.verbose)console.log(`\uD83D\uDD27 Custom extensions detected: ${Q.customExtensions.join(", ")}`),console.log("\uD83D\uDCE6 Falling back to source build for custom configuration");return!1}let z=await this.downloadManifest();return await this.findMatchingBinary(z)!==null}catch{return!1}}async listAvailableBinaries(){let Q=await this.downloadManifest(),z=this.getPlatform(),W=this.getArchitecture();return Q.binaries.filter((T)=>T.platform===z&&T.architecture===W)}}async function m(Q,z){let T=await new L(Q).downloadAndInstallPHP(z);if(!T.success)throw new Error(`Failed to install PHP binary: ${T.error}`);try{let Z=K.join(Q,"php.ini"),A=["; Launchpad php.ini (auto-generated)","memory_limit = 512M","max_execution_time = 300","upload_max_filesize = 64M","post_max_size = 64M","display_errors = On","error_reporting = E_ALL",""],$="";try{let H=K.join(T.packageDir,"lib","php","extensions");if(X.existsSync(H)){let F=X.readdirSync(H).filter((_)=>X.statSync(K.join(H,_)).isDirectory());if(F.length>0)$=K.join(H,F[0])}}catch{}if($)A.push(`extension_dir = ${$}`),A.push("");let G=new Set($&&X.existsSync($)?X.readdirSync($):[]),Y=(H)=>{let F=`${H}.so`;if(!$||G.has(F))A.push(`extension=${H}`)};A.push("; Enable database extensions based on project detection");try{let H=K.join(J.cwd(),".env");if(X.existsSync(H)){let U=X.readFileSync(H,"utf-8").match(/^DB_CONNECTION=(.*)$/m)?.[1]?.trim().toLowerCase();if(U==="pgsql"||U==="postgres"||U==="postgresql")Y("pdo_pgsql"),Y("pgsql");else if(U==="mysql"||U==="mariadb")Y("pdo_mysql"),Y("mysqli");else if(U==="sqlite")Y("pdo_sqlite"),Y("sqlite3")}}catch{}X.writeFileSync(Z,A.join(`
`))}catch{}try{let Z=await E(T.packageDir,Q,"php.net",T.version);if(j.verbose)console.log(`\uD83C\uDF89 Successfully installed php.net (${Z.length} ${Z.length===1?"binary":"binaries"})`),console.log(`\uD83D\uDCC1 Location: ${T.packageDir}`),console.log(`\uD83D\uDD0C Extensions: ${T.extensions.length} loaded`)}catch(Z){if(j.verbose)console.warn(`\u26A0\uFE0F Failed to create php shims: ${Z instanceof Error?Z.message:String(Z)}`)}return V(`\u2705 php.net \x1B[2m\x1B[3m(v${T.version})\x1B[0m`),[T.packageDir]}export{m as downloadPhpBinary,L as PrecompiledBinaryDownloader};