{"version":3,"file":"index.cjs","sources":["../../../../packages/playground/common/src/create-memoized-fetch.ts","../../../../packages/playground/common/src/index.ts"],"sourcesContent":["export interface CacheEntry {\n\tresponsePromise: Promise<Response>;\n\tunlockedBodyStream?: ReadableStream<Uint8Array>;\n\tnextResponse: () => Promise<Response>;\n}\n\n/**\n * Creates a fetch function that memoizes the response stream.\n * Calling it twice will return a response with the same status,\n * headers, and the body stream.\n * Memoization is keyed by URL. Method, headers etc are ignored.\n *\n * @param originalFetch The fetch function to memoize. Defaults to the global fetch.\n */\nexport function createMemoizedFetch(\n\toriginalFetch: (\n\t\tinput: RequestInfo | URL,\n\t\tinit?: RequestInit\n\t) => Promise<Response> = fetch\n) {\n\tconst fetches: Record<string, CacheEntry> = {};\n\n\treturn async function memoizedFetch(url: string, options?: RequestInit) {\n\t\tif (!fetches[url]) {\n\t\t\tfetches[url] = {\n\t\t\t\tresponsePromise: originalFetch(url, options),\n\t\t\t\tasync nextResponse() {\n\t\t\t\t\t// Wait for \"result\" to be set.\n\t\t\t\t\tconst response = await fetches[url].responsePromise;\n\t\t\t\t\tconst [left, right] =\n\t\t\t\t\t\tfetches[url].unlockedBodyStream!.tee();\n\t\t\t\t\tfetches[url].unlockedBodyStream = left;\n\t\t\t\t\treturn new Response(right, {\n\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\t\theaders: response.headers,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t};\n\t\t\tconst response = await fetches[url].responsePromise;\n\t\t\tfetches[url].unlockedBodyStream = response.body!;\n\t\t}\n\n\t\treturn fetches[url].nextResponse();\n\t};\n}\n","/**\n * Avoid adding new code here. @wp-playground/common should remain\n * as lean as possible.\n *\n * This package exists to avoid circular dependencies. Let's not\n * use it as a default place to add code that doesn't seem to fit\n * anywhere else. If there's no good place for your code, perhaps\n * it needs to be restructured? Or maybe there's a need for a new package?\n * Let's always consider these questions before adding new code here.\n */\n\nimport type { PHPRunOptions, UniversalPHP } from '@php-wasm/universal';\nimport { phpVars } from '@php-wasm/util';\n\nexport { createMemoizedFetch } from './create-memoized-fetch';\n\nexport const RecommendedPHPVersion = '8.3';\n\n/**\n * Reports cumulative progress through non-directory ZIP entries.\n *\n * Byte counts use declared uncompressed sizes and advance only between\n * entries. Entries skipped because overwriting is disabled still count as\n * processed.\n */\nexport interface UnzipProgress {\n\t/** Number of non-directory entries processed so far. */\n\tfilesProcessed: number;\n\t/** Total number of non-directory entries in the archive. */\n\ttotalFiles: number;\n\t/** Declared uncompressed bytes processed so far. */\n\tuncompressedBytesProcessed: number;\n\t/** Declared uncompressed bytes across all non-directory entries. */\n\ttotalUncompressedBytes: number;\n}\n\nconst UNZIP_PROGRESS_FILES_INTERVAL = 100;\nconst UNZIP_PROGRESS_UNCOMPRESSED_BYTES_INTERVAL = 400 * 1024;\nconst UNZIP_PROGRESS_LINE_PREFIX = 'PLAYGROUND_UNZIP_PROGRESS:';\n\n/**\n * Extracts a ZIP archive into Playground's virtual filesystem.\n *\n * `ZipArchive::extractTo()` has no extraction-progress callback in any\n * supported PHP version, so this function calls it in batches. Each batch ends\n * after either 100 files or 400 KiB of declared uncompressed data.\n *\n * For example, 250 one-byte files are extracted as three batches of 100, 100,\n * and 50 files. An `onProgress` callback receives an update after each batch.\n * Entries remain atomic, so a single file larger than 400 KiB finishes before\n * the update.\n *\n * `ZipArchive` follows symlinks already present in the destination. Do not\n * extract into a directory containing untrusted symlinks.\n *\n * @param php - PHP runtime whose filesystem contains the archive and target.\n * @param zipPath - Archive path in the PHP filesystem, or a browser `File`.\n * @param extractToPath - Destination directory, created when it does not exist.\n * @param overwriteFiles - Whether archive entries may replace existing paths.\n * Defaults to `true`.\n * @param onProgress - Optional callback for cumulative extraction progress.\n * @returns A promise that resolves when extraction finishes.\n * @throws When extraction fails or the progress callback throws.\n */\nexport const unzipFile = async (\n\tphp: UniversalPHP,\n\tzipPath: string | File,\n\textractToPath: string,\n\toverwriteFiles = true,\n\tonProgress?: (progress: UnzipProgress) => void\n) => {\n\t/**\n\t * Use a random file name to avoid conflicts across concurrent unzipFile()\n\t * calls.\n\t */\n\tconst tmpPath = `/tmp/file-${Math.random()}.zip`;\n\tlet shouldRemoveTmpPath = false;\n\ttry {\n\t\tif (zipPath instanceof File) {\n\t\t\tconst zipFile = zipPath;\n\t\t\tzipPath = tmpPath;\n\t\t\tshouldRemoveTmpPath = true;\n\t\t\tawait php.writeFile(\n\t\t\t\tzipPath,\n\t\t\t\tnew Uint8Array(await zipFile.arrayBuffer())\n\t\t\t);\n\t\t}\n\t\tconst code = `<?php\n\t\t$zipPath = getenv('PLAYGROUND_UNZIP_ZIP_PATH');\n\t\t$extractTo = getenv('PLAYGROUND_UNZIP_EXTRACT_TO_PATH');\n\t\t$overwriteFiles =\n\t\t\tgetenv('PLAYGROUND_UNZIP_OVERWRITE_FILES') === '1';\n\t\t$reportProgress =\n\t\t\tgetenv('PLAYGROUND_UNZIP_REPORT_PROGRESS') === '1';\n\t\t$filesInterval =\n\t\t\tintval(getenv('PLAYGROUND_UNZIP_FILES_INTERVAL'));\n\t\t$uncompressedBytesInterval = intval(\n\t\t\tgetenv('PLAYGROUND_UNZIP_UNCOMPRESSED_BYTES_INTERVAL')\n\t\t);\n\t\t$linePrefix = getenv('PLAYGROUND_UNZIP_LINE_PREFIX');\n\n\t\tif (!is_dir($extractTo)) {\n\t\t\tmkdir($extractTo, 0777, true);\n\t\t}\n\t\t$zip = new ZipArchive;\n\t\t$res = $zip->open($zipPath);\n\t\tif ($res !== TRUE) {\n\t\t\t$fileSize = file_exists($zipPath) ? filesize($zipPath) : 'unknown';\n\t\t\tthrow new Exception(\n\t\t\t\t\"Could not unzip file. Error code: \" . $res .\n\t\t\t\t\". File size: \" . $fileSize . \" bytes.\"\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\t$totalFiles = 0;\n\t\t\t$totalUncompressedBytes = 0;\n\t\t\tif ($reportProgress) {\n\t\t\t\tfor ($i = 0; $i < $zip->numFiles; $i++) {\n\t\t\t\t\t$stat = $zip->statIndex($i);\n\t\t\t\t\tif ($stat === false) {\n\t\t\t\t\t\tthrow new Exception(\n\t\t\t\t\t\t\t\"Could not inspect ZIP entry \" . $i . \".\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (substr($stat['name'], -1) !== '/') {\n\t\t\t\t\t\t$totalFiles++;\n\t\t\t\t\t\t$totalUncompressedBytes += $stat['size'];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Keep one extraction path for all callers. Progress reporting only\n\t\t\t// adds the totals scan above and emits an update between batches.\n\t\t\t$filesProcessed = 0;\n\t\t\t$uncompressedBytesProcessed = 0;\n\t\t\t$filesSinceUpdate = 0;\n\t\t\t$uncompressedBytesSinceUpdate = 0;\n\t\t\t$entriesToExtract = array();\n\t\t\tfor ($i = 0; $i < $zip->numFiles; $i++) {\n\t\t\t\t$stat = $zip->statIndex($i);\n\t\t\t\tif ($stat === false) {\n\t\t\t\t\tthrow new Exception(\n\t\t\t\t\t\t\"Could not inspect ZIP entry \" . $i . \".\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t$filename = $stat['name'];\n\t\t\t\t$isDirectory = substr($filename, -1) === '/';\n\t\t\t\t$extractFilePath =\n\t\t\t\t\trtrim($extractTo, '/') . '/' . $filename;\n\t\t\t\t// Leave existing paths out when $overwriteFiles is false.\n\t\t\t\tif ($overwriteFiles || !file_exists($extractFilePath)) {\n\t\t\t\t\t$entriesToExtract[] = $filename;\n\t\t\t\t}\n\t\t\t\tif ($isDirectory) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$filesProcessed++;\n\t\t\t\t$uncompressedBytesProcessed += $stat['size'];\n\t\t\t\t$filesSinceUpdate++;\n\t\t\t\t$uncompressedBytesSinceUpdate += $stat['size'];\n\t\t\t\tif (\n\t\t\t\t\t$filesSinceUpdate >= $filesInterval ||\n\t\t\t\t\t$uncompressedBytesSinceUpdate >=\n\t\t\t\t\t\t$uncompressedBytesInterval\n\t\t\t\t) {\n\t\t\t\t\textractZipBatch($zip, $extractTo, $entriesToExtract);\n\t\t\t\t\tif ($reportProgress) {\n\t\t\t\t\t\treportUnzipProgress(\n\t\t\t\t\t\t\t$linePrefix,\n\t\t\t\t\t\t\t$filesProcessed,\n\t\t\t\t\t\t\t$totalFiles,\n\t\t\t\t\t\t\t$uncompressedBytesProcessed,\n\t\t\t\t\t\t\t$totalUncompressedBytes\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t$filesSinceUpdate = 0;\n\t\t\t\t\t$uncompressedBytesSinceUpdate = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\textractZipBatch($zip, $extractTo, $entriesToExtract);\n\t\t\tif (\n\t\t\t\t$reportProgress &&\n\t\t\t\t($filesSinceUpdate > 0 ||\n\t\t\t\t\t$uncompressedBytesSinceUpdate > 0 ||\n\t\t\t\t\t$totalFiles === 0)\n\t\t\t) {\n\t\t\t\treportUnzipProgress(\n\t\t\t\t\t$linePrefix,\n\t\t\t\t\t$filesProcessed,\n\t\t\t\t\t$totalFiles,\n\t\t\t\t\t$uncompressedBytesProcessed,\n\t\t\t\t\t$totalUncompressedBytes\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\t// PHP 5.2 does not support finally.\n\t\t\t$zip->close();\n\t\t\tthrow $e;\n\t\t}\n\t\t$zip->close();\n\t\tchmod($extractTo, 0777);\n\n\t\t/**\n\t\t * Extracts and clears the queued ZIP entries.\n\t\t *\n\t\t * @param ZipArchive $zip       Open archive containing the entries.\n\t\t * @param string     $extractTo Destination directory.\n\t\t * @param array      $entries   Entry names to extract.\n\t\t * @return void\n\t\t * @throws Exception When ZipArchive cannot extract the queued entries.\n\t\t */\n\t\tfunction extractZipBatch($zip, $extractTo, &$entries)\n\t\t{\n\t\t\tif (count($entries) === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!$zip->extractTo($extractTo, $entries)) {\n\t\t\t\tthrow new Exception(\"Could not extract ZIP entries.\");\n\t\t\t}\n\t\t\t$entries = array();\n\t\t}\n\n\t\t/**\n\t\t * Writes and flushes one prefixed JSON progress record.\n\t\t *\n\t\t * @param string $linePrefix                Progress record prefix.\n\t\t * @param int    $filesProcessed             Files processed so far.\n\t\t * @param int    $totalFiles                 Total files in the archive.\n\t\t * @param int    $uncompressedBytesProcessed Bytes processed so far.\n\t\t * @param int    $totalUncompressedBytes     Total uncompressed bytes.\n\t\t * @return void\n\t\t */\n\t\tfunction reportUnzipProgress(\n\t\t\t$linePrefix,\n\t\t\t$filesProcessed,\n\t\t\t$totalFiles,\n\t\t\t$uncompressedBytesProcessed,\n\t\t\t$totalUncompressedBytes\n\t\t) {\n\t\t\techo $linePrefix . json_encode(array(\n\t\t\t\t'filesProcessed' => $filesProcessed,\n\t\t\t\t'totalFiles' => $totalFiles,\n\t\t\t\t'uncompressedBytesProcessed' => $uncompressedBytesProcessed,\n\t\t\t\t'totalUncompressedBytes' => $totalUncompressedBytes,\n\t\t\t)) . \"\\\\n\";\n\t\t\tflush();\n\t\t}\n\t\t`;\n\t\tconst request: PHPRunOptions = {\n\t\t\tcode,\n\t\t\tenv: {\n\t\t\t\tPLAYGROUND_UNZIP_ZIP_PATH: zipPath,\n\t\t\t\tPLAYGROUND_UNZIP_EXTRACT_TO_PATH: extractToPath,\n\t\t\t\tPLAYGROUND_UNZIP_OVERWRITE_FILES: overwriteFiles ? '1' : '0',\n\t\t\t\tPLAYGROUND_UNZIP_REPORT_PROGRESS: onProgress ? '1' : '0',\n\t\t\t\tPLAYGROUND_UNZIP_FILES_INTERVAL: String(\n\t\t\t\t\tUNZIP_PROGRESS_FILES_INTERVAL\n\t\t\t\t),\n\t\t\t\tPLAYGROUND_UNZIP_UNCOMPRESSED_BYTES_INTERVAL: String(\n\t\t\t\t\tUNZIP_PROGRESS_UNCOMPRESSED_BYTES_INTERVAL\n\t\t\t\t),\n\t\t\t\tPLAYGROUND_UNZIP_LINE_PREFIX: UNZIP_PROGRESS_LINE_PREFIX,\n\t\t\t},\n\t\t};\n\t\tif (onProgress) {\n\t\t\tawait runUnzipFileWithProgress(php, request, onProgress);\n\t\t} else {\n\t\t\tawait php.run(request);\n\t\t}\n\t} finally {\n\t\tif (shouldRemoveTmpPath) {\n\t\t\ttry {\n\t\t\t\tif (await php.fileExists(tmpPath)) {\n\t\t\t\t\tawait php.unlink(tmpPath);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Best-effort cleanup: preserving the unzip error matters more than\n\t\t\t\t// surfacing a leftover temporary file.\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * Runs ZIP extraction and forwards prefixed JSON progress records from stdout.\n *\n * Stream chunks do not align with lines, so partial lines are buffered.\n * Parsing and callback failures are deferred until PHP finishes so temporary\n * archive cleanup and pooled-instance release cannot race the request. A PHP\n * process failure takes precedence.\n *\n * @param php - PHP runtime used to start the streaming request.\n * @param request - Extraction program and per-request environment variables.\n * @param onProgress - Receives each complete, valid progress record.\n * @returns A promise that resolves after the output streams and PHP process.\n * @throws When PHP, progress parsing, or `onProgress` fails.\n */\nasync function runUnzipFileWithProgress(\n\tphp: UniversalPHP,\n\trequest: PHPRunOptions,\n\tonProgress: (progress: UnzipProgress) => void\n): Promise<void> {\n\tconst response = await php.runStream(request);\n\tconst stderrText = response.stderrText;\n\tconst reader = response.stdout.getReader();\n\tconst decoder = new TextDecoder();\n\tlet buffered = '';\n\tlet progressError: unknown;\n\tconst processLine = (line: string) => {\n\t\tif (!line.startsWith(UNZIP_PROGRESS_LINE_PREFIX)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tonProgress(\n\t\t\t\tJSON.parse(\n\t\t\t\t\tline.slice(UNZIP_PROGRESS_LINE_PREFIX.length)\n\t\t\t\t) as UnzipProgress\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tprogressError ??= error;\n\t\t}\n\t};\n\ttry {\n\t\twhile (true) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tbuffered += decoder.decode(value, { stream: !done });\n\t\t\tlet newline = buffered.indexOf('\\n');\n\t\t\twhile (newline !== -1) {\n\t\t\t\tprocessLine(buffered.slice(0, newline));\n\t\t\t\tbuffered = buffered.slice(newline + 1);\n\t\t\t\tnewline = buffered.indexOf('\\n');\n\t\t\t}\n\t\t\tif (done) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n\tif (buffered) {\n\t\tprocessLine(buffered);\n\t}\n\tconst [exitCode, stderr] = await Promise.all([\n\t\tresponse.exitCode,\n\t\tstderrText,\n\t]);\n\tif (exitCode !== 0) {\n\t\tthrow new Error(\n\t\t\tstderr.trim() ||\n\t\t\t\t`Could not unzip file. PHP exited with code ${exitCode}.`\n\t\t);\n\t}\n\tif (progressError) {\n\t\tthrow progressError;\n\t}\n}\n\nexport const zipDirectory = async (\n\tphp: UniversalPHP,\n\tdirectoryPath: string\n) => {\n\tconst outputPath = `/tmp/file${Math.random()}.zip`;\n\ttry {\n\t\tconst js = phpVars({\n\t\t\tdirectoryPath,\n\t\t\toutputPath,\n\t\t});\n\t\tawait php.run({\n\t\t\tcode: `<?php\n\t\t\t/**\n\t\t\t * Creates a ZIP archive from a Playground directory.\n\t\t\t *\n\t\t\t * The JavaScript wrapper removes the temporary archive in a finally\n\t\t\t * block, so this function only owns archive creation and permissions.\n\t\t\t */\n\t\t\tfunction zipDirectory($directoryPath, $outputPath) {\n\t\t\t\t$zip = new ZipArchive;\n\t\t\t\t$res = $zip->open($outputPath, ZipArchive::CREATE);\n\t\t\tif ($res !== TRUE) {\n\t\t\t\tthrow new Exception('Failed to create ZIP');\n\t\t\t}\n\t\t\t$files = new RecursiveIteratorIterator(\n\t\t\t\tnew RecursiveDirectoryIterator($directoryPath)\n\t\t\t);\n\t\t\tforeach ($files as $file) {\n\t\t\t\t$file = strval($file);\n\t\t\t\tif (is_dir($file)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$zip->addFile($file, substr($file, strlen($directoryPath)));\n\t\t\t}\n\t\t\t$zip->close();\n\t\t\tchmod($outputPath, 0777);\n\t\t}\n\t\t\tzipDirectory(${js.directoryPath}, ${js.outputPath});\n\t\t\t`,\n\t\t});\n\n\t\treturn await php.readFileAsBuffer(outputPath);\n\t} finally {\n\t\ttry {\n\t\t\tif (await php.fileExists(outputPath)) {\n\t\t\t\tawait php.unlink(outputPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Best-effort cleanup: preserving the ZIP error matters more than\n\t\t\t// surfacing a leftover temporary file.\n\t\t}\n\t}\n};\n"],"names":["createMemoizedFetch","originalFetch","fetches","url","options","response","left","right","RecommendedPHPVersion","UNZIP_PROGRESS_FILES_INTERVAL","UNZIP_PROGRESS_UNCOMPRESSED_BYTES_INTERVAL","UNZIP_PROGRESS_LINE_PREFIX","unzipFile","php","zipPath","extractToPath","overwriteFiles","onProgress","tmpPath","shouldRemoveTmpPath","zipFile","request","runUnzipFileWithProgress","stderrText","reader","decoder","buffered","progressError","processLine","line","error","done","value","newline","exitCode","stderr","zipDirectory","directoryPath","outputPath","js","phpVars"],"mappings":"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"}