@wp-playground/cli 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-cli-W9VNXESj.js","sources":["../../../../packages/playground/cli/src/download.ts","../../../../packages/playground/cli/src/server.ts","../../../../packages/playground/cli/src/load-balancer.ts","../../../../packages/playground/cli/src/is-valid-wordpress-slug.ts","../../../../packages/playground/cli/src/reportable-error.ts","../../../../packages/playground/cli/src/resolve-blueprint.ts","../../../../packages/playground/cli/src/run-cli.ts"],"sourcesContent":["import type { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport path, { basename } from 'path';\n\nexport const CACHE_FOLDER = path.join(os.homedir(), '.wordpress-playground');\n\nexport async function fetchSqliteIntegration(\n\tmonitor: EmscriptenDownloadMonitor\n) {\n\tconst sqliteZip = await cachedDownload(\n\t\t'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip',\n\t\t'sqlite.zip',\n\t\tmonitor\n\t);\n\treturn sqliteZip;\n}\n\n// @TODO: Support HTTP cache, invalidate the local file if the remote file has\n// changed\nexport async function cachedDownload(\n\tremoteUrl: string,\n\tcacheKey: string,\n\tmonitor: EmscriptenDownloadMonitor\n) {\n\tconst artifactPath = path.join(CACHE_FOLDER, cacheKey);\n\tif (!fs.existsSync(artifactPath)) {\n\t\tfs.ensureDirSync(CACHE_FOLDER);\n\t\tawait downloadTo(remoteUrl, artifactPath, monitor);\n\t}\n\treturn readAsFile(artifactPath);\n}\n\nasync function downloadTo(\n\tremoteUrl: string,\n\tlocalPath: string,\n\tmonitor: EmscriptenDownloadMonitor\n) {\n\tconst response = await monitor.monitorFetch(fetch(remoteUrl));\n\tconst reader = response.body!.getReader();\n\tconst tmpPath = `${localPath}.partial`;\n\tconst writer = fs.createWriteStream(tmpPath);\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (value) {\n\t\t\twriter.write(value);\n\t\t}\n\t\tif (done) {\n\t\t\tbreak;\n\t\t}\n\t}\n\twriter.close();\n\tif (!writer.closed) {\n\t\tawait new Promise((resolve, reject) => {\n\t\t\twriter.on('finish', () => {\n\t\t\t\tfs.renameSync(tmpPath, localPath);\n\t\t\t\tresolve(null);\n\t\t\t});\n\t\t\twriter.on('error', (err: any) => {\n\t\t\t\tfs.removeSync(tmpPath);\n\t\t\t\treject(err);\n\t\t\t});\n\t\t});\n\t}\n}\n\nexport function readAsFile(path: string, fileName?: string): File {\n\treturn new File([fs.readFileSync(path)], fileName ?? basename(path));\n}\n","import type { PHPRequest, PHPResponse } from '@php-wasm/universal';\nimport type { Request } from 'express';\nimport express from 'express';\nimport type { IncomingMessage, Server, ServerResponse } from 'http';\nimport type { AddressInfo } from 'net';\nimport type { RunCLIServer } from './run-cli';\n\nexport interface ServerOptions {\n\tport: number;\n\tonBind: (server: Server, port: number) => Promise<RunCLIServer>;\n\thandleRequest: (request: PHPRequest) => Promise<PHPResponse>;\n}\n\nexport async function startServer(\n\toptions: ServerOptions\n): Promise<RunCLIServer> {\n\tconst app = express();\n\n\tconst server = await new Promise<\n\t\tServer<typeof IncomingMessage, typeof ServerResponse>\n\t>((resolve, reject) => {\n\t\tconst server = app.listen(options.port, () => {\n\t\t\tconst address = server.address();\n\t\t\tif (address === null || typeof address === 'string') {\n\t\t\t\treject(new Error('Server address is not available'));\n\t\t\t} else {\n\t\t\t\tresolve(server);\n\t\t\t}\n\t\t});\n\t});\n\n\tapp.use('/', async (req, res) => {\n\t\tconst phpResponse = await options.handleRequest({\n\t\t\turl: req.url,\n\t\t\theaders: parseHeaders(req),\n\t\t\tmethod: req.method as any,\n\t\t\tbody: await bufferRequestBody(req),\n\t\t});\n\n\t\tres.statusCode = phpResponse.httpStatusCode;\n\t\tfor (const key in phpResponse.headers) {\n\t\t\tres.setHeader(key, phpResponse.headers[key]);\n\t\t}\n\t\tres.end(phpResponse.bytes);\n\t});\n\n\tconst address = server.address();\n\tconst port = (address! as AddressInfo).port;\n\treturn await options.onBind(server, port);\n}\n\nconst bufferRequestBody = async (req: Request): Promise<Uint8Array> =>\n\tawait new Promise((resolve) => {\n\t\tconst body: Uint8Array[] = [];\n\t\treq.on('data', (chunk) => {\n\t\t\tbody.push(chunk);\n\t\t});\n\t\treq.on('end', () => {\n\t\t\tresolve(new Uint8Array(Buffer.concat(body)));\n\t\t});\n\t});\n\nconst parseHeaders = (req: Request): Record<string, string> => {\n\tconst requestHeaders: Record<string, string> = {};\n\tif (req.rawHeaders && req.rawHeaders.length) {\n\t\tfor (let i = 0; i < req.rawHeaders.length; i += 2) {\n\t\t\trequestHeaders[req.rawHeaders[i].toLowerCase()] =\n\t\t\t\treq.rawHeaders[i + 1];\n\t\t}\n\t}\n\treturn requestHeaders;\n};\n","import type { PHPRequest, PHPResponse, RemoteAPI } from '@php-wasm/universal';\nimport type { PlaygroundCliWorker } from './worker-thread';\n\n// TODO: Let's merge worker management into PHPProcessManager\n// when we can have multiple workers in both CLI and web.\n// ¡ATTENTION!:Please don't expand upon this as an independent abstraction.\n\n// TODO: Could we just spawn a worker using the factory function to PHPProcessManager?\ntype WorkerLoad = {\n\tworker: RemoteAPI<PlaygroundCliWorker>;\n\tactiveRequests: Set<Promise<PHPResponse>>;\n};\nexport class LoadBalancer {\n\tworkerLoads: WorkerLoad[] = [];\n\n\tconstructor(\n\t\t// NOTE: We require a worker to start so that a load balancer\n\t\t// may not exist without being able to service requests.\n\t\t// Playground CLI initialization, as of 2025-06-11, requires that\n\t\t// an initial worker is booted alone and initialized via Blueprint\n\t\t// before additional workers are created based on the initialized worker.\n\t\tinitialWorker: RemoteAPI<PlaygroundCliWorker>\n\t) {\n\t\tthis.addWorker(initialWorker);\n\t}\n\n\taddWorker(worker: RemoteAPI<PlaygroundCliWorker>) {\n\t\tthis.workerLoads.push({\n\t\t\tworker,\n\t\t\tactiveRequests: new Set(),\n\t\t});\n\t}\n\n\tasync handleRequest(request: PHPRequest) {\n\t\tlet smallestWorkerLoad = this.workerLoads[0];\n\n\t\t// TODO: Is there any way for us to track CPU load so we could avoid\n\t\t// picking a worker that is under heavy load despite few requests?\n\t\t// Possibly this: https://nodejs.org/api/worker_threads.html#workerperformance\n\t\t// Though we probably don't need to worry about it.\n\t\tfor (let i = 1; i < this.workerLoads.length; i++) {\n\t\t\tconst workerLoad = this.workerLoads[i];\n\t\t\tif (\n\t\t\t\tworkerLoad.activeRequests.size <\n\t\t\t\tsmallestWorkerLoad.activeRequests.size\n\t\t\t) {\n\t\t\t\tsmallestWorkerLoad = workerLoad;\n\t\t\t}\n\t\t}\n\n\t\t// TODO: Add trace facility to Playground CLI to observe internals like request routing.\n\n\t\tconst promiseForResponse = smallestWorkerLoad.worker.request(request);\n\t\tsmallestWorkerLoad.activeRequests.add(promiseForResponse);\n\n\t\t// Add URL to promise for use while debugging\n\t\t(promiseForResponse as any).url = request.url;\n\n\t\treturn promiseForResponse.finally(() => {\n\t\t\tsmallestWorkerLoad.activeRequests.delete(promiseForResponse);\n\t\t});\n\t}\n}\n","/**\n * Checks if the given version string is a valid WordPress version.\n *\n * The Regex is based on the releases on https://wordpress.org/download/releases/#betas\n * The version string can be one of the following formats:\n * - \"latest\"\n * - \"trunk\"\n * - \"nightly\"\n * - \"x.y\" (x and y are integers) e.g. \"6.2\"\n * - \"x.y.z\" (x, y and z are integers) e.g. \"6.2.1\"\n * - \"x.y.z-betaN\" (N is an integer) e.g. \"6.2.1-beta1\"\n * - \"x.y.z-RCN\" (N is an integer) e.g. \"6.2-RC1\"\n *\n * @param version The version string to check.\n * @returns A boolean value indicating whether the version string is a valid WordPress version.\n */\nexport function isValidWordPressSlug(version: string): boolean {\n\tconst versionPattern =\n\t\t/^latest$|^trunk$|^nightly$|^(?:(\\d+)\\.(\\d+)(?:\\.(\\d+))?)((?:-beta(?:\\d+)?)|(?:-RC(?:\\d+)?))?$/;\n\treturn versionPattern.test(version);\n}\n","/**\n * Specialized error class for CLI errors that should be reported to the user\n * as text messages. Any error of this type thrown in the CLI call stack will\n * be printed as a regular text, not a stack trace, and will cause the process\n * to exit with a non-zero exit code.\n */\nimport { logger } from '@php-wasm/logger';\n\nexport class ReportableError extends Error {\n\tpublic isReportableInCLI = true;\n\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, {\n\t\t\t...options,\n\t\t\tcause: {\n\t\t\t\tisReportableInCLI: true,\n\t\t\t},\n\t\t});\n\t\tthis.isReportableInCLI = true;\n\t}\n\n\tstatic getReportableCause(error: unknown): Error | null {\n\t\tlet iterations = 0;\n\t\tconst maxIterations = 15;\n\n\t\tconst errorStack = [error];\n\t\twhile (errorStack.length > 0 && iterations < maxIterations) {\n\t\t\tconst subError = errorStack.pop();\n\t\t\tif (!(subError instanceof Error)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif ((subError as any).isReportableInCLI) {\n\t\t\t\treturn subError;\n\t\t\t}\n\t\t\tif (Array.isArray(subError.cause)) {\n\t\t\t\terrorStack.push(...subError.cause);\n\t\t\t} else {\n\t\t\t\terrorStack.push(subError.cause);\n\t\t\t}\n\t\t\titerations++;\n\t\t\tif (iterations >= maxIterations) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t'Recursion limit exceeded while checking if error is reportable'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n}\n","import fs from 'fs';\nimport path from 'path';\nimport {\n\tZipFilesystem,\n\tNodeJsFilesystem,\n\tOverlayFilesystem,\n\tInMemoryFilesystem,\n} from '@wp-playground/storage';\nimport { resolveRemoteBlueprint } from '@wp-playground/blueprints';\nimport { ReportableError } from './reportable-error';\n\ntype ResolveBlueprintOptions = {\n\tsourceString: string | undefined;\n\tblueprintMayReadAdjacentFiles: boolean;\n};\n\n/**\n * Resolves a blueprint from a URL or a local path.\n *\n * @TODO: Extract the common Blueprint resolution logic between CLI and\n * the website into a single, isomorphic resolveBlueprint() function.\n * Still retain the CLI-specific bits in the CLI package.\n *\n * @param sourceString - The source string to resolve the blueprint from.\n * @param blueprintMayReadAdjacentFiles - Whether the blueprint may read adjacent files.\n * @returns The resolved blueprint.\n */\nexport async function resolveBlueprint({\n\tsourceString,\n\tblueprintMayReadAdjacentFiles,\n}: ResolveBlueprintOptions) {\n\tif (!sourceString) {\n\t\treturn undefined;\n\t}\n\n\tif (\n\t\tsourceString.startsWith('http://') ||\n\t\tsourceString.startsWith('https://')\n\t) {\n\t\treturn await resolveRemoteBlueprint(sourceString);\n\t}\n\n\t// If the sourceString does not refer to a remote blueprint, try to\n\t// resolve it from a local filesystem.\n\n\tlet blueprintPath = path.resolve(process.cwd(), sourceString);\n\tif (!fs.existsSync(blueprintPath)) {\n\t\tthrow new Error(`Blueprint file does not exist: ${blueprintPath}`);\n\t}\n\n\tconst stat = fs.statSync(blueprintPath);\n\tif (stat.isDirectory()) {\n\t\tblueprintPath = path.join(blueprintPath, 'blueprint.json');\n\t}\n\n\tif (!stat.isFile() && stat.isSymbolicLink()) {\n\t\tthrow new Error(\n\t\t\t`Blueprint path is neither a file nor a directory: ${blueprintPath}`\n\t\t);\n\t}\n\n\tconst extension = path.extname(blueprintPath);\n\tswitch (extension) {\n\t\tcase '.zip':\n\t\t\treturn ZipFilesystem.fromArrayBuffer(\n\t\t\t\tfs.readFileSync(blueprintPath).buffer as ArrayBuffer\n\t\t\t);\n\t\tcase '.json': {\n\t\t\tconst blueprintText = fs.readFileSync(blueprintPath, 'utf-8');\n\t\t\ttry {\n\t\t\t\tJSON.parse(blueprintText);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Blueprint file at ${blueprintPath} is not a valid JSON file`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst contextPath = path.dirname(blueprintPath);\n\t\t\tconst nodeJsFilesystem = new NodeJsFilesystem(contextPath);\n\t\t\treturn new OverlayFilesystem([\n\t\t\t\tnew InMemoryFilesystem({\n\t\t\t\t\t'blueprint.json': blueprintText,\n\t\t\t\t}),\n\t\t\t\t/**\n\t\t\t\t * Wrap the NodeJS filesystem to prevent access to local files\n\t\t\t\t * unless the user explicitly allowed it.\n\t\t\t\t */\n\t\t\t\t{\n\t\t\t\t\tread(path) {\n\t\t\t\t\t\tif (!blueprintMayReadAdjacentFiles) {\n\t\t\t\t\t\t\tthrow new ReportableError(\n\t\t\t\t\t\t\t\t`Error: Blueprint contained tried to read a local file at path \"${path}\" (via a resource of type \"bundled\"). ` +\n\t\t\t\t\t\t\t\t\t`Playground restricts access to local resources by default as a security measure. \\n\\n` +\n\t\t\t\t\t\t\t\t\t`You can allow this Blueprint to read files from the same parent directory by explicitly adding the ` +\n\t\t\t\t\t\t\t\t\t`--blueprint-may-read-adjacent-files option to your command.`\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nodeJsFilesystem.read(path);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t]);\n\t\t}\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`Unsupported blueprint file extension: ${extension}. Only .zip and .json files are supported.`\n\t\t\t);\n\t}\n}\n","import { errorLogPath, logger } from '@php-wasm/logger';\nimport { EmscriptenDownloadMonitor, ProgressTracker } from '@php-wasm/progress';\nimport type {\n\tPHPRequest,\n\tRemoteAPI,\n\tSupportedPHPVersion,\n} from '@php-wasm/universal';\nimport {\n\tPHPResponse,\n\tconsumeAPI,\n\texposeAPI,\n\texposeSyncAPI,\n} from '@php-wasm/universal';\nimport type {\n\tBlueprintBundle,\n\tBlueprintDeclaration,\n} from '@wp-playground/blueprints';\nimport {\n\tcompileBlueprint,\n\tisBlueprintBundle,\n\trunBlueprintSteps,\n} from '@wp-playground/blueprints';\nimport {\n\tRecommendedPHPVersion,\n\tunzipFile,\n\tzipDirectory,\n} from '@wp-playground/common';\nimport fs from 'fs';\nimport type { Server } from 'http';\nimport path from 'path';\nimport { Worker, MessageChannel } from 'worker_threads';\n// @ts-ignore\nimport { resolveWordPressRelease } from '@wp-playground/wordpress';\nimport {\n\texpandAutoMounts,\n\tparseMountDirArguments,\n\tparseMountWithDelimiterArguments,\n} from './mounts';\nimport {\n\tCACHE_FOLDER,\n\tcachedDownload,\n\tfetchSqliteIntegration,\n\treadAsFile,\n} from './download';\nimport { startServer } from './server';\nimport type { Mount, PlaygroundCliWorker } from './worker-thread';\n// @ts-ignore\nimport importedWorkerUrlString from './worker-thread?worker&url';\n// @ts-ignore\nimport { FileLockManagerForNode } from '@php-wasm/node';\nimport { LoadBalancer } from './load-balancer';\n/* eslint-disable no-console */\nimport { SupportedPHPVersions } from '@php-wasm/universal';\nimport { cpus } from 'os';\nimport { jspi } from 'wasm-feature-detect';\nimport type { MessagePort as NodeMessagePort } from 'worker_threads';\nimport yargs from 'yargs';\nimport { isValidWordPressSlug } from './is-valid-wordpress-slug';\nimport { ReportableError } from './reportable-error';\nimport { resolveBlueprint } from './resolve-blueprint';\n\nexport async function parseOptionsAndRunCLI() {\n\t/**\n\t * @TODO This looks similar to Query API args https://wordpress.github.io/wordpress-playground/developers/apis/query-api/\n\t * Perhaps the two could be handled by the same code?\n\t */\n\tconst yargsObject = yargs(process.argv.slice(2))\n\t\t.usage('Usage: wp-playground <command> [options]')\n\t\t.positional('command', {\n\t\t\tdescribe: 'Command to run',\n\t\t\tchoices: ['server', 'run-blueprint', 'build-snapshot'] as const,\n\t\t\tdemandOption: true,\n\t\t})\n\t\t.option('outfile', {\n\t\t\tdescribe: 'When building, write to this output file.',\n\t\t\ttype: 'string',\n\t\t\tdefault: 'wordpress.zip',\n\t\t})\n\t\t.option('port', {\n\t\t\tdescribe: 'Port to listen on when serving.',\n\t\t\ttype: 'number',\n\t\t\tdefault: 9400,\n\t\t})\n\t\t.option('php', {\n\t\t\tdescribe: 'PHP version to use.',\n\t\t\ttype: 'string',\n\t\t\tdefault: RecommendedPHPVersion,\n\t\t\tchoices: SupportedPHPVersions,\n\t\t})\n\t\t.option('wp', {\n\t\t\tdescribe: 'WordPress version to use.',\n\t\t\ttype: 'string',\n\t\t\tdefault: 'latest',\n\t\t})\n\t\t// @TODO: Support read-only mounts, e.g. via WORKERFS, a custom\n\t\t// ReadOnlyNODEFS, or by copying the files into MEMFS\n\t\t.option('mount', {\n\t\t\tdescribe:\n\t\t\t\t'Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path',\n\t\t\ttype: 'array',\n\t\t\tstring: true,\n\t\t\tcoerce: parseMountWithDelimiterArguments,\n\t\t})\n\t\t.option('mount-before-install', {\n\t\t\tdescribe:\n\t\t\t\t'Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: /host/path:/vfs/path',\n\t\t\ttype: 'array',\n\t\t\tstring: true,\n\t\t\tcoerce: parseMountWithDelimiterArguments,\n\t\t})\n\t\t.option('mount-dir', {\n\t\t\tdescribe:\n\t\t\t\t'Mount a directory to the PHP runtime (can be used multiple times). Format: \"/host/path\" \"/vfs/path\"',\n\t\t\ttype: 'array',\n\t\t\tnargs: 2,\n\t\t\tarray: true,\n\t\t\t// coerce: parseMountDirArguments,\n\t\t})\n\t\t.option('mount-dir-before-install', {\n\t\t\tdescribe:\n\t\t\t\t'Mount a directory before WordPress installation (can be used multiple times). Format: \"/host/path\" \"/vfs/path\"',\n\t\t\ttype: 'string',\n\t\t\tnargs: 2,\n\t\t\tarray: true,\n\t\t\tcoerce: parseMountDirArguments,\n\t\t})\n\t\t.option('login', {\n\t\t\tdescribe: 'Should log the user in',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('blueprint', {\n\t\t\tdescribe: 'Blueprint to execute.',\n\t\t\ttype: 'string',\n\t\t})\n\t\t.option('blueprint-may-read-adjacent-files', {\n\t\t\tdescribe:\n\t\t\t\t'Consent flag: Allow \"bundled\" resources in a local blueprint to read files in the same directory as the blueprint file.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('skip-wordpress-setup', {\n\t\t\tdescribe:\n\t\t\t\t'Do not download, unzip, and install WordPress. Useful for mounting a pre-configured WordPress directory at /wordpress.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('skip-sqlite-setup', {\n\t\t\tdescribe:\n\t\t\t\t'Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('quiet', {\n\t\t\tdescribe: 'Do not output logs and progress messages.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('debug', {\n\t\t\tdescribe:\n\t\t\t\t'Print PHP error log content if an error occurs during Playground boot.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('auto-mount', {\n\t\t\tdescribe: `Automatically mount the current working directory. You can mount a WordPress directory, a plugin directory, a theme directory, a wp-content directory, or any directory containing PHP and HTML files.`,\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('follow-symlinks', {\n\t\t\tdescribe:\n\t\t\t\t'Allow Playground to follow symlinks by automatically mounting symlinked directories and files encountered in mounted directories. \\nWarning: Following symlinks will expose files outside mounted directories to Playground and could be a security risk.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('experimentalTrace', {\n\t\t\tdescribe:\n\t\t\t\t'Print detailed messages about system behavior to the console. Useful for troubleshooting.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t\t// Hide this option because we want to replace with a more general log-level flag.\n\t\t\thidden: true,\n\t\t})\n\t\t.option('internal-cookie-store', {\n\t\t\tdescribe:\n\t\t\t\t'Enable internal cookie handling. When enabled, Playground will manage cookies internally using ' +\n\t\t\t\t'an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled ' +\n\t\t\t\t'externally (e.g., by a browser in Node.js environments).',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t.option('xdebug', {\n\t\t\tdescribe: 'Enable Xdebug.',\n\t\t\ttype: 'boolean',\n\t\t\tdefault: false,\n\t\t})\n\t\t// TODO: Should we make this a hidden flag?\n\t\t.option('experimentalMultiWorker', {\n\t\t\tdescribe:\n\t\t\t\t'Enable experimental multi-worker support which requires JSPI ' +\n\t\t\t\t'and a /wordpress directory backed by a real filesystem. ' +\n\t\t\t\t'Pass a positive number to specify the number of workers to use. ' +\n\t\t\t\t'Otherwise, default to the number of CPUs minus 1.',\n\t\t\ttype: 'number',\n\t\t\tcoerce: (value?: number) => value ?? cpus().length - 1,\n\t\t})\n\t\t.showHelpOnFail(false)\n\t\t.check(async (args) => {\n\t\t\tif (args.wp !== undefined && !isValidWordPressSlug(args.wp)) {\n\t\t\t\ttry {\n\t\t\t\t\t// Check if is valid URL\n\t\t\t\t\tnew URL(args.wp);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Unrecognized WordPress version. Please use \"latest\", a URL, or a numeric version such as \"6.2\", \"6.0.1\", \"6.2-beta1\", or \"6.2-RC1\"'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (args.experimentalMultiWorker !== undefined) {\n\t\t\t\tif (args.experimentalMultiWorker <= 1) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'The --experimentalMultiWorker flag must be a positive integer greater than 1.'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst isMountingWordPressDir = (mount: Mount) =>\n\t\t\t\t\tmount.vfsPath === '/wordpress';\n\t\t\t\tif (\n\t\t\t\t\t!args.mount?.some(isMountingWordPressDir) &&\n\t\t\t\t\t!(args['mount-before-install'] as any)?.some(\n\t\t\t\t\t\tisMountingWordPressDir\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Please mount a real filesystem directory as the /wordpress directory before using the --experimentalMultiWorker flag.'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\n\tyargsObject.wrap(yargsObject.terminalWidth());\n\tconst args = await yargsObject.argv;\n\n\tconst command = args._[0] as string;\n\n\tif (!['run-blueprint', 'server', 'build-snapshot'].includes(command)) {\n\t\tyargsObject.showHelp();\n\t\tprocess.exit(1);\n\t}\n\n\tconst cliArgs = {\n\t\t...args,\n\t\tcommand,\n\t\tblueprint: await resolveBlueprint({\n\t\t\tsourceString: args.blueprint,\n\t\t\tblueprintMayReadAdjacentFiles: args.blueprintMayReadAdjacentFiles,\n\t\t}),\n\t\tmount: [...(args.mount || []), ...(args['mount-dir'] || [])],\n\t\t'mount-before-install': [\n\t\t\t...(args['mount-before-install'] || []),\n\t\t\t...(args['mount-dir-before-install'] || []),\n\t\t],\n\t} as RunCLIArgs;\n\n\ttry {\n\t\treturn runCLI(cliArgs);\n\t} catch (e) {\n\t\tconst reportableCause = ReportableError.getReportableCause(e);\n\t\tif (reportableCause) {\n\t\t\tconsole.log('');\n\t\t\tconsole.log(reportableCause.message);\n\t\t\tprocess.exit(1);\n\t\t} else {\n\t\t\tthrow e;\n\t\t}\n\t}\n}\n\nexport interface RunCLIArgs {\n\tblueprint?: BlueprintDeclaration | BlueprintBundle;\n\tcommand: 'server' | 'run-blueprint' | 'build-snapshot';\n\tdebug?: boolean;\n\tlogin?: boolean;\n\tmount?: Mount[];\n\t'mount-before-install'?: Mount[];\n\toutfile?: string;\n\tphp?: SupportedPHPVersion;\n\tport?: number;\n\tquiet?: boolean;\n\tskipWordPressSetup?: boolean;\n\tskipSqliteSetup?: boolean;\n\twp?: string;\n\tautoMount?: boolean;\n\tfollowSymlinks?: boolean;\n\texperimentalMultiWorker?: number;\n\texperimentalTrace?: boolean;\n\tinternalCookieStore?: boolean;\n\t'additional-blueprint-steps'?: any[];\n\txdebug?: boolean;\n}\n\nexport interface RunCLIServer extends AsyncDisposable {\n\tplayground: RemoteAPI<PlaygroundCliWorker>;\n\tserver: Server;\n\t[Symbol.asyncDispose](): Promise<void>;\n}\n\nexport async function runCLI(args: RunCLIArgs): Promise<RunCLIServer> {\n\tlet loadBalancer: LoadBalancer;\n\tlet playground: RemoteAPI<PlaygroundCliWorker>;\n\n\tconst playgroundsToCleanUp: {\n\t\tplayground: RemoteAPI<PlaygroundCliWorker>;\n\t\tworker: Worker;\n\t}[] = [];\n\n\t/**\n\t * Expand auto-mounts to include the necessary mounts and steps\n\t * when running in auto-mount mode.\n\t */\n\tif (args.autoMount) {\n\t\targs = expandAutoMounts(args);\n\t}\n\n\t/**\n\t * TODO: This exact feature will be provided in the PHP Blueprints library.\n\t * Let's use it when it ships. Let's also use it in the web Playground\n\t * app.\n\t */\n\tasync function zipSite(outfile: string) {\n\t\tawait playground.run({\n\t\t\tcode: `<?php\n\t\t\t$zip = new ZipArchive();\n\t\t\tif(false === $zip->open('/tmp/build.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE)) {\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('/wordpress')\n\t\t\t);\n\t\t\tforeach ($files as $file) {\n\t\t\t\techo $file . PHP_EOL;\n\t\t\t\tif (!$file->isFile()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$zip->addFile($file->getPathname(), $file->getPathname());\n\t\t\t}\n\t\t\t$zip->close();\n\n\t\t`,\n\t\t});\n\t\tconst zip = await playground.readFileAsBuffer('/tmp/build.zip');\n\t\tfs.writeFileSync(outfile, zip);\n\t}\n\n\tasync function compileInputBlueprint(additionalBlueprintSteps: any[]) {\n\t\t/**\n\t\t * @TODO This looks similar to the resolveBlueprint() call in the website package:\n\t\t * \t https://github.com/WordPress/wordpress-playground/blob/ce586059e5885d185376184fdd2f52335cca32b0/packages/playground/website/src/main.tsx#L41\n\t\t *\n\t\t * \t\t Also the Blueprint Builder tool does something similar.\n\t\t * Perhaps all these cases could be handled by the same function?\n\t\t */\n\t\tconst blueprint: BlueprintDeclaration | BlueprintBundle =\n\t\t\tisBlueprintBundle(args.blueprint)\n\t\t\t\t? args.blueprint\n\t\t\t\t: {\n\t\t\t\t\t\tlogin: args.login,\n\t\t\t\t\t\t...args.blueprint,\n\t\t\t\t\t\tpreferredVersions: {\n\t\t\t\t\t\t\tphp:\n\t\t\t\t\t\t\t\targs.php ??\n\t\t\t\t\t\t\t\targs?.blueprint?.preferredVersions?.php ??\n\t\t\t\t\t\t\t\tRecommendedPHPVersion,\n\t\t\t\t\t\t\twp:\n\t\t\t\t\t\t\t\targs.wp ??\n\t\t\t\t\t\t\t\targs?.blueprint?.preferredVersions?.wp ??\n\t\t\t\t\t\t\t\t'latest',\n\t\t\t\t\t\t\t...(args.blueprint?.preferredVersions || {}),\n\t\t\t\t\t\t},\n\t\t\t\t };\n\n\t\tconst tracker = new ProgressTracker();\n\t\tlet lastCaption = '';\n\t\tlet progressReached100 = false;\n\t\ttracker.addEventListener('progress', (e: any) => {\n\t\t\tif (progressReached100) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprogressReached100 = e.detail.progress === 100;\n\n\t\t\t// Use floor() so we don't report 100% until truly there.\n\t\t\tconst progressInteger = Math.floor(e.detail.progress);\n\t\t\tlastCaption =\n\t\t\t\te.detail.caption || lastCaption || 'Running the Blueprint';\n\t\t\tconst message = `${lastCaption.trim()} – ${progressInteger}%`;\n\t\t\tif (!args.quiet) {\n\t\t\t\twriteProgressUpdate(\n\t\t\t\t\tprocess.stdout,\n\t\t\t\t\tmessage,\n\t\t\t\t\tprogressReached100\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\treturn await compileBlueprint(blueprint as BlueprintDeclaration, {\n\t\t\tprogress: tracker,\n\t\t\tadditionalSteps: additionalBlueprintSteps,\n\t\t});\n\t}\n\n\tlet lastProgressMessage = '';\n\tfunction writeProgressUpdate(\n\t\twriteStream: NodeJS.WriteStream,\n\t\tmessage: string,\n\t\tfinalUpdate: boolean\n\t) {\n\t\tif (message === lastProgressMessage) {\n\t\t\t// Avoid repeating the same message\n\t\t\treturn;\n\t\t}\n\t\tlastProgressMessage = message;\n\n\t\tif (writeStream.isTTY) {\n\t\t\t// Overwrite previous progress updates in-place for a quieter UX.\n\t\t\twriteStream.cursorTo(0);\n\t\t\twriteStream.write(message);\n\t\t\twriteStream.clearLine(1);\n\n\t\t\tif (finalUpdate) {\n\t\t\t\twriteStream.write('\\n');\n\t\t\t}\n\t\t} else {\n\t\t\t// Fall back to writing one line per progress update\n\t\t\twriteStream.write(`${message}\\n`);\n\t\t}\n\t}\n\n\t/**\n\t * Spawns a new Worker Thread.\n\t *\n\t * @param workerUrl The absolute URL of the worker script.\n\t * @returns The spawned Worker Thread.\n\t */\n\tasync function spawnPHPWorkerThread(workerUrl: URL) {\n\t\tconst worker = new Worker(workerUrl);\n\n\t\treturn new Promise<{ worker: Worker; phpPort: NodeMessagePort }>(\n\t\t\t(resolve, reject) => {\n\t\t\t\tworker.once('message', function (message: any) {\n\t\t\t\t\t// Let the worker confirm it has initialized.\n\t\t\t\t\t// We could use the 'online' event to detect start of JS execution,\n\t\t\t\t\t// but that would miss initialization errors.\n\t\t\t\t\tif (message.command === 'worker-script-initialized') {\n\t\t\t\t\t\tresolve({ worker, phpPort: message.phpPort });\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tworker.once('error', function (e: Error) {\n\t\t\t\t\tconsole.error(e);\n\t\t\t\t\tconst error = new Error(\n\t\t\t\t\t\t`Worker failed to load at ${workerUrl}. ${\n\t\t\t\t\t\t\te.message ? `Original error: ${e.message}` : ''\n\t\t\t\t\t\t}`\n\t\t\t\t\t);\n\t\t\t\t\t(error as any).filename = workerUrl;\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t\t}\n\t\t);\n\t}\n\n\tfunction spawnWorkerThreads(\n\t\tcount: number\n\t): Promise<{ worker: Worker; phpPort: NodeMessagePort }[]> {\n\t\tconst moduleWorkerUrl = new URL(\n\t\t\timportedWorkerUrlString,\n\t\t\timport.meta.url\n\t\t);\n\n\t\tconst promises = [];\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tpromises.push(spawnPHPWorkerThread(moduleWorkerUrl));\n\t\t}\n\t\treturn Promise.all(promises);\n\t}\n\n\tif (args.quiet) {\n\t\t// @ts-ignore\n\t\tlogger.handlers = [];\n\t}\n\n\tconst compiledBlueprint = await compileInputBlueprint(\n\t\targs['additional-blueprint-steps'] || []\n\t);\n\n\t// Declare file lock manager outside scope of startServer\n\t// so we can look at it when debugging request handling.\n\tconst nativeFlockSync = await import('fs-ext')\n\t\t.then((m) => m.flockSync)\n\t\t.catch(() => {\n\t\t\tlogger.warn(\n\t\t\t\t'The fs-ext package is not installed. ' +\n\t\t\t\t\t'Internal file locking will not be integrated with ' +\n\t\t\t\t\t'host OS file locking.'\n\t\t\t);\n\t\t\treturn undefined;\n\t\t});\n\tconst fileLockManager = new FileLockManagerForNode(nativeFlockSync);\n\n\t/**\n\t * Expose the file lock manager API on a MessagePort and return it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync function exposeFileLockManager() {\n\t\tconst { port1, port2 } = new MessageChannel();\n\t\tif (await jspi()) {\n\t\t\t/**\n\t\t\t * When JSPI is available, the worker thread expects an asynchronous API.\n\t\t\t *\n\t\t\t * @see worker-thread.ts\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\texposeAPI(fileLockManager, null, port1);\n\t\t} else {\n\t\t\t/**\n\t\t\t * When JSPI is not available, the worker thread expects a synchronous API.\n\t\t\t *\n\t\t\t * @see worker-thread.ts\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tawait exposeSyncAPI(fileLockManager, port1);\n\t\t}\n\t\treturn port2;\n\t}\n\n\tlet wordPressReady = false;\n\n\tlogger.log('Starting a PHP server...');\n\n\treturn startServer({\n\t\tport: args['port'] as number,\n\t\tonBind: async (server: Server, port: number): Promise<RunCLIServer> => {\n\t\t\tconst absoluteUrl = `http://127.0.0.1:${port}`;\n\n\t\t\t// Kick off worker threads now to save time later.\n\t\t\t// There is no need to wait for other async processes to complete.\n\t\t\tconst totalWorkerCount = args.experimentalMultiWorker ?? 1;\n\t\t\tconst promisedWorkers = spawnWorkerThreads(totalWorkerCount);\n\n\t\t\tlogger.log(`Setting up WordPress ${args.wp}`);\n\t\t\tlet wpDetails: any = undefined;\n\t\t\t// @TODO: Rename to FetchProgressMonitor. There's nothing Emscripten\n\t\t\t// about that class anymore.\n\t\t\tconst monitor = new EmscriptenDownloadMonitor();\n\t\t\tif (!args.skipWordPressSetup) {\n\t\t\t\tlet progressReached100 = false;\n\t\t\t\tmonitor.addEventListener('progress', ((\n\t\t\t\t\te: CustomEvent<ProgressEvent & { finished: boolean }>\n\t\t\t\t) => {\n\t\t\t\t\tif (progressReached100) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// @TODO Every progress bar will want percentages. The\n\t\t\t\t\t// download monitor should just provide that.\n\t\t\t\t\tconst { loaded, total } = e.detail;\n\t\t\t\t\t// Use floor() so we don't report 100% until truly there.\n\t\t\t\t\tconst percentProgress = Math.floor(\n\t\t\t\t\t\tMath.min(100, (100 * loaded) / total)\n\t\t\t\t\t);\n\t\t\t\t\tprogressReached100 = percentProgress === 100;\n\n\t\t\t\t\tif (!args.quiet) {\n\t\t\t\t\t\twriteProgressUpdate(\n\t\t\t\t\t\t\tprocess.stdout,\n\t\t\t\t\t\t\t`Downloading WordPress ${percentProgress}%...`,\n\t\t\t\t\t\t\tprogressReached100\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}) as any);\n\n\t\t\t\twpDetails = await resolveWordPressRelease(args.wp);\n\t\t\t\tlogger.log(\n\t\t\t\t\t`Resolved WordPress release URL: ${wpDetails?.releaseUrl}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst preinstalledWpContentPath =\n\t\t\t\twpDetails &&\n\t\t\t\tpath.join(\n\t\t\t\t\tCACHE_FOLDER,\n\t\t\t\t\t`prebuilt-wp-content-for-wp-${wpDetails.version}.zip`\n\t\t\t\t);\n\t\t\tconst wordPressZip = !wpDetails\n\t\t\t\t? undefined\n\t\t\t\t: fs.existsSync(preinstalledWpContentPath)\n\t\t\t\t? readAsFile(preinstalledWpContentPath)\n\t\t\t\t: await cachedDownload(\n\t\t\t\t\t\twpDetails.releaseUrl,\n\t\t\t\t\t\t`${wpDetails.version}.zip`,\n\t\t\t\t\t\tmonitor\n\t\t\t\t );\n\n\t\t\tlogger.log(`Fetching SQLite integration plugin...`);\n\t\t\tconst sqliteIntegrationPluginZip = args.skipSqliteSetup\n\t\t\t\t? undefined\n\t\t\t\t: await fetchSqliteIntegration(monitor);\n\n\t\t\tconst followSymlinks = args.followSymlinks === true;\n\t\t\tconst trace = args.experimentalTrace === true;\n\t\t\ttry {\n\t\t\t\tconst mountsBeforeWpInstall =\n\t\t\t\t\targs['mount-before-install'] || [];\n\t\t\t\tconst mountsAfterWpInstall = args.mount || [];\n\n\t\t\t\tconst [initialWorker, ...additionalWorkers] =\n\t\t\t\t\tawait promisedWorkers;\n\n\t\t\t\tplayground = consumeAPI<PlaygroundCliWorker>(\n\t\t\t\t\tinitialWorker.phpPort\n\t\t\t\t);\n\t\t\t\tplaygroundsToCleanUp.push({\n\t\t\t\t\tplayground,\n\t\t\t\t\tworker: initialWorker.worker,\n\t\t\t\t});\n\n\t\t\t\t// Comlink communication proxy\n\t\t\t\tawait playground.isConnected();\n\n\t\t\t\tconst fileLockManagerPort = await exposeFileLockManager();\n\n\t\t\t\tlogger.log(`Booting WordPress...`);\n\n\t\t\t\t// Each additional worker needs a separate process ID space\n\t\t\t\t// for file locking to work properly because locks are associated\n\t\t\t\t// with individual processes. To accommodate this, we split the safe\n\t\t\t\t// integers into a range for each worker.\n\t\t\t\tconst processIdSpaceLength = Math.floor(\n\t\t\t\t\tNumber.MAX_SAFE_INTEGER / totalWorkerCount\n\t\t\t\t);\n\n\t\t\t\tawait playground.useFileLockManager(fileLockManagerPort);\n\t\t\t\tawait playground.boot({\n\t\t\t\t\tphpVersion: compiledBlueprint.versions.php,\n\t\t\t\t\twpVersion: compiledBlueprint.versions.wp,\n\t\t\t\t\tabsoluteUrl,\n\t\t\t\t\tmountsBeforeWpInstall,\n\t\t\t\t\tmountsAfterWpInstall,\n\t\t\t\t\twordPressZip:\n\t\t\t\t\t\twordPressZip && (await wordPressZip!.arrayBuffer()),\n\t\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\t\tawait sqliteIntegrationPluginZip!.arrayBuffer(),\n\t\t\t\t\tfirstProcessId: 0,\n\t\t\t\t\tprocessIdSpaceLength,\n\t\t\t\t\tfollowSymlinks,\n\t\t\t\t\ttrace,\n\t\t\t\t\tinternalCookieStore: args.internalCookieStore,\n\t\t\t\t\twithXdebug: args.xdebug,\n\t\t\t\t});\n\n\t\t\t\tif (\n\t\t\t\t\twpDetails &&\n\t\t\t\t\t!args['mount-before-install'] &&\n\t\t\t\t\t!fs.existsSync(preinstalledWpContentPath)\n\t\t\t\t) {\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t`Caching preinstalled WordPress for the next boot...`\n\t\t\t\t\t);\n\t\t\t\t\tfs.writeFileSync(\n\t\t\t\t\t\tpreinstalledWpContentPath,\n\t\t\t\t\t\tawait zipDirectory(playground, '/wordpress')\n\t\t\t\t\t);\n\t\t\t\t\tlogger.log(`Cached!`);\n\t\t\t\t}\n\n\t\t\t\tloadBalancer = new LoadBalancer(playground);\n\n\t\t\t\tawait playground.isReady();\n\t\t\t\twordPressReady = true;\n\t\t\t\tlogger.log(`Booted!`);\n\n\t\t\t\tif (compiledBlueprint) {\n\t\t\t\t\tlogger.log(`Running the Blueprint...`);\n\t\t\t\t\tawait runBlueprintSteps(compiledBlueprint, playground);\n\t\t\t\t\tlogger.log(`Finished running the blueprint`);\n\t\t\t\t}\n\n\t\t\t\tif (args.command === 'build-snapshot') {\n\t\t\t\t\tawait zipSite(args.outfile as string);\n\t\t\t\t\tlogger.log(`WordPress exported to ${args.outfile}`);\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t} else if (args.command === 'run-blueprint') {\n\t\t\t\t\tlogger.log(`Blueprint executed`);\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\targs.experimentalMultiWorker &&\n\t\t\t\t\targs.experimentalMultiWorker > 1\n\t\t\t\t) {\n\t\t\t\t\tlogger.log(`Preparing additional workers...`);\n\n\t\t\t\t\t// Save /internal directory from initial worker so we can replicate it\n\t\t\t\t\t// in each additional worker.\n\t\t\t\t\tconst internalZip = await zipDirectory(\n\t\t\t\t\t\tplayground,\n\t\t\t\t\t\t'/internal'\n\t\t\t\t\t);\n\n\t\t\t\t\t// Boot additional workers\n\t\t\t\t\tconst initialWorkerProcessIdSpace = processIdSpaceLength;\n\t\t\t\t\tawait Promise.all(\n\t\t\t\t\t\tadditionalWorkers.map(async (worker, index) => {\n\t\t\t\t\t\t\tconst additionalPlayground =\n\t\t\t\t\t\t\t\tconsumeAPI<PlaygroundCliWorker>(worker.phpPort);\n\t\t\t\t\t\t\tplaygroundsToCleanUp.push({\n\t\t\t\t\t\t\t\tplayground: additionalPlayground,\n\t\t\t\t\t\t\t\tworker: worker.worker,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tawait additionalPlayground.isConnected();\n\n\t\t\t\t\t\t\tconst firstProcessId =\n\t\t\t\t\t\t\t\tinitialWorkerProcessIdSpace +\n\t\t\t\t\t\t\t\tindex * processIdSpaceLength;\n\n\t\t\t\t\t\t\tconst fileLockManagerPort =\n\t\t\t\t\t\t\t\tawait exposeFileLockManager();\n\t\t\t\t\t\t\tawait additionalPlayground.useFileLockManager(\n\t\t\t\t\t\t\t\tfileLockManagerPort\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait additionalPlayground.boot({\n\t\t\t\t\t\t\t\tphpVersion: compiledBlueprint.versions.php,\n\t\t\t\t\t\t\t\tabsoluteUrl,\n\t\t\t\t\t\t\t\tmountsBeforeWpInstall,\n\t\t\t\t\t\t\t\tmountsAfterWpInstall,\n\t\t\t\t\t\t\t\t// Skip WordPress zip because we share the /wordpress directory\n\t\t\t\t\t\t\t\t// populated by the initial worker.\n\t\t\t\t\t\t\t\twordPressZip: undefined,\n\t\t\t\t\t\t\t\t// Skip SQLite integration plugin for now because we\n\t\t\t\t\t\t\t\t// will copy it from primary's `/internal` directory.\n\t\t\t\t\t\t\t\tsqliteIntegrationPluginZip: undefined,\n\t\t\t\t\t\t\t\tdataSqlPath:\n\t\t\t\t\t\t\t\t\t'/wordpress/wp-content/database/.ht.sqlite',\n\t\t\t\t\t\t\t\tfirstProcessId,\n\t\t\t\t\t\t\t\tprocessIdSpaceLength,\n\t\t\t\t\t\t\t\tfollowSymlinks,\n\t\t\t\t\t\t\t\ttrace,\n\t\t\t\t\t\t\t\tinternalCookieStore: args.internalCookieStore,\n\t\t\t\t\t\t\t\twithXdebug: args.xdebug,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tawait additionalPlayground.isReady();\n\n\t\t\t\t\t\t\t// Replicate the Blueprint-initialized /internal directory\n\t\t\t\t\t\t\tawait additionalPlayground.writeFile(\n\t\t\t\t\t\t\t\t'/tmp/internal.zip',\n\t\t\t\t\t\t\t\tinternalZip\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait unzipFile(\n\t\t\t\t\t\t\t\tadditionalPlayground,\n\t\t\t\t\t\t\t\t'/tmp/internal.zip',\n\t\t\t\t\t\t\t\t'/internal'\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait additionalPlayground.unlink(\n\t\t\t\t\t\t\t\t'/tmp/internal.zip'\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tloadBalancer.addWorker(additionalPlayground);\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\n\t\t\t\t\tlogger.log(`Ready!`);\n\t\t\t\t}\n\n\t\t\t\tlogger.log(`WordPress is running on ${absoluteUrl}`);\n\n\t\t\t\treturn {\n\t\t\t\t\tplayground,\n\t\t\t\t\tserver,\n\t\t\t\t\t[Symbol.asyncDispose]: async function disposeCLI() {\n\t\t\t\t\t\tawait Promise.all(\n\t\t\t\t\t\t\tplaygroundsToCleanUp.map(\n\t\t\t\t\t\t\t\tasync ({ playground, worker }) => {\n\t\t\t\t\t\t\t\t\tawait playground.dispose();\n\t\t\t\t\t\t\t\t\tawait worker.terminate();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait new Promise((resolve) => server.close(resolve));\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tif (!args.debug) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst phpLogs = await playground.readFileAsText(errorLogPath);\n\t\t\t\tthrow new Error(phpLogs, { cause: error });\n\t\t\t}\n\t\t},\n\t\tasync handleRequest(request: PHPRequest) {\n\t\t\tif (!wordPressReady) {\n\t\t\t\treturn PHPResponse.forHttpCode(\n\t\t\t\t\t502,\n\t\t\t\t\t'WordPress is not ready yet'\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn await loadBalancer.handleRequest(request);\n\t\t},\n\t});\n}\n"],"names":["CACHE_FOLDER","path","os","fetchSqliteIntegration","monitor","cachedDownload","remoteUrl","cacheKey","artifactPath","fs","downloadTo","readAsFile","localPath","reader","tmpPath","writer","done","value","resolve","reject","err","fileName","basename","startServer","options","app","express","server","address","req","res","phpResponse","parseHeaders","bufferRequestBody","key","port","body","chunk","requestHeaders","i","LoadBalancer","initialWorker","worker","request","smallestWorkerLoad","workerLoad","promiseForResponse","isValidWordPressSlug","version","ReportableError","message","error","iterations","maxIterations","errorStack","subError","logger","resolveBlueprint","sourceString","blueprintMayReadAdjacentFiles","resolveRemoteBlueprint","blueprintPath","stat","extension","ZipFilesystem","blueprintText","contextPath","nodeJsFilesystem","NodeJsFilesystem","OverlayFilesystem","InMemoryFilesystem","parseOptionsAndRunCLI","yargsObject","yargs","RecommendedPHPVersion","SupportedPHPVersions","parseMountWithDelimiterArguments","parseMountDirArguments","cpus","args","isMountingWordPressDir","mount","command","cliArgs","runCLI","e","reportableCause","loadBalancer","playground","playgroundsToCleanUp","expandAutoMounts","zipSite","outfile","zip","compileInputBlueprint","additionalBlueprintSteps","blueprint","isBlueprintBundle","tracker","ProgressTracker","lastCaption","progressReached100","progressInteger","writeProgressUpdate","compileBlueprint","lastProgressMessage","writeStream","finalUpdate","spawnPHPWorkerThread","workerUrl","Worker","spawnWorkerThreads","count","moduleWorkerUrl","importedWorkerUrlString","promises","compiledBlueprint","nativeFlockSync","m","fileLockManager","FileLockManagerForNode","exposeFileLockManager","port1","port2","MessageChannel","jspi","exposeAPI","exposeSyncAPI","wordPressReady","absoluteUrl","totalWorkerCount","promisedWorkers","wpDetails","EmscriptenDownloadMonitor","loaded","total","percentProgress","resolveWordPressRelease","preinstalledWpContentPath","wordPressZip","sqliteIntegrationPluginZip","followSymlinks","trace","mountsBeforeWpInstall","mountsAfterWpInstall","additionalWorkers","consumeAPI","fileLockManagerPort","processIdSpaceLength","zipDirectory","runBlueprintSteps","internalZip","initialWorkerProcessIdSpace","index","additionalPlayground","firstProcessId","unzipFile","phpLogs","errorLogPath","PHPResponse"],"mappings":"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"}
package/run-cli.d.ts CHANGED
@@ -10,7 +10,7 @@ export interface RunCLIArgs {
10
10
  debug?: boolean;
11
11
  login?: boolean;
12
12
  mount?: Mount[];
13
- mountBeforeInstall?: Mount[];
13
+ 'mount-before-install'?: Mount[];
14
14
  outfile?: string;
15
15
  php?: SupportedPHPVersion;
16
16
  port?: number;
@@ -23,6 +23,8 @@ export interface RunCLIArgs {
23
23
  experimentalMultiWorker?: number;
24
24
  experimentalTrace?: boolean;
25
25
  internalCookieStore?: boolean;
26
+ 'additional-blueprint-steps'?: any[];
27
+ xdebug?: boolean;
26
28
  }
27
29
  export interface RunCLIServer extends AsyncDisposable {
28
30
  playground: RemoteAPI<PlaygroundCliWorker>;
package/v2.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { type StreamedPHPResponse, type UniversalPHP } from '@php-wasm/universal';
2
+ import type { BlueprintDeclaration } from '@wp-playground/blueprints';
3
+ export type PHPExceptionDetails = {
4
+ exception: string;
5
+ message: string;
6
+ file: string;
7
+ line: number;
8
+ trace: string;
9
+ };
10
+ export type BlueprintMessage = {
11
+ type: 'blueprint.target_resolved';
12
+ } | {
13
+ type: 'blueprint.progress';
14
+ progress: number;
15
+ caption: string;
16
+ } | {
17
+ type: 'blueprint.error';
18
+ message: string;
19
+ details?: PHPExceptionDetails;
20
+ } | {
21
+ type: 'blueprint.completion';
22
+ message: string;
23
+ };
24
+ interface RunV2Options {
25
+ php: UniversalPHP;
26
+ cliArgs?: string[];
27
+ blueprint: BlueprintV2Declaration | ParsedBlueprintV2Declaration;
28
+ blueprintOverrides?: {
29
+ wordpressVersion?: string;
30
+ additionalSteps?: any[];
31
+ };
32
+ onMessage?: (message: BlueprintMessage) => void | Promise<void>;
33
+ }
34
+ export type BlueprintV2Declaration = string | BlueprintDeclaration | undefined;
35
+ export type ParsedBlueprintV2Declaration = {
36
+ type: 'inline-file';
37
+ contents: string;
38
+ } | {
39
+ type: 'file-reference';
40
+ reference: string;
41
+ };
42
+ export declare function parseBlueprintDeclaration(source: BlueprintV2Declaration | ParsedBlueprintV2Declaration): ParsedBlueprintV2Declaration;
43
+ export declare function getV2Runner(): Promise<File>;
44
+ export declare function runBlueprintV2(options: RunV2Options): Promise<StreamedPHPResponse>;
45
+ export {};
@@ -0,0 +1,130 @@
1
+ import { createNodeFsMountHandler as k, loadNodeRuntime as v } from "@php-wasm/node";
2
+ import { EmscriptenDownloadMonitor as M } from "@php-wasm/progress";
3
+ import { exposeAPI as F, PHPWorker as H, consumeAPI as S, consumeAPISync as W, sandboxedSpawnHandlerFactory as E } from "@php-wasm/universal";
4
+ import { sprintf as I } from "@php-wasm/util";
5
+ import { RecommendedPHPVersion as L } from "@wp-playground/common";
6
+ import { bootWordPress as A } from "@wp-playground/wordpress";
7
+ import { rootCertificates as D } from "tls";
8
+ import { jspi as R } from "wasm-feature-detect";
9
+ import { MessageChannel as x, parentPort as C } from "worker_threads";
10
+ import "fs";
11
+ import "path";
12
+ async function m(t, e) {
13
+ for (const o of e)
14
+ await t.mount(
15
+ o.vfsPath,
16
+ k(o.hostPath)
17
+ );
18
+ }
19
+ function G(t, e, ...o) {
20
+ console.log(
21
+ performance.now().toFixed(6).padStart(15, "0"),
22
+ t.toString().padStart(16, "0"),
23
+ I(e, ...o)
24
+ );
25
+ }
26
+ class U extends H {
27
+ constructor(e) {
28
+ super(void 0, e), this.booted = !1;
29
+ }
30
+ /**
31
+ * Call this method before boot() to use file locking.
32
+ *
33
+ * This method is separate from boot() to simplify the related Comlink.transferHandlers
34
+ * setup – if an argument is a MessagePort, we're transferring it, not copying it.
35
+ *
36
+ * @see comlink-sync.ts
37
+ * @see phpwasm-emscripten-library-file-locking-for-node.js
38
+ */
39
+ async useFileLockManager(e) {
40
+ await R() ? this.fileLockManager = S(e) : this.fileLockManager = await W(e);
41
+ }
42
+ async boot({
43
+ absoluteUrl: e,
44
+ mountsBeforeWpInstall: o,
45
+ mountsAfterWpInstall: f,
46
+ phpVersion: P = L,
47
+ wordPressZip: c,
48
+ sqliteIntegrationPluginZip: p,
49
+ firstProcessId: n,
50
+ processIdSpaceLength: h,
51
+ dataSqlPath: u,
52
+ followSymlinks: w,
53
+ trace: y,
54
+ internalCookieStore: g,
55
+ withXdebug: b
56
+ }) {
57
+ if (this.booted)
58
+ throw new Error("Playground already booted");
59
+ this.booted = !0;
60
+ let r = n;
61
+ const _ = n + h - 1;
62
+ try {
63
+ const a = {
64
+ WP_DEBUG: !0,
65
+ WP_DEBUG_LOG: !0,
66
+ WP_DEBUG_DISPLAY: !1
67
+ }, l = await A({
68
+ siteUrl: e,
69
+ createPhpRuntime: async () => {
70
+ const i = r;
71
+ return r < _ ? r++ : r = n, await v(P, {
72
+ emscriptenOptions: {
73
+ fileLockManager: this.fileLockManager,
74
+ processId: i,
75
+ trace: y ? G : void 0
76
+ },
77
+ followSymlinks: w,
78
+ withXdebug: b
79
+ });
80
+ },
81
+ wordPressZip: c !== void 0 ? new File([c], "wordpress.zip") : void 0,
82
+ sqliteIntegrationPluginZip: p !== void 0 ? new File(
83
+ [p],
84
+ "sqlite-integration-plugin.zip"
85
+ ) : void 0,
86
+ sapiName: "cli",
87
+ createFiles: {
88
+ "/internal/shared/ca-bundle.crt": D.join(`
89
+ `)
90
+ },
91
+ constants: a,
92
+ phpIniEntries: {
93
+ "openssl.cafile": "/internal/shared/ca-bundle.crt",
94
+ allow_url_fopen: "1",
95
+ disable_functions: ""
96
+ },
97
+ hooks: {
98
+ async beforeWordPressFiles(i) {
99
+ m(i, o);
100
+ }
101
+ },
102
+ cookieStore: g ? void 0 : !1,
103
+ dataSqlPath: u,
104
+ spawnHandler: E
105
+ });
106
+ this.__internal_setRequestHandler(l);
107
+ const d = await l.getPrimaryPhp();
108
+ await this.setPrimaryPHP(d), m(d, f), q();
109
+ } catch (a) {
110
+ throw z(a), a;
111
+ }
112
+ }
113
+ // Provide a named disposal method that can be invoked via comlink.
114
+ async dispose() {
115
+ await this[Symbol.asyncDispose]();
116
+ }
117
+ }
118
+ const s = new x(), [q, z] = F(
119
+ new U(new M()),
120
+ void 0,
121
+ s.port1
122
+ );
123
+ C.postMessage(
124
+ {
125
+ command: "worker-script-initialized",
126
+ phpPort: s.port2
127
+ },
128
+ [s.port2]
129
+ );
130
+ //# sourceMappingURL=worker-thread-CYvRK9UX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-thread-CYvRK9UX.js","sources":["packages/playground/cli/src/mounts.ts","packages/playground/cli/src/worker-thread.ts"],"sourcesContent":["import { createNodeFsMountHandler } from '@php-wasm/node';\nimport type { PHP } from '@php-wasm/universal';\nimport fs, { existsSync } from 'fs';\nimport path, { basename, join } from 'path';\nimport type { RunCLIArgs } from './run-cli';\n\nexport interface Mount {\n\thostPath: string;\n\tvfsPath: string;\n}\n\n/**\n * Parse an array of mount argument strings where the host path and VFS path\n * are separated by a colon.\n *\n * Example:\n * parseMountWithDelimiterArguments( [ '/host/path:/vfs/path', '/host/path:/vfs/path' ] )\n * // returns:\n * [\n * { hostPath: '/host/path', vfsPath: '/vfs/path' },\n * { hostPath: '/host/path', vfsPath: '/vfs/path' }\n * ]\n *\n * @param mounts - An array of mount argument strings separated by a colon.\n * @returns An array of Mount objects.\n */\nexport function parseMountWithDelimiterArguments(mounts: string[]): Mount[] {\n\tconst parsedMounts = [];\n\tfor (const mount of mounts) {\n\t\tconst mountParts = mount.split(':');\n\t\tif (mountParts.length !== 2) {\n\t\t\tthrow new Error(`Invalid mount format: ${mount}.\n\t\t\t\tExpected format: /host/path:/vfs/path.\n\t\t\t\tIf your path contains a colon, e.g. C:\\\\myplugin, use the --mount-dir option instead.\n\t\t\t\tExample: --mount-dir C:\\\\my-plugin /wordpress/wp-content/plugins/my-plugin`);\n\t\t}\n\t\tconst [hostPath, vfsPath] = mountParts;\n\t\tif (!existsSync(hostPath)) {\n\t\t\tthrow new Error(`Host path does not exist: ${hostPath}`);\n\t\t}\n\t\tparsedMounts.push({ hostPath, vfsPath });\n\t}\n\treturn parsedMounts;\n}\n\n/**\n * Parse an array of mount argument strings where each odd array element is a host path\n * and each even element is the VFS path.\n * e.g. [ '/host/path', '/vfs/path', '/host/path2', '/vfs/path2' ]\n *\n * The result will be an array of Mount objects for each host path the\n * following element is it's VFS path.\n * e.g. [\n * { hostPath: '/host/path', vfsPath: '/vfs/path' },\n * { hostPath: '/host/path2', vfsPath: '/vfs/path2' }\n * ]\n *\n * @param mounts - An array of paths\n * @returns An array of Mount objects.\n */\nexport function parseMountDirArguments(mounts: string[]): Mount[] {\n\tif (mounts.length % 2 !== 0) {\n\t\tthrow new Error('Invalid mount format. Expected: /host/path /vfs/path');\n\t}\n\n\tconst parsedMounts = [];\n\tfor (let i = 0; i < mounts.length; i += 2) {\n\t\tconst source = mounts[i];\n\t\tconst vfsPath = mounts[i + 1];\n\t\tif (!existsSync(source)) {\n\t\t\tthrow new Error(`Host path does not exist: ${source}`);\n\t\t}\n\t\tparsedMounts.push({\n\t\t\thostPath: path.resolve(process.cwd(), source),\n\t\t\tvfsPath,\n\t\t});\n\t}\n\treturn parsedMounts;\n}\n\nexport async function mountResources(php: PHP, mounts: Mount[]) {\n\tfor (const mount of mounts) {\n\t\tawait php.mount(\n\t\t\tmount.vfsPath,\n\t\t\tcreateNodeFsMountHandler(mount.hostPath)\n\t\t);\n\t}\n}\n\nconst ACTIVATE_FIRST_THEME_STEP = {\n\tstep: 'runPHP',\n\tcode: {\n\t\tfilename: 'activate-theme.php',\n\t\tcontent: `<?php\n\t\t\trequire_once getenv('DOCROOT') . '/wp-load.php';\n\t\t\t$theme = wp_get_theme();\n\t\t\tif (!$theme->exists()) {\n\t\t\t\t$themes = wp_get_themes();\n\t\t\t\tif (count($themes) > 0) {\n\t\t\t\t\t$themeName = array_keys($themes)[0];\n\t\t\t\t\tswitch_theme($themeName);\n\t\t\t\t}\n\t\t\t}\n\t\t`,\n\t},\n};\n\n/**\n * Auto-mounts resolution logic:\n */\nexport function expandAutoMounts(args: RunCLIArgs): RunCLIArgs {\n\tconst path = process.cwd();\n\n\tconst mount = [...(args.mount || [])];\n\tconst mountBeforeInstall = [...(args['mount-before-install'] || [])];\n\n\tconst newArgs = {\n\t\t...args,\n\t\tmount,\n\t\t'mount-before-install': mountBeforeInstall,\n\t\t'additional-blueprint-steps': [\n\t\t\t...((args as any)['additional-blueprint-steps'] || []),\n\t\t],\n\t};\n\n\tif (isPluginFilename(path)) {\n\t\tconst pluginName = basename(path);\n\t\tmount.push({\n\t\t\thostPath: path,\n\t\t\tvfsPath: `/wordpress/wp-content/plugins/${pluginName}`,\n\t\t});\n\t\tnewArgs['additional-blueprint-steps'].push({\n\t\t\tstep: 'activatePlugin',\n\t\t\tpluginPath: `/wordpress/wp-content/plugins/${basename(path)}`,\n\t\t});\n\t} else if (isThemeDirectory(path)) {\n\t\tconst themeName = basename(path);\n\t\tmount.push({\n\t\t\thostPath: path,\n\t\t\tvfsPath: `/wordpress/wp-content/themes/${themeName}`,\n\t\t});\n\t\tnewArgs['additional-blueprint-steps'].push({\n\t\t\tstep: 'activateTheme',\n\t\t\tthemeDirectoryName: themeName,\n\t\t});\n\t} else if (containsWpContentDirectories(path)) {\n\t\t/**\n\t\t * Mount each wp-content file and directory individually.\n\t\t */\n\t\tconst files = fs.readdirSync(path);\n\t\tfor (const file of files) {\n\t\t\t/**\n\t\t\t * WordPress already ships with the wp-content/index.php file\n\t\t\t * and Playground does not support overriding existing VFS files\n\t\t\t * with mounts.\n\t\t\t */\n\t\t\tif (file === 'index.php') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmount.push({\n\t\t\t\thostPath: `${path}/${file}`,\n\t\t\t\tvfsPath: `/wordpress/wp-content/${file}`,\n\t\t\t});\n\t\t}\n\t\tnewArgs['additional-blueprint-steps'].push(ACTIVATE_FIRST_THEME_STEP);\n\t} else if (containsFullWordPressInstallation(path)) {\n\t\tmountBeforeInstall.push({ hostPath: path, vfsPath: '/wordpress' });\n\t\t// @TODO: Uncomment when merging Blueprints v2 support\n\t\t// newArgs.mode = 'apply-to-existing-site';\n\t\tnewArgs['additional-blueprint-steps'].push(ACTIVATE_FIRST_THEME_STEP);\n\t} else {\n\t\t/**\n\t\t * By default, mount the current working directory as the Playground root.\n\t\t * This allows users to run and PHP or HTML files using the Playground CLI.\n\t\t */\n\t\tmount.push({ hostPath: path, vfsPath: '/wordpress' });\n\t\t// @TODO: Uncomment when merging Blueprints v2 support\n\t\t// newArgs.mode = 'mount-only';\n\t}\n\n\treturn newArgs as RunCLIArgs;\n}\n\nexport function containsFullWordPressInstallation(path: string): boolean {\n\tconst files = fs.readdirSync(path);\n\treturn (\n\t\tfiles.includes('wp-admin') &&\n\t\tfiles.includes('wp-includes') &&\n\t\tfiles.includes('wp-content')\n\t);\n}\n\nexport function containsWpContentDirectories(path: string): boolean {\n\tconst files = fs.readdirSync(path);\n\treturn (\n\t\tfiles.includes('themes') ||\n\t\tfiles.includes('plugins') ||\n\t\tfiles.includes('mu-plugins') ||\n\t\tfiles.includes('uploads')\n\t);\n}\n\nexport function isThemeDirectory(path: string): boolean {\n\tconst files = fs.readdirSync(path);\n\tif (!files.includes('style.css')) {\n\t\treturn false;\n\t}\n\tconst styleCssContent = fs.readFileSync(join(path, 'style.css'), 'utf8');\n\tconst themeNameRegex = /^(?:[ \\t]*<\\?php)?[ \\t/*#@]*Theme Name:(.*)$/im;\n\treturn !!themeNameRegex.exec(styleCssContent);\n}\n\nexport function isPluginFilename(path: string): boolean {\n\tconst files = fs.readdirSync(path);\n\tconst pluginNameRegex = /^(?:[ \\t]*<\\?php)?[ \\t/*#@]*Plugin Name:(.*)$/im;\n\tconst pluginNameMatch = files\n\t\t.filter((file) => file.endsWith('.php'))\n\t\t.find((file) => {\n\t\t\tconst fileContent = fs.readFileSync(join(path, file), 'utf8');\n\t\t\treturn !!pluginNameRegex.exec(fileContent);\n\t\t});\n\treturn !!pluginNameMatch;\n}\n","import type { FileLockManager } from '@php-wasm/node';\nimport { loadNodeRuntime } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport { bootWordPress } from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { jspi } from 'wasm-feature-detect';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { mountResources } from './mounts';\n\nexport interface Mount {\n\thostPath: string;\n\tvfsPath: string;\n}\n\nexport type PrimaryWorkerBootOptions = {\n\twpVersion?: string;\n\tphpVersion?: SupportedPHPVersion;\n\tabsoluteUrl: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tfirstProcessId: number;\n\tprocessIdSpaceLength: number;\n\tdataSqlPath?: string;\n\tfollowSymlinks: boolean;\n\ttrace: boolean;\n\t/**\n\t * When true, Playground will not send cookies to the client but will manage\n\t * them internally. This can be useful in environments that can't store cookies,\n\t * e.g. VS Code WebView.\n\t *\n\t * Default: false.\n\t */\n\tinternalCookieStore?: boolean;\n\twithXdebug?: boolean;\n};\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliWorker extends PHPWorker {\n\tbooted = false;\n\tfileLockManager: RemoteAPI<FileLockManager> | FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tif (await jspi()) {\n\t\t\t/**\n\t\t\t * If JSPI is available, php.js supports both synchronous and asynchronous locking syscalls.\n\t\t\t * Web browsers, however, only support asynchronous message passing so let's use the\n\t\t\t * asynchronous API. Every method call will return a promise.\n\t\t\t *\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tthis.fileLockManager = consumeAPI<FileLockManager>(port);\n\t\t} else {\n\t\t\t/**\n\t\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t\t * until the result is available.\n\t\t\t *\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t\t}\n\t}\n\n\tasync boot({\n\t\tabsoluteUrl,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t\tphpVersion = RecommendedPHPVersion,\n\t\twordPressZip,\n\t\tsqliteIntegrationPluginZip,\n\t\tfirstProcessId,\n\t\tprocessIdSpaceLength,\n\t\tdataSqlPath,\n\t\tfollowSymlinks,\n\t\ttrace,\n\t\tinternalCookieStore,\n\t\twithXdebug,\n\t}: PrimaryWorkerBootOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\tlet nextProcessId = firstProcessId;\n\t\tconst lastProcessId = firstProcessId + processIdSpaceLength - 1;\n\n\t\ttry {\n\t\t\tconst constants: Record<string, string | number | boolean | null> =\n\t\t\t\t{\n\t\t\t\t\tWP_DEBUG: true,\n\t\t\t\t\tWP_DEBUG_LOG: true,\n\t\t\t\t\tWP_DEBUG_DISPLAY: false,\n\t\t\t\t};\n\n\t\t\tconst requestHandler = await bootWordPress({\n\t\t\t\tsiteUrl: absoluteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\tconst processId = nextProcessId;\n\n\t\t\t\t\tif (nextProcessId < lastProcessId) {\n\t\t\t\t\t\tnextProcessId++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// We've reached the end of the process ID space. Start over.\n\t\t\t\t\t\tnextProcessId = firstProcessId;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks,\n\t\t\t\t\t\twithXdebug,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t )\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\thooks: {\n\t\t\t\t\tasync beforeWordPressFiles(php) {\n\t\t\t\t\t\tmountResources(php, mountsBeforeWpInstall);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcookieStore: internalCookieStore ? undefined : false,\n\t\t\t\tdataSqlPath,\n\t\t\t\tspawnHandler: sandboxedSpawnHandlerFactory,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tmountResources(primaryPhp, mountsAfterWpInstall);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliWorker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort!.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["mountResources","php","mounts","mount","createNodeFsMountHandler","tracePhpWasm","processId","format","args","sprintf","PlaygroundCliWorker","PHPWorker","monitor","port","jspi","consumeAPI","consumeAPISync","absoluteUrl","mountsBeforeWpInstall","mountsAfterWpInstall","phpVersion","RecommendedPHPVersion","wordPressZip","sqliteIntegrationPluginZip","firstProcessId","processIdSpaceLength","dataSqlPath","followSymlinks","trace","internalCookieStore","withXdebug","nextProcessId","lastProcessId","constants","requestHandler","bootWordPress","loadNodeRuntime","rootCertificates","sandboxedSpawnHandlerFactory","primaryPhp","setApiReady","e","setAPIError","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
package/worker-thread.cjs CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("@php-wasm/node"),M=require("@php-wasm/progress"),n=require("@php-wasm/universal"),S=require("@php-wasm/util"),W=require("@wp-playground/wordpress"),E=require("tls"),F=require("./index-CyPmrjJv.cjs"),f=require("worker_threads");function P(o,e){for(const t of e)o.mkdir(t.vfsPath),o.mount(t.vfsPath,h.createNodeFsMountHandler(t.hostPath))}function I(o,e,...t){console.log(performance.now().toFixed(6).padStart(15,"0"),o.toString().padStart(16,"0"),S.sprintf(e,...t))}class w extends n.PHPWorker{constructor(e){super(void 0,e),this.booted=!1}async useFileLockManager(e){await F.jspi()?this.fileLockManager=n.consumeAPI(e):this.fileLockManager=await n.consumeAPISync(e)}async boot({absoluteUrl:e,mountsBeforeWpInstall:t,mountsAfterWpInstall:m,phpVersion:y="8.0",wordPressZip:l,sqliteIntegrationPluginZip:d,firstProcessId:i,processIdSpaceLength:g,dataSqlPath:b,followSymlinks:k,trace:v,internalCookieStore:_}){if(this.booted)throw new Error("Playground already booted");this.booted=!0;let r=i;const q=i+g-1;try{const s={WP_DEBUG:!0,WP_DEBUG_LOG:!0,WP_DEBUG_DISPLAY:!1},u=await W.bootWordPress({siteUrl:e,createPhpRuntime:async()=>{const a=r;return r<q?r++:r=i,await h.loadNodeRuntime(y,{emscriptenOptions:{fileLockManager:this.fileLockManager,processId:a,trace:v?I:void 0},followSymlinks:k})},wordPressZip:l!==void 0?new File([l],"wordpress.zip"):void 0,sqliteIntegrationPluginZip:d!==void 0?new File([d],"sqlite-integration-plugin.zip"):void 0,sapiName:"cli",createFiles:{"/internal/shared/ca-bundle.crt":E.rootCertificates.join(`
2
- `)},constants:s,phpIniEntries:{"openssl.cafile":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""},hooks:{async beforeWordPressFiles(a){P(a,t)}},cookieStore:_?void 0:!1,dataSqlPath:b});this.__internal_setRequestHandler(u);const p=await u.getPrimaryPhp();await this.setPrimaryPHP(p),P(p,m),L()}catch(s){throw A(s),s}}async dispose(){await this[Symbol.asyncDispose]()}}const c=new f.MessageChannel,[L,A]=n.exposeAPI(new w(new M.EmscriptenDownloadMonitor),void 0,c.port1);f.parentPort.postMessage({command:"worker-script-initialized",phpPort:c.port2},[c.port2]);exports.PlaygroundCliWorker=w;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S=require("@php-wasm/node"),M=require("@php-wasm/progress"),r=require("@php-wasm/universal"),W=require("@php-wasm/util"),F=require("@wp-playground/common"),D=require("@wp-playground/wordpress"),E=require("tls"),H=require("wasm-feature-detect"),h=require("worker_threads"),P=require("./mounts-D7bhhGq3.cjs");function I(c,e,...s){console.log(performance.now().toFixed(6).padStart(15,"0"),c.toString().padStart(16,"0"),W.sprintf(e,...s))}class w extends r.PHPWorker{constructor(e){super(void 0,e),this.booted=!1}async useFileLockManager(e){await H.jspi()?this.fileLockManager=r.consumeAPI(e):this.fileLockManager=await r.consumeAPISync(e)}async boot({absoluteUrl:e,mountsBeforeWpInstall:s,mountsAfterWpInstall:m,phpVersion:y=F.RecommendedPHPVersion,wordPressZip:l,sqliteIntegrationPluginZip:d,firstProcessId:n,processIdSpaceLength:g,dataSqlPath:f,followSymlinks:b,trace:q,internalCookieStore:k,withXdebug:_}){if(this.booted)throw new Error("Playground already booted");this.booted=!0;let o=n;const v=n+g-1;try{const t={WP_DEBUG:!0,WP_DEBUG_LOG:!0,WP_DEBUG_DISPLAY:!1},u=await D.bootWordPress({siteUrl:e,createPhpRuntime:async()=>{const i=o;return o<v?o++:o=n,await S.loadNodeRuntime(y,{emscriptenOptions:{fileLockManager:this.fileLockManager,processId:i,trace:q?I:void 0},followSymlinks:b,withXdebug:_})},wordPressZip:l!==void 0?new File([l],"wordpress.zip"):void 0,sqliteIntegrationPluginZip:d!==void 0?new File([d],"sqlite-integration-plugin.zip"):void 0,sapiName:"cli",createFiles:{"/internal/shared/ca-bundle.crt":E.rootCertificates.join(`
2
+ `)},constants:t,phpIniEntries:{"openssl.cafile":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""},hooks:{async beforeWordPressFiles(i){P.mountResources(i,s)}},cookieStore:k?void 0:!1,dataSqlPath:f,spawnHandler:r.sandboxedSpawnHandlerFactory});this.__internal_setRequestHandler(u);const p=await u.getPrimaryPhp();await this.setPrimaryPHP(p),P.mountResources(p,m),L()}catch(t){throw R(t),t}}async dispose(){await this[Symbol.asyncDispose]()}}const a=new h.MessageChannel,[L,R]=r.exposeAPI(new w(new M.EmscriptenDownloadMonitor),void 0,a.port1);h.parentPort.postMessage({command:"worker-script-initialized",phpPort:a.port2},[a.port2]);exports.PlaygroundCliWorker=w;
3
3
  //# sourceMappingURL=worker-thread.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-thread.cjs","sources":["../../../../packages/playground/cli/src/worker-thread.ts"],"sourcesContent":["import type { FileLockManager } from '@php-wasm/node';\nimport { createNodeFsMountHandler, loadNodeRuntime } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { PHP, RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { bootWordPress } from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { jspi } from 'wasm-feature-detect';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\n\nexport interface Mount {\n\thostPath: string;\n\tvfsPath: string;\n}\n\nexport type PrimaryWorkerBootOptions = {\n\twpVersion?: string;\n\tphpVersion?: SupportedPHPVersion;\n\tabsoluteUrl: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tfirstProcessId: number;\n\tprocessIdSpaceLength: number;\n\tdataSqlPath?: string;\n\tfollowSymlinks: boolean;\n\ttrace: boolean;\n\t/**\n\t * When true, Playground will not send cookies to the client but will manage\n\t * them internally. This can be useful in environments that can't store cookies,\n\t * e.g. VS Code WebView.\n\t *\n\t * Default: false.\n\t */\n\tinternalCookieStore?: boolean;\n};\n\nfunction mountResources(php: PHP, mounts: Mount[]) {\n\tfor (const mount of mounts) {\n\t\tphp.mkdir(mount.vfsPath);\n\t\tphp.mount(mount.vfsPath, createNodeFsMountHandler(mount.hostPath));\n\t}\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliWorker extends PHPWorker {\n\tbooted = false;\n\tfileLockManager: RemoteAPI<FileLockManager> | FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tif (await jspi()) {\n\t\t\t/**\n\t\t\t * If JSPI is available, php.js supports both synchronous and asynchronous locking syscalls.\n\t\t\t * Web browsers, however, only support asynchronous message passing so let's use the\n\t\t\t * asynchronous API. Every method call will return a promise.\n\t\t\t *\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tthis.fileLockManager = consumeAPI<FileLockManager>(port);\n\t\t} else {\n\t\t\t/**\n\t\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t\t * until the result is available.\n\t\t\t *\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t\t}\n\t}\n\n\tasync boot({\n\t\tabsoluteUrl,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t\tphpVersion = '8.0',\n\t\twordPressZip,\n\t\tsqliteIntegrationPluginZip,\n\t\tfirstProcessId,\n\t\tprocessIdSpaceLength,\n\t\tdataSqlPath,\n\t\tfollowSymlinks,\n\t\ttrace,\n\t\tinternalCookieStore,\n\t}: PrimaryWorkerBootOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\tlet nextProcessId = firstProcessId;\n\t\tconst lastProcessId = firstProcessId + processIdSpaceLength - 1;\n\n\t\ttry {\n\t\t\tconst constants: Record<string, string | number | boolean | null> =\n\t\t\t\t{\n\t\t\t\t\tWP_DEBUG: true,\n\t\t\t\t\tWP_DEBUG_LOG: true,\n\t\t\t\t\tWP_DEBUG_DISPLAY: false,\n\t\t\t\t};\n\n\t\t\tconst requestHandler = await bootWordPress({\n\t\t\t\tsiteUrl: absoluteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\tconst processId = nextProcessId;\n\n\t\t\t\t\tif (nextProcessId < lastProcessId) {\n\t\t\t\t\t\tnextProcessId++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// We've reached the end of the process ID space. Start over.\n\t\t\t\t\t\tnextProcessId = firstProcessId;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t )\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\thooks: {\n\t\t\t\t\tasync beforeWordPressFiles(php) {\n\t\t\t\t\t\tmountResources(php, mountsBeforeWpInstall);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcookieStore: internalCookieStore ? undefined : false,\n\t\t\t\tdataSqlPath,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tmountResources(primaryPhp, mountsAfterWpInstall);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliWorker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort!.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["mountResources","php","mounts","mount","createNodeFsMountHandler","tracePhpWasm","processId","format","args","sprintf","PlaygroundCliWorker","PHPWorker","monitor","port","jspi","consumeAPI","consumeAPISync","absoluteUrl","mountsBeforeWpInstall","mountsAfterWpInstall","phpVersion","wordPressZip","sqliteIntegrationPluginZip","firstProcessId","processIdSpaceLength","dataSqlPath","followSymlinks","trace","internalCookieStore","nextProcessId","lastProcessId","constants","requestHandler","bootWordPress","loadNodeRuntime","rootCertificates","primaryPhp","setApiReady","e","setAPIError","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"oUA4CA,SAASA,EAAeC,EAAUC,EAAiB,CAClD,UAAWC,KAASD,EACfD,EAAA,MAAME,EAAM,OAAO,EACvBF,EAAI,MAAME,EAAM,QAASC,EAAAA,yBAAyBD,EAAM,QAAQ,CAAC,CAEnE,CASA,SAASE,EAAaC,EAAmBC,KAAmBC,EAAa,CAEhE,QAAA,IACP,YAAY,MAAM,QAAQ,CAAC,EAAE,SAAS,GAAI,GAAG,EAC7CF,EAAU,SAAW,EAAA,SAAS,GAAI,GAAG,EACrCG,EAAA,QAAQF,EAAQ,GAAGC,CAAI,CACxB,CACD,CAEO,MAAME,UAA4BC,EAAAA,SAAU,CAIlD,YAAYC,EAAoC,CAC/C,MAAM,OAAWA,CAAO,EAJhB,KAAA,OAAA,EAAA,CAgBT,MAAM,mBAAmBC,EAAmB,CACvC,MAAMC,SASJ,KAAA,gBAAkBC,aAA4BF,CAAI,EAUlD,KAAA,gBAAkB,MAAMG,EAAA,eAAgCH,CAAI,CAClE,CAGD,MAAM,KAAK,CACV,YAAAI,EACA,sBAAAC,EACA,qBAAAC,EACA,WAAAC,EAAa,MACb,aAAAC,EACA,2BAAAC,EACA,eAAAC,EACA,qBAAAC,EACA,YAAAC,EACA,eAAAC,EACA,MAAAC,EACA,oBAAAC,CAAA,EAC4B,CAC5B,GAAI,KAAK,OACF,MAAA,IAAI,MAAM,2BAA2B,EAE5C,KAAK,OAAS,GAEd,IAAIC,EAAgBN,EACd,MAAAO,EAAgBP,EAAiBC,EAAuB,EAE1D,GAAA,CACH,MAAMO,EACL,CACC,SAAU,GACV,aAAc,GACd,iBAAkB,EACnB,EAEKC,EAAiB,MAAMC,gBAAc,CAC1C,QAAShB,EACT,iBAAkB,SAAY,CAC7B,MAAMX,EAAYuB,EAElB,OAAIA,EAAgBC,EACnBD,IAGgBA,EAAAN,EAGV,MAAMW,kBAAgBd,EAAY,CACxC,kBAAmB,CAClB,gBAAiB,KAAK,gBACtB,UAAAd,EACA,MAAOqB,EAAQtB,EAAe,MAC/B,EACA,eAAAqB,CAAA,CACA,CACF,EACA,aACCL,IAAiB,OACd,IAAI,KAAK,CAACA,CAAY,EAAG,eAAe,EACxC,OACJ,2BACCC,IAA+B,OAC5B,IAAI,KACJ,CAACA,CAA0B,EAC3B,+BAEA,EAAA,OACJ,SAAU,MACV,YAAa,CACZ,iCACCa,EAAAA,iBAAiB,KAAK;AAAA,CAAI,CAC5B,EACA,UAAAJ,EACA,cAAe,CACd,iBAAkB,iCAClB,gBAAiB,IACjB,kBAAmB,EACpB,EACA,MAAO,CACN,MAAM,qBAAqB9B,EAAK,CAC/BD,EAAeC,EAAKiB,CAAqB,CAAA,CAE3C,EACA,YAAaU,EAAsB,OAAY,GAC/C,YAAAH,CAAA,CACA,EACD,KAAK,6BAA6BO,CAAc,EAE1C,MAAAI,EAAa,MAAMJ,EAAe,cAAc,EAChD,MAAA,KAAK,cAAcI,CAAU,EAEnCpC,EAAeoC,EAAYjB,CAAoB,EAEnCkB,EAAA,QACJC,EAAG,CACX,MAAAC,EAAYD,CAAU,EAChBA,CAAA,CACP,CAID,MAAM,SAAU,CACT,MAAA,KAAK,OAAO,YAAY,EAAE,CAAA,CAElC,CAEA,MAAME,EAAa,IAAIC,EAAAA,eAEjB,CAACJ,EAAaE,CAAW,EAAIG,EAAA,UAClC,IAAIhC,EAAoB,IAAIiC,EAAAA,yBAA2B,EACvD,OACAH,EAAW,KACZ,EAEAI,EAAAA,WAAY,YACX,CACC,QAAS,4BACT,QAASJ,EAAW,KACrB,EACA,CAACA,EAAW,KAAY,CACzB"}
1
+ {"version":3,"file":"worker-thread.cjs","sources":["../../../../packages/playground/cli/src/worker-thread.ts"],"sourcesContent":["import type { FileLockManager } from '@php-wasm/node';\nimport { loadNodeRuntime } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport { bootWordPress } from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { jspi } from 'wasm-feature-detect';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { mountResources } from './mounts';\n\nexport interface Mount {\n\thostPath: string;\n\tvfsPath: string;\n}\n\nexport type PrimaryWorkerBootOptions = {\n\twpVersion?: string;\n\tphpVersion?: SupportedPHPVersion;\n\tabsoluteUrl: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tfirstProcessId: number;\n\tprocessIdSpaceLength: number;\n\tdataSqlPath?: string;\n\tfollowSymlinks: boolean;\n\ttrace: boolean;\n\t/**\n\t * When true, Playground will not send cookies to the client but will manage\n\t * them internally. This can be useful in environments that can't store cookies,\n\t * e.g. VS Code WebView.\n\t *\n\t * Default: false.\n\t */\n\tinternalCookieStore?: boolean;\n\twithXdebug?: boolean;\n};\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliWorker extends PHPWorker {\n\tbooted = false;\n\tfileLockManager: RemoteAPI<FileLockManager> | FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tif (await jspi()) {\n\t\t\t/**\n\t\t\t * If JSPI is available, php.js supports both synchronous and asynchronous locking syscalls.\n\t\t\t * Web browsers, however, only support asynchronous message passing so let's use the\n\t\t\t * asynchronous API. Every method call will return a promise.\n\t\t\t *\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tthis.fileLockManager = consumeAPI<FileLockManager>(port);\n\t\t} else {\n\t\t\t/**\n\t\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t\t * until the result is available.\n\t\t\t *\n\t\t\t * @see comlink-sync.ts\n\t\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t\t */\n\t\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t\t}\n\t}\n\n\tasync boot({\n\t\tabsoluteUrl,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t\tphpVersion = RecommendedPHPVersion,\n\t\twordPressZip,\n\t\tsqliteIntegrationPluginZip,\n\t\tfirstProcessId,\n\t\tprocessIdSpaceLength,\n\t\tdataSqlPath,\n\t\tfollowSymlinks,\n\t\ttrace,\n\t\tinternalCookieStore,\n\t\twithXdebug,\n\t}: PrimaryWorkerBootOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\tlet nextProcessId = firstProcessId;\n\t\tconst lastProcessId = firstProcessId + processIdSpaceLength - 1;\n\n\t\ttry {\n\t\t\tconst constants: Record<string, string | number | boolean | null> =\n\t\t\t\t{\n\t\t\t\t\tWP_DEBUG: true,\n\t\t\t\t\tWP_DEBUG_LOG: true,\n\t\t\t\t\tWP_DEBUG_DISPLAY: false,\n\t\t\t\t};\n\n\t\t\tconst requestHandler = await bootWordPress({\n\t\t\t\tsiteUrl: absoluteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\tconst processId = nextProcessId;\n\n\t\t\t\t\tif (nextProcessId < lastProcessId) {\n\t\t\t\t\t\tnextProcessId++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// We've reached the end of the process ID space. Start over.\n\t\t\t\t\t\tnextProcessId = firstProcessId;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks,\n\t\t\t\t\t\twithXdebug,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t )\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\thooks: {\n\t\t\t\t\tasync beforeWordPressFiles(php) {\n\t\t\t\t\t\tmountResources(php, mountsBeforeWpInstall);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcookieStore: internalCookieStore ? undefined : false,\n\t\t\t\tdataSqlPath,\n\t\t\t\tspawnHandler: sandboxedSpawnHandlerFactory,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tmountResources(primaryPhp, mountsAfterWpInstall);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliWorker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort!.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["tracePhpWasm","processId","format","args","sprintf","PlaygroundCliWorker","PHPWorker","monitor","port","jspi","consumeAPI","consumeAPISync","absoluteUrl","mountsBeforeWpInstall","mountsAfterWpInstall","phpVersion","RecommendedPHPVersion","wordPressZip","sqliteIntegrationPluginZip","firstProcessId","processIdSpaceLength","dataSqlPath","followSymlinks","trace","internalCookieStore","withXdebug","nextProcessId","lastProcessId","constants","requestHandler","bootWordPress","loadNodeRuntime","rootCertificates","php","mountResources","sandboxedSpawnHandlerFactory","primaryPhp","setApiReady","e","setAPIError","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"yYAuDA,SAASA,EAAaC,EAAmBC,KAAmBC,EAAa,CAEhE,QAAA,IACP,YAAY,MAAM,QAAQ,CAAC,EAAE,SAAS,GAAI,GAAG,EAC7CF,EAAU,SAAW,EAAA,SAAS,GAAI,GAAG,EACrCG,EAAA,QAAQF,EAAQ,GAAGC,CAAI,CACxB,CACD,CAEO,MAAME,UAA4BC,EAAAA,SAAU,CAIlD,YAAYC,EAAoC,CAC/C,MAAM,OAAWA,CAAO,EAJhB,KAAA,OAAA,EAAA,CAgBT,MAAM,mBAAmBC,EAAmB,CACvC,MAAMC,SASJ,KAAA,gBAAkBC,aAA4BF,CAAI,EAUlD,KAAA,gBAAkB,MAAMG,EAAA,eAAgCH,CAAI,CAClE,CAGD,MAAM,KAAK,CACV,YAAAI,EACA,sBAAAC,EACA,qBAAAC,EACA,WAAAC,EAAaC,EAAA,sBACb,aAAAC,EACA,2BAAAC,EACA,eAAAC,EACA,qBAAAC,EACA,YAAAC,EACA,eAAAC,EACA,MAAAC,EACA,oBAAAC,EACA,WAAAC,CAAA,EAC4B,CAC5B,GAAI,KAAK,OACF,MAAA,IAAI,MAAM,2BAA2B,EAE5C,KAAK,OAAS,GAEd,IAAIC,EAAgBP,EACd,MAAAQ,EAAgBR,EAAiBC,EAAuB,EAE1D,GAAA,CACH,MAAMQ,EACL,CACC,SAAU,GACV,aAAc,GACd,iBAAkB,EACnB,EAEKC,EAAiB,MAAMC,gBAAc,CAC1C,QAASlB,EACT,iBAAkB,SAAY,CAC7B,MAAMX,EAAYyB,EAElB,OAAIA,EAAgBC,EACnBD,IAGgBA,EAAAP,EAGV,MAAMY,kBAAgBhB,EAAY,CACxC,kBAAmB,CAClB,gBAAiB,KAAK,gBACtB,UAAAd,EACA,MAAOsB,EAAQvB,EAAe,MAC/B,EACA,eAAAsB,EACA,WAAAG,CAAA,CACA,CACF,EACA,aACCR,IAAiB,OACd,IAAI,KAAK,CAACA,CAAY,EAAG,eAAe,EACxC,OACJ,2BACCC,IAA+B,OAC5B,IAAI,KACJ,CAACA,CAA0B,EAC3B,+BAEA,EAAA,OACJ,SAAU,MACV,YAAa,CACZ,iCACCc,EAAAA,iBAAiB,KAAK;AAAA,CAAI,CAC5B,EACA,UAAAJ,EACA,cAAe,CACd,iBAAkB,iCAClB,gBAAiB,IACjB,kBAAmB,EACpB,EACA,MAAO,CACN,MAAM,qBAAqBK,EAAK,CAC/BC,EAAA,eAAeD,EAAKpB,CAAqB,CAAA,CAE3C,EACA,YAAaW,EAAsB,OAAY,GAC/C,YAAAH,EACA,aAAcc,EAAAA,4BAAA,CACd,EACD,KAAK,6BAA6BN,CAAc,EAE1C,MAAAO,EAAa,MAAMP,EAAe,cAAc,EAChD,MAAA,KAAK,cAAcO,CAAU,EAEnCF,EAAA,eAAeE,EAAYtB,CAAoB,EAEnCuB,EAAA,QACJC,EAAG,CACX,MAAAC,EAAYD,CAAU,EAChBA,CAAA,CACP,CAID,MAAM,SAAU,CACT,MAAA,KAAK,OAAO,YAAY,EAAE,CAAA,CAElC,CAEA,MAAME,EAAa,IAAIC,EAAAA,eAEjB,CAACJ,EAAaE,CAAW,EAAIG,EAAA,UAClC,IAAIrC,EAAoB,IAAIsC,EAAAA,yBAA2B,EACvD,OACAH,EAAW,KACZ,EAEAI,EAAAA,WAAY,YACX,CACC,QAAS,4BACT,QAASJ,EAAW,KACrB,EACA,CAACA,EAAW,KAAY,CACzB"}
@@ -29,6 +29,7 @@ export type PrimaryWorkerBootOptions = {
29
29
  * Default: false.
30
30
  */
31
31
  internalCookieStore?: boolean;
32
+ withXdebug?: boolean;
32
33
  };
33
34
  export declare class PlaygroundCliWorker extends PHPWorker {
34
35
  booted: boolean;
@@ -44,6 +45,6 @@ export declare class PlaygroundCliWorker extends PHPWorker {
44
45
  * @see phpwasm-emscripten-library-file-locking-for-node.js
45
46
  */
46
47
  useFileLockManager(port: MessagePort): Promise<void>;
47
- boot({ absoluteUrl, mountsBeforeWpInstall, mountsAfterWpInstall, phpVersion, wordPressZip, sqliteIntegrationPluginZip, firstProcessId, processIdSpaceLength, dataSqlPath, followSymlinks, trace, internalCookieStore, }: PrimaryWorkerBootOptions): Promise<void>;
48
+ boot({ absoluteUrl, mountsBeforeWpInstall, mountsAfterWpInstall, phpVersion, wordPressZip, sqliteIntegrationPluginZip, firstProcessId, processIdSpaceLength, dataSqlPath, followSymlinks, trace, internalCookieStore, withXdebug, }: PrimaryWorkerBootOptions): Promise<void>;
48
49
  dispose(): Promise<void>;
49
50
  }