@wp-playground/cli 3.0.22 → 3.0.30
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.
- package/README.md +80 -45
- package/blueprints-v1/worker-thread-v1.d.ts +6 -6
- package/blueprints-v2/worker-thread-v2.d.ts +10 -5
- package/cli.cjs +1 -1
- package/cli.js +1 -1
- package/index.cjs +1 -1
- package/index.js +6 -4
- package/is-valid-wordpress-slug.d.ts +1 -1
- package/mounts.d.ts +1 -4
- package/package.json +16 -14
- package/resolve-blueprint.d.ts +1 -1
- package/run-cli-D8BNUM1f.cjs +42 -0
- package/run-cli-D8BNUM1f.cjs.map +1 -0
- package/run-cli-pLJHMn5d.js +1234 -0
- package/run-cli-pLJHMn5d.js.map +1 -0
- package/run-cli.d.ts +22 -2
- package/utils/progress.d.ts +3 -0
- package/worker-thread-v1.cjs +2 -2
- package/worker-thread-v1.cjs.map +1 -1
- package/worker-thread-v1.js +123 -117
- package/worker-thread-v1.js.map +1 -1
- package/worker-thread-v2.cjs +12 -8
- package/worker-thread-v2.cjs.map +1 -1
- package/worker-thread-v2.js +150 -79
- package/worker-thread-v2.js.map +1 -1
- package/mounts-ChxECdbN.js +0 -131
- package/mounts-ChxECdbN.js.map +0 -1
- package/mounts-R4uHe-O-.cjs +0 -16
- package/mounts-R4uHe-O-.cjs.map +0 -1
- package/run-cli-BIOpeo2v.cjs +0 -46
- package/run-cli-BIOpeo2v.cjs.map +0 -1
- package/run-cli-LDWC8vQD.js +0 -1391
- package/run-cli-LDWC8vQD.js.map +0 -1
- package/xdebug-path-mappings.d.ts +0 -92
package/mounts-ChxECdbN.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mounts-ChxECdbN.js","sources":["../../../../packages/playground/cli/src/mounts.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\t// @TODO: Remove DOCROOT check after moving totally to Blueprints v2.\n\t\tcontent: `<?php\n\t\t\t$docroot = getenv('DOCROOT') ? getenv('DOCROOT') : '/wordpress';\n\t\t\trequire_once \"$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 = args.autoMount!;\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\targs['experimental-blueprints-v2-runner']\n\t\t\t\t? {\n\t\t\t\t\t\tstep: 'activateTheme',\n\t\t\t\t\t\tthemeDirectoryName: themeName,\n\t\t\t\t }\n\t\t\t\t: {\n\t\t\t\t\t\tstep: 'activateTheme',\n\t\t\t\t\t\tthemeFolderName: themeName,\n\t\t\t\t }\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: If overriding another mode, throw an error or print a warning.\n\t\tnewArgs.mode = 'apply-to-existing-site';\n\t\tnewArgs['additional-blueprint-steps'].push(ACTIVATE_FIRST_THEME_STEP);\n\t\tif (!newArgs.wordpressInstallMode) {\n\t\t\tnewArgs.wordpressInstallMode =\n\t\t\t\t'install-from-existing-files-if-needed';\n\t\t}\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: If overriding another mode, throw an error or print a warning.\n\t\tnewArgs.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"],"names":["parseMountWithDelimiterArguments","mounts","parsedMounts","mount","mountParts","hostPath","vfsPath","existsSync","parseMountDirArguments","i","source","path","mountResources","php","createNodeFsMountHandler","ACTIVATE_FIRST_THEME_STEP","expandAutoMounts","args","mountBeforeInstall","newArgs","isPluginFilename","pluginName","basename","isThemeDirectory","themeName","containsWpContentDirectories","files","fs","file","containsFullWordPressInstallation","styleCssContent","join","pluginNameRegex","fileContent"],"mappings":"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"}
|
package/mounts-R4uHe-O-.cjs
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";const c=require("@php-wasm/node"),r=require("fs"),a=require("path");function l(t){const e=[];for(const n of t){const o=n.split(":");if(o.length!==2)throw new Error(`Invalid mount format: ${n}.
|
|
2
|
-
Expected format: /host/path:/vfs/path.
|
|
3
|
-
If your path contains a colon, e.g. C:\\myplugin, use the --mount-dir option instead.
|
|
4
|
-
Example: --mount-dir C:\\my-plugin /wordpress/wp-content/plugins/my-plugin`);const[s,i]=o;if(!r.existsSync(s))throw new Error(`Host path does not exist: ${s}`);e.push({hostPath:s,vfsPath:i})}return e}function h(t){if(t.length%2!==0)throw new Error("Invalid mount format. Expected: /host/path /vfs/path");const e=[];for(let n=0;n<t.length;n+=2){const o=t[n],s=t[n+1];if(!r.existsSync(o))throw new Error(`Host path does not exist: ${o}`);e.push({hostPath:a.resolve(process.cwd(),o),vfsPath:s})}return e}async function d(t,e){for(const n of e)await t.mount(n.vfsPath,c.createNodeFsMountHandler(n.hostPath))}const p={step:"runPHP",code:{filename:"activate-theme.php",content:`<?php
|
|
5
|
-
$docroot = getenv('DOCROOT') ? getenv('DOCROOT') : '/wordpress';
|
|
6
|
-
require_once "$docroot/wp-load.php";
|
|
7
|
-
$theme = wp_get_theme();
|
|
8
|
-
if (!$theme->exists()) {
|
|
9
|
-
$themes = wp_get_themes();
|
|
10
|
-
if (count($themes) > 0) {
|
|
11
|
-
$themeName = array_keys($themes)[0];
|
|
12
|
-
switch_theme($themeName);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
`}};function m(t){const e=t.autoMount,n=[...t.mount||[]],o=[...t["mount-before-install"]||[]],s={...t,mount:n,"mount-before-install":o,"additional-blueprint-steps":[...t["additional-blueprint-steps"]||[]]};if(P(e)){const i=a.basename(e);n.push({hostPath:e,vfsPath:`/wordpress/wp-content/plugins/${i}`}),s["additional-blueprint-steps"].push({step:"activatePlugin",pluginPath:`/wordpress/wp-content/plugins/${a.basename(e)}`})}else if(g(e)){const i=a.basename(e);n.push({hostPath:e,vfsPath:`/wordpress/wp-content/themes/${i}`}),s["additional-blueprint-steps"].push(t["experimental-blueprints-v2-runner"]?{step:"activateTheme",themeDirectoryName:i}:{step:"activateTheme",themeFolderName:i})}else if(w(e)){const i=r.readdirSync(e);for(const u of i)u!=="index.php"&&n.push({hostPath:`${e}/${u}`,vfsPath:`/wordpress/wp-content/${u}`});s["additional-blueprint-steps"].push(p)}else f(e)?(o.push({hostPath:e,vfsPath:"/wordpress"}),s.mode="apply-to-existing-site",s["additional-blueprint-steps"].push(p),s.wordpressInstallMode||(s.wordpressInstallMode="install-from-existing-files-if-needed")):(n.push({hostPath:e,vfsPath:"/wordpress"}),s.mode="mount-only");return s}function f(t){const e=r.readdirSync(t);return e.includes("wp-admin")&&e.includes("wp-includes")&&e.includes("wp-content")}function w(t){const e=r.readdirSync(t);return e.includes("themes")||e.includes("plugins")||e.includes("mu-plugins")||e.includes("uploads")}function g(t){if(!r.readdirSync(t).includes("style.css"))return!1;const n=r.readFileSync(a.join(t,"style.css"),"utf8");return!!/^(?:[ \t]*<\?php)?[ \t/*#@]*Theme Name:(.*)$/im.exec(n)}function P(t){const e=r.readdirSync(t),n=/^(?:[ \t]*<\?php)?[ \t/*#@]*Plugin Name:(.*)$/im;return!!e.filter(s=>s.endsWith(".php")).find(s=>{const i=r.readFileSync(a.join(t,s),"utf8");return!!n.exec(i)})}exports.expandAutoMounts=m;exports.mountResources=d;exports.parseMountDirArguments=h;exports.parseMountWithDelimiterArguments=l;
|
|
16
|
-
//# sourceMappingURL=mounts-R4uHe-O-.cjs.map
|
package/mounts-R4uHe-O-.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mounts-R4uHe-O-.cjs","sources":["../../../../packages/playground/cli/src/mounts.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\t// @TODO: Remove DOCROOT check after moving totally to Blueprints v2.\n\t\tcontent: `<?php\n\t\t\t$docroot = getenv('DOCROOT') ? getenv('DOCROOT') : '/wordpress';\n\t\t\trequire_once \"$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 = args.autoMount!;\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\targs['experimental-blueprints-v2-runner']\n\t\t\t\t? {\n\t\t\t\t\t\tstep: 'activateTheme',\n\t\t\t\t\t\tthemeDirectoryName: themeName,\n\t\t\t\t }\n\t\t\t\t: {\n\t\t\t\t\t\tstep: 'activateTheme',\n\t\t\t\t\t\tthemeFolderName: themeName,\n\t\t\t\t }\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: If overriding another mode, throw an error or print a warning.\n\t\tnewArgs.mode = 'apply-to-existing-site';\n\t\tnewArgs['additional-blueprint-steps'].push(ACTIVATE_FIRST_THEME_STEP);\n\t\tif (!newArgs.wordpressInstallMode) {\n\t\t\tnewArgs.wordpressInstallMode =\n\t\t\t\t'install-from-existing-files-if-needed';\n\t\t}\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: If overriding another mode, throw an error or print a warning.\n\t\tnewArgs.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"],"names":["parseMountWithDelimiterArguments","mounts","parsedMounts","mount","mountParts","hostPath","vfsPath","existsSync","parseMountDirArguments","i","source","path","mountResources","php","createNodeFsMountHandler","ACTIVATE_FIRST_THEME_STEP","expandAutoMounts","args","mountBeforeInstall","newArgs","isPluginFilename","pluginName","basename","isThemeDirectory","themeName","containsWpContentDirectories","files","fs","file","containsFullWordPressInstallation","styleCssContent","join","pluginNameRegex","fileContent"],"mappings":"iFA0BO,SAASA,EAAiCC,EAA2B,CAC3E,MAAMC,EAAe,CAAA,EACrB,UAAWC,KAASF,EAAQ,CAC3B,MAAMG,EAAaD,EAAM,MAAM,GAAG,EAClC,GAAIC,EAAW,SAAW,EACzB,MAAM,IAAI,MAAM,yBAAyBD,CAAK;AAAA;AAAA;AAAA,+EAG8B,EAE7E,KAAM,CAACE,EAAUC,CAAO,EAAIF,EAC5B,GAAI,CAACG,EAAAA,WAAWF,CAAQ,EACvB,MAAM,IAAI,MAAM,6BAA6BA,CAAQ,EAAE,EAExDH,EAAa,KAAK,CAAE,SAAAG,EAAU,QAAAC,CAAA,CAAS,CACxC,CACA,OAAOJ,CACR,CAiBO,SAASM,EAAuBP,EAA2B,CACjE,GAAIA,EAAO,OAAS,IAAM,EACzB,MAAM,IAAI,MAAM,sDAAsD,EAGvE,MAAMC,EAAe,CAAA,EACrB,QAASO,EAAI,EAAGA,EAAIR,EAAO,OAAQQ,GAAK,EAAG,CAC1C,MAAMC,EAAST,EAAOQ,CAAC,EACjBH,EAAUL,EAAOQ,EAAI,CAAC,EAC5B,GAAI,CAACF,EAAAA,WAAWG,CAAM,EACrB,MAAM,IAAI,MAAM,6BAA6BA,CAAM,EAAE,EAEtDR,EAAa,KAAK,CACjB,SAAUS,EAAK,QAAQ,QAAQ,IAAA,EAAOD,CAAM,EAC5C,QAAAJ,CAAA,CACA,CACF,CACA,OAAOJ,CACR,CAEA,eAAsBU,EAAeC,EAAUZ,EAAiB,CAC/D,UAAWE,KAASF,EACnB,MAAMY,EAAI,MACTV,EAAM,QACNW,EAAAA,yBAAyBX,EAAM,QAAQ,CAAA,CAG1C,CAEA,MAAMY,EAA4B,CACjC,KAAM,SACN,KAAM,CACL,SAAU,qBAEV,QAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAAA,CAaX,EAKO,SAASC,EAAiBC,EAA8B,CAC9D,MAAMN,EAAOM,EAAK,UAEZd,EAAQ,CAAC,GAAIc,EAAK,OAAS,CAAA,CAAG,EAC9BC,EAAqB,CAAC,GAAID,EAAK,sBAAsB,GAAK,CAAA,CAAG,EAE7DE,EAAU,CACf,GAAGF,EACH,MAAAd,EACA,uBAAwBe,EACxB,6BAA8B,CAC7B,GAAKD,EAAa,4BAA4B,GAAK,CAAA,CAAC,CACrD,EAGD,GAAIG,EAAiBT,CAAI,EAAG,CAC3B,MAAMU,EAAaC,EAAAA,SAASX,CAAI,EAChCR,EAAM,KAAK,CACV,SAAUQ,EACV,QAAS,iCAAiCU,CAAU,EAAA,CACpD,EACDF,EAAQ,4BAA4B,EAAE,KAAK,CAC1C,KAAM,iBACN,WAAY,iCAAiCG,EAAAA,SAASX,CAAI,CAAC,EAAA,CAC3D,CACF,SAAWY,EAAiBZ,CAAI,EAAG,CAClC,MAAMa,EAAYF,EAAAA,SAASX,CAAI,EAC/BR,EAAM,KAAK,CACV,SAAUQ,EACV,QAAS,gCAAgCa,CAAS,EAAA,CAClD,EACDL,EAAQ,4BAA4B,EAAE,KACrCF,EAAK,mCAAmC,EACrC,CACA,KAAM,gBACN,mBAAoBO,CAAA,EAEpB,CACA,KAAM,gBACN,gBAAiBA,CAAA,CACjB,CAEL,SAAWC,EAA6Bd,CAAI,EAAG,CAI9C,MAAMe,EAAQC,EAAG,YAAYhB,CAAI,EACjC,UAAWiB,KAAQF,EAMdE,IAAS,aAGbzB,EAAM,KAAK,CACV,SAAU,GAAGQ,CAAI,IAAIiB,CAAI,GACzB,QAAS,yBAAyBA,CAAI,EAAA,CACtC,EAEFT,EAAQ,4BAA4B,EAAE,KAAKJ,CAAyB,CACrE,MAAWc,EAAkClB,CAAI,GAChDO,EAAmB,KAAK,CAAE,SAAUP,EAAM,QAAS,aAAc,EAEjEQ,EAAQ,KAAO,yBACfA,EAAQ,4BAA4B,EAAE,KAAKJ,CAAyB,EAC/DI,EAAQ,uBACZA,EAAQ,qBACP,2CAOFhB,EAAM,KAAK,CAAE,SAAUQ,EAAM,QAAS,aAAc,EAEpDQ,EAAQ,KAAO,cAGhB,OAAOA,CACR,CAEO,SAASU,EAAkClB,EAAuB,CACxE,MAAMe,EAAQC,EAAG,YAAYhB,CAAI,EACjC,OACCe,EAAM,SAAS,UAAU,GACzBA,EAAM,SAAS,aAAa,GAC5BA,EAAM,SAAS,YAAY,CAE7B,CAEO,SAASD,EAA6Bd,EAAuB,CACnE,MAAMe,EAAQC,EAAG,YAAYhB,CAAI,EACjC,OACCe,EAAM,SAAS,QAAQ,GACvBA,EAAM,SAAS,SAAS,GACxBA,EAAM,SAAS,YAAY,GAC3BA,EAAM,SAAS,SAAS,CAE1B,CAEO,SAASH,EAAiBZ,EAAuB,CAEvD,GAAI,CADUgB,EAAG,YAAYhB,CAAI,EACtB,SAAS,WAAW,EAC9B,MAAO,GAER,MAAMmB,EAAkBH,EAAG,aAAaI,EAAAA,KAAKpB,EAAM,WAAW,EAAG,MAAM,EAEvE,MAAO,CAAC,CADe,iDACC,KAAKmB,CAAe,CAC7C,CAEO,SAASV,EAAiBT,EAAuB,CACvD,MAAMe,EAAQC,EAAG,YAAYhB,CAAI,EAC3BqB,EAAkB,kDAOxB,MAAO,CAAC,CANgBN,EACtB,OAAQE,GAASA,EAAK,SAAS,MAAM,CAAC,EACtC,KAAMA,GAAS,CACf,MAAMK,EAAcN,EAAG,aAAaI,EAAAA,KAAKpB,EAAMiB,CAAI,EAAG,MAAM,EAC5D,MAAO,CAAC,CAACI,EAAgB,KAAKC,CAAW,CAC1C,CAAC,CAEH"}
|
package/run-cli-BIOpeo2v.cjs
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";var me=Object.create;var K=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,ye=Object.prototype.hasOwnProperty;var be=(e,o,t,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of ge(o))!ye.call(e,s)&&s!==t&&K(e,s,{get:()=>o[s],enumerable:!(i=he(o,s))||i.enumerable});return e};var ve=(e,o,t)=>(t=e!=null?me(we(e)):{},be(o||!e||!e.__esModule?K(t,"default",{value:e,enumerable:!0}):t,e));const h=require("@php-wasm/logger"),k=require("@php-wasm/universal"),M=require("@wp-playground/blueprints"),z=require("@wp-playground/common"),u=require("fs"),J=require("worker_threads"),W=require("./mounts-R4uHe-O-.cjs"),Pe=require("express"),Se=require("@php-wasm/node"),Y=require("os"),xe=require("wasm-feature-detect"),ke=require("yargs"),m=require("path"),U=require("@wp-playground/storage"),Q=require("@php-wasm/progress"),Ie=require("@wp-playground/wordpress"),j=require("fs-extra"),Ee=require("@php-wasm/xdebug-bridge"),ee=require("tmp-promise"),Ce=require("ps-man"),q=require("fast-xml-parser"),P=require("jsonc-parser");var $=typeof document<"u"?document.currentScript:null;async function Le(e){const o=Pe(),t=await new Promise((n,a)=>{const r=o.listen(e.port,()=>{const l=r.address();l===null||typeof l=="string"?a(new Error("Server address is not available")):n(r)})});o.use("/",async(n,a)=>{let r;try{r=await e.handleRequest({url:n.url,headers:Te(n),method:n.method,body:await Re(n)})}catch(l){h.logger.error(l),r=k.PHPResponse.forHttpCode(500)}a.statusCode=r.httpStatusCode;for(const l in r.headers)a.setHeader(l,r.headers[l]);a.end(r.bytes)});const s=t.address().port;return await e.onBind(t,s)}const Re=async e=>await new Promise(o=>{const t=[];e.on("data",i=>{t.push(i)}),e.on("end",()=>{o(new Uint8Array(Buffer.concat(t)))})}),Te=e=>{const o={};if(e.rawHeaders&&e.rawHeaders.length)for(let t=0;t<e.rawHeaders.length;t+=2)o[e.rawHeaders[t].toLowerCase()]=e.rawHeaders[t+1];return o};class $e{constructor(o){this.workerLoads=[],this.addWorker(o)}addWorker(o){this.workerLoads.push({worker:o,activeRequests:new Set})}async removeWorker(o){const t=this.workerLoads.findIndex(s=>s.worker===o);if(t===-1)return;const[i]=this.workerLoads.splice(t,1);await Promise.allSettled(i.activeRequests)}async handleRequest(o){let t=this.workerLoads[0];for(let s=1;s<this.workerLoads.length;s++){const n=this.workerLoads[s];n.activeRequests.size<t.activeRequests.size&&(t=n)}const i=t.worker.request(o);return t.activeRequests.add(i),i.url=o.url,i.finally(()=>{t.activeRequests.delete(i)})}}function Me(e){return/^latest$|^trunk$|^nightly$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/.test(e)}async function je({sourceString:e,blueprintMayReadAdjacentFiles:o}){if(!e)return;if(e.startsWith("http://")||e.startsWith("https://"))return await M.resolveRemoteBlueprint(e);let t=m.resolve(process.cwd(),e);if(!u.existsSync(t))throw new Error(`Blueprint file does not exist: ${t}`);const i=u.statSync(t);if(i.isDirectory()&&(t=m.join(t,"blueprint.json")),!i.isFile()&&i.isSymbolicLink())throw new Error(`Blueprint path is neither a file nor a directory: ${t}`);const s=m.extname(t);switch(s){case".zip":return U.ZipFilesystem.fromArrayBuffer(u.readFileSync(t).buffer);case".json":{const n=u.readFileSync(t,"utf-8");try{JSON.parse(n)}catch{throw new Error(`Blueprint file at ${t} is not a valid JSON file`)}const a=m.dirname(t),r=new U.NodeJsFilesystem(a);return new U.OverlayFilesystem([new U.InMemoryFilesystem({"blueprint.json":n}),{read(l){if(!o)throw new Error(`Error: Blueprint contained tried to read a local file at path "${l}" (via a resource of type "bundled"). Playground restricts access to local resources by default as a security measure.
|
|
2
|
-
|
|
3
|
-
You can allow this Blueprint to read files from the same parent directory by explicitly adding the --blueprint-may-read-adjacent-files option to your command.`);return r.read(l)}}])}default:throw new Error(`Unsupported blueprint file extension: ${s}. Only .zip and .json files are supported.`)}}class Be{constructor(o,t){this.lastProgressMessage="",this.args=o,this.siteUrl=t.siteUrl,this.processIdSpaceLength=t.processIdSpaceLength,this.phpVersion=o.php}getWorkerType(){return"v2"}async bootAndSetUpInitialPlayground(o,t,i){const s=k.consumeAPI(o);await s.useFileLockManager(t);const n={...this.args,phpVersion:this.phpVersion,siteUrl:this.siteUrl,firstProcessId:1,processIdSpaceLength:this.processIdSpaceLength,trace:this.args.debug||!1,blueprint:this.args.blueprint,withXdebug:!1,xdebug:void 0,nativeInternalDirPath:i};return await s.bootAndSetUpInitialWorker(n),s}async bootPlayground({worker:o,fileLockManagerPort:t,firstProcessId:i,nativeInternalDirPath:s}){const n=k.consumeAPI(o.phpPort);await n.useFileLockManager(t);const a={...this.args,phpVersion:this.phpVersion,siteUrl:this.siteUrl,firstProcessId:i,processIdSpaceLength:this.processIdSpaceLength,trace:this.args.debug||!1,withXdebug:!!this.args.xdebug,nativeInternalDirPath:s,mountsBeforeWpInstall:this.args["mount-before-install"]||[],mountsAfterWpInstall:this.args.mount||[]};return await n.bootWorker(a),n}writeProgressUpdate(o,t,i){t!==this.lastProgressMessage&&(this.lastProgressMessage=t,o.isTTY?(o.cursorTo(0),o.write(t),o.clearLine(1),i&&o.write(`
|
|
4
|
-
`)):o.write(`${t}
|
|
5
|
-
`))}}const Z=m.join(Y.homedir(),".wordpress-playground");async function Fe(e){return await re("https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip","sqlite.zip",e)}async function re(e,o,t){const i=m.join(Z,o);return j.existsSync(i)||(j.ensureDirSync(Z),await De(e,i,t)),ne(i)}async function De(e,o,t){const s=(await t.monitorFetch(fetch(e))).body.getReader(),n=`${o}.partial`,a=j.createWriteStream(n);for(;;){const{done:r,value:l}=await s.read();if(l&&a.write(l),r)break}a.close(),a.closed||await new Promise((r,l)=>{a.on("finish",()=>{j.renameSync(n,o),r(null)}),a.on("error",d=>{j.removeSync(n),l(d)})})}function ne(e,o){return new File([j.readFileSync(e)],m.basename(e))}class We{constructor(o,t){this.lastProgressMessage="",this.args=o,this.siteUrl=t.siteUrl,this.processIdSpaceLength=t.processIdSpaceLength}getWorkerType(){return"v1"}async bootAndSetUpInitialPlayground(o,t,i){let s,n,a;const r=new Q.EmscriptenDownloadMonitor;if(this.args.wordpressInstallMode==="download-and-install"){let E=!1;r.addEventListener("progress",B=>{if(E)return;const{loaded:x,total:f}=B.detail,F=Math.floor(Math.min(100,100*x/f));E=F===100,this.writeProgressUpdate(process.stdout,`Downloading WordPress ${F}%...`,E)}),s=await Ie.resolveWordPressRelease(this.args.wp),a=m.join(Z,`prebuilt-wp-content-for-wp-${s.version}.zip`),n=u.existsSync(a)?ne(a):await re(s.releaseUrl,`${s.version}.zip`,r),h.logger.log(`Resolved WordPress release URL: ${s?.releaseUrl}`)}h.logger.log("Fetching SQLite integration plugin...");const l=this.args.skipSqliteSetup?void 0:await Fe(r),d=this.args.followSymlinks===!0,c=this.args.experimentalTrace===!0,p=this.args["mount-before-install"]||[],g=this.args.mount||[],b=k.consumeAPI(o);await b.isConnected(),h.logger.log("Booting WordPress...");const S=await M.resolveRuntimeConfiguration(this.getEffectiveBlueprint());return await b.useFileLockManager(t),await b.bootAndSetUpInitialWorker({phpVersion:S.phpVersion,wpVersion:S.wpVersion,siteUrl:this.siteUrl,mountsBeforeWpInstall:p,mountsAfterWpInstall:g,wordpressInstallMode:this.args.wordpressInstallMode||"download-and-install",wordPressZip:n&&await n.arrayBuffer(),sqliteIntegrationPluginZip:await l?.arrayBuffer(),firstProcessId:0,processIdSpaceLength:this.processIdSpaceLength,followSymlinks:d,trace:c,internalCookieStore:this.args.internalCookieStore,withXdebug:!1,nativeInternalDirPath:i}),a&&!this.args["mount-before-install"]&&!u.existsSync(a)&&(h.logger.log("Caching preinstalled WordPress for the next boot..."),u.writeFileSync(a,await z.zipDirectory(b,"/wordpress")),h.logger.log("Cached!")),b}async bootPlayground({worker:o,fileLockManagerPort:t,firstProcessId:i,nativeInternalDirPath:s}){const n=k.consumeAPI(o.phpPort);await n.isConnected();const a=await M.resolveRuntimeConfiguration(this.getEffectiveBlueprint());return await n.useFileLockManager(t),await n.bootWorker({phpVersion:a.phpVersion,siteUrl:this.siteUrl,mountsBeforeWpInstall:this.args["mount-before-install"]||[],mountsAfterWpInstall:this.args.mount||[],firstProcessId:i,processIdSpaceLength:this.processIdSpaceLength,followSymlinks:this.args.followSymlinks===!0,trace:this.args.experimentalTrace===!0,internalCookieStore:this.args.internalCookieStore,withXdebug:!!this.args.xdebug,nativeInternalDirPath:s}),await n.isReady(),n}async compileInputBlueprint(o){const t=this.getEffectiveBlueprint(),i=new Q.ProgressTracker;let s="",n=!1;return i.addEventListener("progress",a=>{if(n)return;n=a.detail.progress===100;const r=Math.floor(a.detail.progress);s=a.detail.caption||s||"Running the Blueprint";const l=`${s.trim()} – ${r}%`;this.writeProgressUpdate(process.stdout,l,n)}),await M.compileBlueprintV1(t,{progress:i,additionalSteps:o})}getEffectiveBlueprint(){const o=this.args.blueprint;return M.isBlueprintBundle(o)?o:{login:this.args.login,...o||{},preferredVersions:{php:this.args.php??o?.preferredVersions?.php??z.RecommendedPHPVersion,wp:this.args.wp??o?.preferredVersions?.wp??"latest",...o?.preferredVersions||{}}}}writeProgressUpdate(o,t,i){this.args.verbosity!==V.Quiet.name&&t!==this.lastProgressMessage&&(this.lastProgressMessage=t,o.isTTY?(o.cursorTo(0),o.write(t),o.clearLine(1),i&&o.write(`
|
|
6
|
-
`)):o.write(`${t}
|
|
7
|
-
`))}}async function Ae(e,o=!0){const i=`${m.basename(process.argv0)}${e}${process.pid}-`,s=await ee.dir({prefix:i,unsafeCleanup:!0});return o&&ee.setGracefulCleanup(),s}async function Ue(e,o,t){const s=(await qe(e,o,t)).map(n=>new Promise(a=>{u.rm(n,{recursive:!0},r=>{r?h.logger.warn(`Failed to delete stale Playground temp dir: ${n}`,r):h.logger.info(`Deleted stale Playground temp dir: ${n}`),a()})}));await Promise.all(s)}async function qe(e,o,t){try{const i=u.readdirSync(t).map(n=>m.join(t,n)),s=[];for(const n of i)await Ne(e,o,n)&&s.push(n);return s}catch(i){return h.logger.warn(`Failed to find stale Playground temp dirs: ${i}`),[]}}async function Ne(e,o,t){if(!u.lstatSync(t).isDirectory())return!1;const s=m.basename(t);if(!s.includes(e))return!1;const n=s.match(new RegExp(`^(.+)${e}(\\d+)-`));if(!n)return!1;const a={executableName:n[1],pid:n[2]};if(await Ve(a.pid,a.executableName))return!1;const r=Date.now()-o;return u.statSync(t).mtime.getTime()<r}async function Ve(e,o){const[t]=await new Promise((i,s)=>{Ce.list({pid:e,name:o,clean:!0},(n,a)=>{n?s(n):i(a)})});return!!t&&t.pid===e&&t.command===o}async function _e(e,o,t){const i=t==="win32"?"junction":"dir";u.symlinkSync(e,o,i)}async function Oe(e){try{u.lstatSync(e).isSymbolicLink()&&u.unlinkSync(e)}catch{}}function He(e,o){return o.filter(t=>{const i=m.resolve(t.hostPath),s=m.join(e,m.sep);return i===e||i.startsWith(s)})}const C={ignoreAttributes:!1,attributeNamePrefix:"",preserveOrder:!0,cdataPropName:"__cdata",commentPropName:"__xmlComment",allowBooleanAttributes:!0,trimValues:!0},ie={ignoreAttributes:C.ignoreAttributes,attributeNamePrefix:C.attributeNamePrefix,preserveOrder:C.preserveOrder,cdataPropName:C.cdataPropName,commentPropName:C.commentPropName,suppressBooleanAttributes:!C.allowBooleanAttributes,format:!0,indentBy:" "},N={allowEmptyContent:!0,allowTrailingComma:!0};function Xe(e,o){const{name:t,host:i,port:s,mappings:n,ideKey:a}=o,r=new q.XMLParser(C),l=(()=>{try{return r.parse(e,!0)}catch{throw new Error("PhpStorm configuration file is not valid XML.")}})(),d={server:[{path_mappings:n.map(f=>({mapping:[],":@":{"local-root":`$PROJECT_DIR$/${ae(m.relative(o.projectDir,f.hostPath))}`,"remote-root":f.vfsPath}}))}],":@":{name:t,host:`${i}:${s}`,use_path_mappings:"true"}};let c=l?.find(f=>!!f?.project);if(c){const f=c[":@"]?.version;if(f===void 0)throw new Error('PhpStorm IDE integration only supports <project version="4"> in workspace.xml, but the <project> configuration has no version number.');if(f!=="4")throw new Error(`PhpStorm IDE integration only supports <project version="4"> in workspace.xml, but we found a <project> configuration with version "${f}".`)}c===void 0&&(c={project:[],":@":{version:"4"}},l.push(c));let p=c.project?.find(f=>!!f?.component&&f?.[":@"]?.name==="PhpServers");p===void 0&&(p={component:[],":@":{name:"PhpServers"}},c.project===void 0&&(c.project=[]),c.project.push(p));let g=p.component?.find(f=>!!f?.servers);g===void 0&&(g={servers:[]},p.component===void 0&&(p.component=[]),p.component.push(g));const b=g.servers?.findIndex(f=>!!f?.server&&f?.[":@"]?.name===t);(b===void 0||b<0)&&(g.servers===void 0&&(g.servers=[]),g.servers.push(d));let S=c.project?.find(f=>!!f?.component&&f?.[":@"]?.name==="RunManager");if(S===void 0&&(S={component:[],":@":{name:"RunManager"}},c.project===void 0&&(c.project=[]),c.project.push(S)),(S.component?.findIndex(f=>!!f?.configuration&&f?.[":@"]?.name===t)??-1)<0){const f={configuration:[{method:[],":@":{v:"2"}}],":@":{name:t,type:"PhpRemoteDebugRunConfigurationType",factoryName:"PHP Remote Debug",filter_connections:"FILTER",server_name:t,session_id:a}};S.component===void 0&&(S.component=[]),S.component.push(f)}const x=new q.XMLBuilder(ie).build(l);try{r.parse(x,!0)}catch{throw new Error("The resulting PhpStorm configuration file is not valid XML.")}return x}function ze(e,o){const{name:t,mappings:i}=o,s=[];let n=e,a=P.parseTree(n,s,N);if(a===void 0||s.length)throw new Error("VS Code configuration file is not valid JSON.");let r=P.findNodeAtLocation(a,["configurations"]);if(r===void 0||r.children===void 0){const d=P.modify(n,["configurations"],[],{});n=P.applyEdits(n,d),a=P.parseTree(n,[],N),r=P.findNodeAtLocation(a,["configurations"])}const l=r?.children?.findIndex(d=>P.findNodeAtLocation(d,["name"])?.value===t);if(l===void 0||l<0){const d={name:t,type:"php",request:"launch",port:9003,pathMappings:i.reduce((g,b)=>(g[b.vfsPath]=`\${workspaceFolder}/${ae(m.relative(o.workspaceDir,b.hostPath))}`,g),{})},c=r?.children?.length||0,p=P.modify(n,["configurations",c],d,{formattingOptions:{insertSpaces:!0,tabSize:4,eol:`
|
|
8
|
-
`}});n=se(n,p)}return n}async function Je({name:e,ides:o,host:t,port:i,cwd:s,mounts:n,ideKey:a="PLAYGROUNDCLI"}){const r=He(s,n),l=[];if(o.includes("phpstorm")){const d=".idea/workspace.xml",c=m.join(s,d);if(!u.existsSync(c)){if(u.existsSync(m.dirname(c)))u.writeFileSync(c,`<?xml version="1.0" encoding="UTF-8"?>
|
|
9
|
-
<project version="4">
|
|
10
|
-
</project>`);else if(o.length==1)throw new Error("PhpStorm IDE integration requested, but no '.idea' directory was found in the current working directory.")}if(u.existsSync(c)){const p=u.readFileSync(c,"utf8"),g=Xe(p,{name:e,host:t,port:i,projectDir:s,mappings:r,ideKey:a});u.writeFileSync(c,g)}l.push(d)}if(o.includes("vscode")){const d=".vscode/launch.json",c=m.join(s,d);if(!u.existsSync(c)){if(u.existsSync(m.dirname(c)))u.writeFileSync(c,`{
|
|
11
|
-
"configurations": []
|
|
12
|
-
}`);else if(o.length==1)throw new Error("VS Code IDE integration requested, but no '.vscode' directory was found in the current working directory.")}if(u.existsSync(c)){const p=u.readFileSync(c,"utf-8"),g=ze(p,{name:e,workspaceDir:s,mappings:r});g!==p&&(u.writeFileSync(c,g),l.push(d))}}return l}async function Ze(e,o){const t=m.join(o,".idea/workspace.xml");if(u.existsSync(t)){const s=u.readFileSync(t,"utf8"),n=new q.XMLParser(C),a=(()=>{try{return n.parse(s,!0)}catch{throw new Error("PhpStorm configuration file is not valid XML.")}})(),d=a.find(p=>!!p?.project)?.project?.find(p=>!!p?.component&&p?.[":@"]?.name==="PhpServers")?.component?.find(p=>!!p?.servers),c=d?.servers?.findIndex(p=>!!p?.server&&p?.[":@"]?.name===e);if(c!==void 0&&c>=0){d.servers.splice(c,1);const g=new q.XMLBuilder(ie).build(a);try{n.parse(g,!0)}catch{throw new Error("The resulting PhpStorm configuration file is not valid XML.")}g===`<?xml version="1.0" encoding="UTF-8"?>
|
|
13
|
-
<project version="4">
|
|
14
|
-
<component name="PhpServers">
|
|
15
|
-
<servers></servers>
|
|
16
|
-
</component>
|
|
17
|
-
</project>`?u.unlinkSync(t):u.writeFileSync(t,g)}}const i=m.join(o,".vscode/launch.json");if(u.existsSync(i)){const s=[],n=u.readFileSync(i,"utf-8"),a=P.parseTree(n,s,N);if(a===void 0||s.length)throw new Error("VS Code configuration file is not valid JSON.");const l=P.findNodeAtLocation(a,["configurations"])?.children?.findIndex(d=>P.findNodeAtLocation(d,["name"])?.value===e);if(l!==void 0&&l>=0){const d=P.modify(n,["configurations",l],void 0,{formattingOptions:{insertSpaces:!0,tabSize:4,eol:`
|
|
18
|
-
`}}),c=se(n,d);c===`{
|
|
19
|
-
"configurations": []
|
|
20
|
-
}`?u.unlinkSync(i):u.writeFileSync(i,c)}}}function se(e,o){const t=[],i=P.applyEdits(e,o);if(t.length=0,P.parseTree(i,t,N),t.length){const s=t.map(a=>({message:P.printParseErrorCode(a.error),offset:a.offset,length:a.length,fragment:i.slice(Math.max(0,a.offset-20),Math.min(i.length,a.offset+a.length+10))})).map(a=>`${a.message} at ${a.offset}:${a.length} (${a.fragment})`),n=o.map(a=>`At ${a.offset}:${a.length} - (${a.content})`);throw new Error(`VS Code configuration file (.vscode/launch.json) is not valid a JSONC after Playground CLI modifications. This is likely a Playground CLI bug. Please report it at https://github.com/WordPress/wordpress-playground/issues and include the contents of your ".vscode/launch.json" file.
|
|
21
|
-
|
|
22
|
-
Applied edits: ${n.join(`
|
|
23
|
-
`)}
|
|
24
|
-
|
|
25
|
-
The errors are: ${s.join(`
|
|
26
|
-
`)}`)}return i}function ae(e){return e.replaceAll(m.sep,m.posix.sep)}const V={Quiet:{name:"quiet",severity:h.LogSeverity.Fatal},Normal:{name:"normal",severity:h.LogSeverity.Info},Debug:{name:"debug",severity:h.LogSeverity.Debug}};async function Ye(e){try{const o=ke(e).usage("Usage: wp-playground <command> [options]").positional("command",{describe:"Command to run",choices:["server","run-blueprint","build-snapshot"],demandOption:!0}).option("outfile",{describe:"When building, write to this output file.",type:"string",default:"wordpress.zip"}).option("port",{describe:"Port to listen on when serving.",type:"number",default:9400}).option("site-url",{describe:"Site URL to use for WordPress. Defaults to http://127.0.0.1:{port}",type:"string"}).option("php",{describe:"PHP version to use.",type:"string",default:z.RecommendedPHPVersion,choices:k.SupportedPHPVersions}).option("wp",{describe:"WordPress version to use.",type:"string",default:"latest"}).option("mount",{describe:"Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path",type:"array",string:!0,coerce:W.parseMountWithDelimiterArguments}).option("mount-before-install",{describe:"Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: /host/path:/vfs/path",type:"array",string:!0,coerce:W.parseMountWithDelimiterArguments}).option("mount-dir",{describe:'Mount a directory to the PHP runtime (can be used multiple times). Format: "/host/path" "/vfs/path"',type:"array",nargs:2,array:!0,coerce:W.parseMountDirArguments}).option("mount-dir-before-install",{describe:'Mount a directory before WordPress installation (can be used multiple times). Format: "/host/path" "/vfs/path"',type:"string",nargs:2,array:!0,coerce:W.parseMountDirArguments}).option("login",{describe:"Should log the user in",type:"boolean",default:!1}).option("blueprint",{describe:"Blueprint to execute.",type:"string"}).option("blueprint-may-read-adjacent-files",{describe:'Consent flag: Allow "bundled" resources in a local blueprint to read files in the same directory as the blueprint file.',type:"boolean",default:!1}).option("wordpress-install-mode",{describe:"Control how Playground prepares WordPress before booting.",type:"string",default:"download-and-install",choices:["download-and-install","install-from-existing-files","install-from-existing-files-if-needed","do-not-attempt-installing"]}).option("skip-wordpress-install",{describe:"[Deprecated] Use --wordpress-install-mode instead.",type:"boolean",hidden:!0}).option("skip-sqlite-setup",{describe:"Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.",type:"boolean",default:!1}).option("quiet",{describe:"Do not output logs and progress messages.",type:"boolean",default:!1,hidden:!0}).option("verbosity",{describe:"Output logs and progress messages.",type:"string",choices:Object.values(V).map(r=>r.name),default:"normal"}).option("debug",{describe:"Print PHP error log content if an error occurs during Playground boot.",type:"boolean",default:!1}).option("auto-mount",{describe:"Automatically mount the specified directory. If no path is provided, 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.",type:"string"}).option("follow-symlinks",{describe:`Allow Playground to follow symlinks by automatically mounting symlinked directories and files encountered in mounted directories.
|
|
27
|
-
Warning: Following symlinks will expose files outside mounted directories to Playground and could be a security risk.`,type:"boolean",default:!1}).option("experimental-trace",{describe:"Print detailed messages about system behavior to the console. Useful for troubleshooting.",type:"boolean",default:!1,hidden:!0}).option("internal-cookie-store",{describe:"Enable internal cookie handling. When enabled, Playground will manage cookies internally using an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled externally (e.g., by a browser in Node.js environments).",type:"boolean",default:!1}).option("xdebug",{describe:"Enable Xdebug.",type:"boolean",default:!1}).option("experimental-unsafe-ide-integration",{describe:"Enable experimental IDE development tools. This option edits IDE config files to set Xdebug path mappings and web server details. CAUTION: If there are bugs, this feature may break your IDE config files. Please consider backing up your IDE configs before using this feature.",type:"string",choices:["","vscode","phpstorm"],coerce:r=>r===""?["vscode","phpstorm"]:[r]}).option("experimental-devtools",{describe:"Enable experimental browser development tools.",type:"boolean"}).conflicts("experimental-unsafe-ide-integration","experimental-devtools").option("experimental-multi-worker",{describe:"Enable experimental multi-worker support which requires a /wordpress directory backed by a real filesystem. Pass a positive number to specify the number of workers to use. Otherwise, default to the number of CPUs minus 1.",type:"number",coerce:r=>r??Y.cpus().length-1}).option("experimental-blueprints-v2-runner",{describe:"Use the experimental Blueprint V2 runner.",type:"boolean",default:!1,hidden:!0}).option("mode",{describe:"Blueprints v2 runner mode to use. This option is required when using the --experimental-blueprints-v2-runner flag with a blueprint.",type:"string",choices:["create-new-site","apply-to-existing-site"],hidden:!0}).showHelpOnFail(!1).strictOptions().check(async r=>{if(r["skip-wordpress-install"]===!0&&(r["wordpress-install-mode"]="do-not-attempt-installing",r.wordpressInstallMode="do-not-attempt-installing"),r.wp!==void 0&&!Me(r.wp))try{new URL(r.wp)}catch{throw new Error('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"')}if(r["site-url"]!==void 0&&r["site-url"]!=="")try{new URL(r["site-url"])}catch{throw new Error(`Invalid site-url "${r["site-url"]}". Please provide a valid URL (e.g., http://localhost:8080 or https://example.com)`)}if(r["auto-mount"]){let l=!1;try{l=u.statSync(r["auto-mount"]).isDirectory()}catch{l=!1}if(!l)throw new Error(`The specified --auto-mount path is not a directory: '${r["auto-mount"]}'.`)}if(r["experimental-multi-worker"]!==void 0){if(r._[0]!=="server")throw new Error("The --experimental-multi-worker flag is only supported when running the server command.");if(r["experimental-multi-worker"]<=1)throw new Error("The --experimental-multi-worker flag must be a positive integer greater than 1.")}if(r["experimental-blueprints-v2-runner"]===!0){if(r.mode!==void 0){if(r["wordpress-install-mode"]!==void 0)throw new Error("The --wordpress-install-mode option cannot be used with the --mode option. Use one or the other.");if("skip-sqlite-setup"in r)throw new Error("The --skipSqliteSetup option is not supported in Blueprint V2 mode.");if(r["auto-mount"]!==void 0)throw new Error("The --mode option cannot be used with --auto-mount because --auto-mount automatically sets the mode.")}else r["wordpress-install-mode"]==="do-not-attempt-installing"?r.mode="apply-to-existing-site":r.mode="create-new-site";const l=r.allow||[];r.followSymlinks===!0&&l.push("follow-symlinks"),r["blueprint-may-read-adjacent-files"]===!0&&l.push("read-local-fs"),r.allow=l}else if(r.mode!==void 0)throw new Error("The --mode option requires the --experimentalBlueprintsV2Runner flag.");return!0});o.wrap(o.terminalWidth());const t=await o.argv,i=t._[0];["run-blueprint","server","build-snapshot"].includes(i)||(o.showHelp(),process.exit(1));const s={...t,command:i,mount:[...t.mount||[],...t["mount-dir"]||[]],"mount-before-install":[...t["mount-before-install"]||[],...t["mount-dir-before-install"]||[]]},n=await le(s);n===void 0&&process.exit(0);const a=(()=>{let r;return async()=>{r!==void 0&&(r=n[Symbol.asyncDispose]()),await r,process.exit(0)}})();process.on("SIGINT",a),process.on("SIGTERM",a)}catch(o){if(!(o instanceof Error))throw o;if(process.argv.includes("--debug"))k.printDebugDetails(o);else{const i=[];let s=o;do i.push(s.message),s=s.cause;while(s instanceof Error);console.error("\x1B[1m"+i.join(" caused by: ")+"\x1B[0m")}process.exit(1)}}const D=e=>process.stdout.isTTY?"\x1B[1m"+e+"\x1B[0m":e,Ge=e=>process.stdout.isTTY?`\x1B[2m${e}\x1B[0m`:e,X=e=>process.stdout.isTTY?`\x1B[3m${e}\x1B[0m`:e,te=e=>process.stdout.isTTY?`\x1B[33m${e}\x1B[0m`:e;async function le(e){let o,t;const i=new Map;if(e.autoMount!==void 0&&(e.autoMount===""&&(e={...e,autoMount:process.cwd()}),e=W.expandAutoMounts(e)),e.wordpressInstallMode===void 0&&(e.wordpressInstallMode="download-and-install"),e.quiet&&(e.verbosity="quiet",delete e.quiet),e.debug?e.verbosity="debug":e.verbosity==="debug"&&(e.debug=!0),e.verbosity){const l=Object.values(V).find(d=>d.name===e.verbosity).severity;h.logger.setSeverityFilterLevel(l)}const s=Y.platform()==="win32"?void 0:await import("fs-ext").then(l=>l.flockSync).catch(()=>{h.logger.warn("The fs-ext package is not installed. Internal file locking will not be integrated with host OS file locking.")}),n=new Se.FileLockManagerForNode(s);let a=!1,r=!0;return h.logger.log("Starting a PHP server..."),Le({port:e.port,onBind:async(l,d)=>{const c="127.0.0.1",p=`http://${c}:${d}`,g=e["site-url"]||p,b=e.command==="server"?e.experimentalMultiWorker??1:1,S=e.command==="server"?b+1:b,E=Math.floor(Number.MAX_SAFE_INTEGER/S),B="-playground-cli-site-",x=await Ae(B);h.logger.debug(`Native temp dir for VFS root: ${x.path}`);const f="WP Playground CLI - Listen for Xdebug",F=".playground-xdebug-root",G=m.join(process.cwd(),F);if(await Oe(G),e.xdebug&&e.experimentalUnsafeIdeIntegration){await _e(x.path,G,process.platform);const w={hostPath:m.join(".",m.sep,F),vfsPath:"/"};try{await Ze(f,process.cwd());const y=typeof e.xdebug=="object"?e.xdebug:void 0,T=await Je({name:f,host:c,port:d,ides:e.experimentalUnsafeIdeIntegration,cwd:process.cwd(),mounts:[w,...e["mount-before-install"]||[],...e.mount||[]],ideKey:y?.ideKey}),v=e.experimentalUnsafeIdeIntegration,I=v.includes("vscode"),L=v.includes("phpstorm");console.log(""),console.log(D("Xdebug configured successfully")),console.log(te("Updated IDE config: ")+T.join(" ")),console.log(te("Playground source root: ")+".playground-xdebug-root"+X(Ge(" – you can set breakpoints and preview Playground's VFS structure in there."))),console.log(""),I&&(console.log(D("VS Code / Cursor instructions:")),console.log(" 1. Ensure you have installed an IDE extension for PHP Debugging"),console.log(` (The ${D("PHP Debug")} extension by ${D("Xdebug")} has been a solid option)`),console.log(" 2. Open the Run and Debug panel on the left sidebar"),console.log(` 3. Select "${X(f)}" from the dropdown`),console.log(' 3. Click "start debugging"'),console.log(" 5. Set a breakpoint. For example, in .playground-xdebug-root/wordpress/index.php"),console.log(" 6. Visit Playground in your browser to hit the breakpoint"),L&&console.log("")),L&&(console.log(D("PhpStorm instructions:")),console.log(` 1. Choose "${X(f)}" debug configuration in the toolbar`),console.log(" 2. Click the debug button (bug icon)`"),console.log(" 3. Set a breakpoint. For example, in .playground-xdebug-root/wordpress/index.php"),console.log(" 4. Visit Playground in your browser to hit the breakpoint")),console.log("")}catch(y){throw new Error("Could not configure Xdebug",{cause:y})}}const ce=m.dirname(x.path),de=2*24*60*60*1e3;Ue(B,de,ce);const _=m.join(x.path,"internal");u.mkdirSync(_);const ue=["wordpress","tmp","home"];for(const w of ue){const y=v=>v.vfsPath===`/${w}`;if(!(e["mount-before-install"]?.some(y)||e.mount?.some(y))){const v=m.join(x.path,w);u.mkdirSync(v),e["mount-before-install"]===void 0&&(e["mount-before-install"]=[]),e["mount-before-install"].unshift({vfsPath:`/${w}`,hostPath:v})}}if(e["mount-before-install"])for(const w of e["mount-before-install"])h.logger.debug(`Mount before WP install: ${w.vfsPath} -> ${w.hostPath}`);if(e.mount)for(const w of e.mount)h.logger.debug(`Mount after WP install: ${w.vfsPath} -> ${w.hostPath}`);let R;e["experimental-blueprints-v2-runner"]?R=new Be(e,{siteUrl:g,processIdSpaceLength:E}):(R=new We(e,{siteUrl:g,processIdSpaceLength:E}),typeof e.blueprint=="string"&&(e.blueprint=await je({sourceString:e.blueprint,blueprintMayReadAdjacentFiles:e["blueprint-may-read-adjacent-files"]===!0})));let O=!1;const A=async function(){O||(O=!0,await Promise.all([...i].map(async([y,T])=>{await T.dispose(),await y.terminate()})),l&&await new Promise(y=>l.close(y)),await x.cleanup())},pe=Ke(S,R.getWorkerType(),({exitCode:w,workerIndex:y})=>{O||w===0&&h.logger.error(`Worker ${y} exited with code ${w}
|
|
28
|
-
`)});h.logger.log("Starting up workers");try{const w=await pe,y=await oe(n);{const v=w.shift(),I=await R.bootAndSetUpInitialPlayground(v.phpPort,y,_);if(i.set(v.worker,I),await I.isReady(),a=!0,h.logger.log("Booted!"),o=new $e(I),!e["experimental-blueprints-v2-runner"]){const L=await R.compileInputBlueprint(e["additional-blueprint-steps"]||[]);L&&(h.logger.log("Running the Blueprint..."),await M.runBlueprintV1Steps(L,I),h.logger.log("Finished running the blueprint"))}if(e.command==="build-snapshot"){await et(t,e.outfile),h.logger.log(`WordPress exported to ${e.outfile}`),await A();return}else if(e.command==="run-blueprint"){h.logger.log("Blueprint executed"),await A();return}await o.removeWorker(I),await I.dispose(),await v.worker.terminate(),i.delete(v.worker)}h.logger.log("Preparing workers...");const T=E;return[t]=await Promise.all(w.map(async(v,I)=>{const L=T+I*E,fe=await oe(n),H=await R.bootPlayground({worker:v,fileLockManagerPort:fe,firstProcessId:L,nativeInternalDirPath:_});return i.set(v.worker,H),o.addWorker(H),H})),h.logger.log(`WordPress is running on ${p} with ${b} worker(s)`),e.xdebug&&e.experimentalDevtools&&(await Ee.startBridge({phpInstance:t,phpRoot:"/wordpress"})).start(),{playground:t,server:l,serverUrl:p,[Symbol.asyncDispose]:A,workerThreadCount:b}}catch(w){if(!e.debug)throw w;let y="";throw await t?.fileExists(h.errorLogPath)&&(y=await t.readFileAsText(h.errorLogPath)),await A(),new Error(y,{cause:w})}},async handleRequest(l){if(!a)return k.PHPResponse.forHttpCode(502,"WordPress is not ready yet");if(r){r=!1;const d={"Content-Type":["text/plain"],"Content-Length":["0"],Location:[l.url]};return l.headers?.cookie?.includes("playground_auto_login_already_happened")&&(d["Set-Cookie"]=["playground_auto_login_already_happened=1; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/"]),new k.PHPResponse(302,d,new Uint8Array)}return await o.handleRequest(l)}})}async function Ke(e,o,t){const i=[];for(let s=0;s<e;s++){const n=await Qe(o),a=r=>{t({exitCode:r,workerIndex:s})};i.push(new Promise((r,l)=>{n.once("message",function(d){d.command==="worker-script-initialized"&&r({worker:n,phpPort:d.phpPort})}),n.once("error",function(d){console.error(d);const c=new Error(`Worker failed to load worker. ${d.message?`Original error: ${d.message}`:""}`);l(c)}),n.once("exit",a)}))}return Promise.all(i)}async function Qe(e){return e==="v1"?new J.Worker(new URL("./worker-thread-v1.cjs",typeof document>"u"?require("url").pathToFileURL(__filename).href:$&&$.tagName.toUpperCase()==="SCRIPT"&&$.src||new URL("run-cli-BIOpeo2v.cjs",document.baseURI).href)):new J.Worker(new URL("./worker-thread-v2.cjs",typeof document>"u"?require("url").pathToFileURL(__filename).href:$&&$.tagName.toUpperCase()==="SCRIPT"&&$.src||new URL("run-cli-BIOpeo2v.cjs",document.baseURI).href))}async function oe(e){const{port1:o,port2:t}=new J.MessageChannel;return await xe.jspi()?k.exposeAPI(e,null,o):await k.exposeSyncAPI(e,o),t}async function et(e,o){await e.run({code:`<?php
|
|
29
|
-
$zip = new ZipArchive();
|
|
30
|
-
if(false === $zip->open('/tmp/build.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
|
|
31
|
-
throw new Exception('Failed to create ZIP');
|
|
32
|
-
}
|
|
33
|
-
$files = new RecursiveIteratorIterator(
|
|
34
|
-
new RecursiveDirectoryIterator('/wordpress')
|
|
35
|
-
);
|
|
36
|
-
foreach ($files as $file) {
|
|
37
|
-
echo $file . PHP_EOL;
|
|
38
|
-
if (!$file->isFile()) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
$zip->addFile($file->getPathname(), $file->getPathname());
|
|
42
|
-
}
|
|
43
|
-
$zip->close();
|
|
44
|
-
|
|
45
|
-
`});const t=await e.readFileAsBuffer("/tmp/build.zip");u.writeFileSync(o,t)}exports.LogVerbosity=V;exports.parseOptionsAndRunCLI=Ye;exports.runCLI=le;
|
|
46
|
-
//# sourceMappingURL=run-cli-BIOpeo2v.cjs.map
|
package/run-cli-BIOpeo2v.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-cli-BIOpeo2v.cjs","sources":["../../../../packages/playground/cli/src/start-server.ts","../../../../packages/playground/cli/src/load-balancer.ts","../../../../packages/playground/cli/src/is-valid-wordpress-slug.ts","../../../../packages/playground/cli/src/resolve-blueprint.ts","../../../../packages/playground/cli/src/blueprints-v2/blueprints-v2-handler.ts","../../../../packages/playground/cli/src/blueprints-v1/download.ts","../../../../packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts","../../../../packages/playground/cli/src/temp-dir.ts","../../../../packages/playground/cli/src/xdebug-path-mappings.ts","../../../../packages/playground/cli/src/run-cli.ts"],"sourcesContent":["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';\nimport { logger } from '@php-wasm/logger';\n\nexport interface ServerOptions {\n\tport: number;\n\tonBind: (server: Server, port: number) => Promise<RunCLIServer | void>;\n\thandleRequest: (request: PHPRequest) => Promise<PHPResponse>;\n}\n\nexport async function startServer(\n\toptions: ServerOptions\n): Promise<RunCLIServer | void> {\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\tlet phpResponse: PHPResponse;\n\t\ttry {\n\t\t\tphpResponse = await options.handleRequest({\n\t\t\t\turl: req.url,\n\t\t\t\theaders: parseHeaders(req),\n\t\t\t\tmethod: req.method as any,\n\t\t\t\tbody: await bufferRequestBody(req),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.error(error);\n\t\t\tphpResponse = PHPResponse.forHttpCode(500);\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 { PlaygroundCliBlueprintV1Worker as PlaygroundCliWorkerV1 } from './blueprints-v1/worker-thread-v1';\nimport type { PlaygroundCliBlueprintV2Worker as PlaygroundCliWorkerV2 } from './blueprints-v2/worker-thread-v2';\n\ntype PlaygroundCliWorker = PlaygroundCliWorkerV1 | PlaygroundCliWorkerV2;\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\tasync removeWorker(worker: RemoteAPI<PlaygroundCliWorker>) {\n\t\tconst workerIndex = this.workerLoads.findIndex(\n\t\t\t(workerLoad) => workerLoad.worker === worker\n\t\t);\n\t\tif (workerIndex === -1) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst [removedWorker] = this.workerLoads.splice(workerIndex, 1);\n\n\t\t// A worker can only be considered fully removed once all\n\t\t// its active requests have settled.\n\t\tawait Promise.allSettled(removedWorker.activeRequests);\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","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';\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 Error(\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 type { RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';\nimport { consumeAPI } from '@php-wasm/universal';\nimport type {\n\tPlaygroundCliBlueprintV2Worker,\n\tSecondaryWorkerBootArgs,\n} from './worker-thread-v2';\nimport type { MessagePort as NodeMessagePort } from 'worker_threads';\nimport type { RunCLIArgs, SpawnedWorker, WorkerType } from '../run-cli';\n\n/**\n * Boots Playground CLI workers using Blueprint version 2.\n *\n * Progress tracking, downloads, steps, and all other features are\n * implemented in PHP and orchestrated by the worker thread.\n */\nexport class BlueprintsV2Handler {\n\tprivate phpVersion: SupportedPHPVersion;\n\tprivate lastProgressMessage = '';\n\n\tprivate siteUrl: string;\n\tprivate processIdSpaceLength: number;\n\tprivate args: RunCLIArgs;\n\n\tconstructor(\n\t\targs: RunCLIArgs,\n\t\toptions: {\n\t\t\tsiteUrl: string;\n\t\t\tprocessIdSpaceLength: number;\n\t\t}\n\t) {\n\t\tthis.args = args;\n\t\tthis.siteUrl = options.siteUrl;\n\t\tthis.processIdSpaceLength = options.processIdSpaceLength;\n\t\tthis.phpVersion = args.php as SupportedPHPVersion;\n\t}\n\n\tgetWorkerType(): WorkerType {\n\t\treturn 'v2';\n\t}\n\n\tasync bootAndSetUpInitialPlayground(\n\t\tphpPort: NodeMessagePort,\n\t\tfileLockManagerPort: NodeMessagePort,\n\t\tnativeInternalDirPath: string\n\t) {\n\t\tconst playground: RemoteAPI<PlaygroundCliBlueprintV2Worker> =\n\t\t\tconsumeAPI(phpPort);\n\n\t\tawait playground.useFileLockManager(fileLockManagerPort);\n\n\t\tconst workerBootArgs = {\n\t\t\t...this.args,\n\t\t\tphpVersion: this.phpVersion,\n\t\t\tsiteUrl: this.siteUrl,\n\t\t\tfirstProcessId: 1,\n\t\t\tprocessIdSpaceLength: this.processIdSpaceLength,\n\t\t\ttrace: this.args.debug || false,\n\t\t\tblueprint: this.args.blueprint!,\n\t\t\t// We do not enable Xdebug by default for the initial worker\n\t\t\t// because we do not imagine users expect to hit breakpoints\n\t\t\t// until Playground has fully booted.\n\t\t\t// TODO: Consider supporting Xdebug for the initial worker via a dedicated flag.\n\t\t\twithXdebug: false,\n\t\t\txdebug: undefined,\n\t\t\tnativeInternalDirPath,\n\t\t};\n\n\t\tawait playground.bootAndSetUpInitialWorker(workerBootArgs);\n\t\treturn playground;\n\t}\n\n\tasync bootPlayground({\n\t\tworker,\n\t\tfileLockManagerPort,\n\t\tfirstProcessId,\n\t\tnativeInternalDirPath,\n\t}: {\n\t\tworker: SpawnedWorker;\n\t\tfileLockManagerPort: NodeMessagePort;\n\t\tfirstProcessId: number;\n\t\tnativeInternalDirPath: string;\n\t}) {\n\t\tconst playground: RemoteAPI<PlaygroundCliBlueprintV2Worker> =\n\t\t\tconsumeAPI(worker.phpPort);\n\n\t\tawait playground.useFileLockManager(fileLockManagerPort);\n\n\t\tconst workerBootArgs: SecondaryWorkerBootArgs = {\n\t\t\t...this.args,\n\t\t\tphpVersion: this.phpVersion,\n\t\t\tsiteUrl: this.siteUrl,\n\t\t\tfirstProcessId,\n\t\t\tprocessIdSpaceLength: this.processIdSpaceLength,\n\t\t\ttrace: this.args.debug || false,\n\t\t\twithXdebug: !!this.args.xdebug,\n\t\t\tnativeInternalDirPath,\n\t\t\tmountsBeforeWpInstall: this.args['mount-before-install'] || [],\n\t\t\tmountsAfterWpInstall: this.args.mount || [],\n\t\t};\n\n\t\tawait playground.bootWorker(workerBootArgs);\n\n\t\treturn playground;\n\t}\n\n\twriteProgressUpdate(\n\t\twriteStream: NodeJS.WriteStream,\n\t\tmessage: string,\n\t\tfinalUpdate: boolean\n\t) {\n\t\tif (message === this.lastProgressMessage) {\n\t\t\t// Avoid repeating the same message\n\t\t\treturn;\n\t\t}\n\t\tthis.lastProgressMessage = 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","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 { logger } from '@php-wasm/logger';\nimport { EmscriptenDownloadMonitor, ProgressTracker } from '@php-wasm/progress';\nimport { consumeAPI } from '@php-wasm/universal';\nimport type { BlueprintV1Declaration } from '@wp-playground/blueprints';\nimport {\n\tcompileBlueprintV1,\n\tisBlueprintBundle,\n\tresolveRuntimeConfiguration,\n} from '@wp-playground/blueprints';\nimport { RecommendedPHPVersion, zipDirectory } from '@wp-playground/common';\nimport fs from 'fs';\nimport path from 'path';\nimport { resolveWordPressRelease } from '@wp-playground/wordpress';\nimport {\n\tCACHE_FOLDER,\n\tcachedDownload,\n\tfetchSqliteIntegration,\n\treadAsFile,\n} from './download';\nimport type { PlaygroundCliBlueprintV1Worker } from './worker-thread-v1';\nimport type { MessagePort as NodeMessagePort } from 'worker_threads';\nimport {\n\tLogVerbosity,\n\ttype RunCLIArgs,\n\ttype SpawnedWorker,\n\ttype WorkerType,\n} from '../run-cli';\n\n/**\n * Boots Playground CLI workers using Blueprint version 1.\n *\n * Progress tracking, downloads, steps, and all other features are\n * implemented in TypeScript and orchestrated by this class.\n */\nexport class BlueprintsV1Handler {\n\tprivate lastProgressMessage = '';\n\n\tprivate siteUrl: string;\n\tprivate processIdSpaceLength: number;\n\tprivate args: RunCLIArgs;\n\n\tconstructor(\n\t\targs: RunCLIArgs,\n\t\toptions: {\n\t\t\tsiteUrl: string;\n\t\t\tprocessIdSpaceLength: number;\n\t\t}\n\t) {\n\t\tthis.args = args;\n\t\tthis.siteUrl = options.siteUrl;\n\t\tthis.processIdSpaceLength = options.processIdSpaceLength;\n\t}\n\n\tgetWorkerType(): WorkerType {\n\t\treturn 'v1';\n\t}\n\n\tasync bootAndSetUpInitialPlayground(\n\t\tphpPort: NodeMessagePort,\n\t\tfileLockManagerPort: NodeMessagePort,\n\t\tnativeInternalDirPath: string\n\t) {\n\t\tlet wpDetails: any = undefined;\n\t\tlet wordPressZip: any = undefined;\n\t\tlet preinstalledWpContentPath: string | undefined = undefined;\n\t\t// @TODO: Rename to FetchProgressMonitor. There's nothing Emscripten\n\t\t// about that class anymore.\n\t\tconst monitor = new EmscriptenDownloadMonitor();\n\t\tif (this.args.wordpressInstallMode === 'download-and-install') {\n\t\t\tlet progressReached100 = false;\n\t\t\tmonitor.addEventListener('progress', ((\n\t\t\t\te: CustomEvent<ProgressEvent & { finished: boolean }>\n\t\t\t) => {\n\t\t\t\tif (progressReached100) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// @TODO Every progress bar will want percentages. The\n\t\t\t\t// download monitor should just provide that.\n\t\t\t\tconst { loaded, total } = e.detail;\n\t\t\t\t// Use floor() so we don't report 100% until truly there.\n\t\t\t\tconst percentProgress = Math.floor(\n\t\t\t\t\tMath.min(100, (100 * loaded) / total)\n\t\t\t\t);\n\t\t\t\tprogressReached100 = percentProgress === 100;\n\n\t\t\t\tthis.writeProgressUpdate(\n\t\t\t\t\tprocess.stdout,\n\t\t\t\t\t`Downloading WordPress ${percentProgress}%...`,\n\t\t\t\t\tprogressReached100\n\t\t\t\t);\n\t\t\t}) as any);\n\n\t\t\twpDetails = await resolveWordPressRelease(this.args.wp);\n\t\t\tpreinstalledWpContentPath = path.join(\n\t\t\t\tCACHE_FOLDER,\n\t\t\t\t`prebuilt-wp-content-for-wp-${wpDetails.version}.zip`\n\t\t\t);\n\t\t\twordPressZip = 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\t\t\tlogger.log(\n\t\t\t\t`Resolved WordPress release URL: ${wpDetails?.releaseUrl}`\n\t\t\t);\n\t\t}\n\n\t\tlogger.log(`Fetching SQLite integration plugin...`);\n\n\t\tconst sqliteIntegrationPluginZip = this.args.skipSqliteSetup\n\t\t\t? undefined\n\t\t\t: await fetchSqliteIntegration(monitor);\n\n\t\tconst followSymlinks = this.args.followSymlinks === true;\n\t\tconst trace = this.args.experimentalTrace === true;\n\n\t\tconst mountsBeforeWpInstall = this.args['mount-before-install'] || [];\n\t\tconst mountsAfterWpInstall = this.args.mount || [];\n\n\t\tconst playground = consumeAPI<PlaygroundCliBlueprintV1Worker>(phpPort);\n\n\t\t// Comlink communication proxy\n\t\tawait playground.isConnected();\n\n\t\tlogger.log(`Booting WordPress...`);\n\n\t\tconst runtimeConfiguration = await resolveRuntimeConfiguration(\n\t\t\tthis.getEffectiveBlueprint()\n\t\t);\n\n\t\tawait playground.useFileLockManager(fileLockManagerPort);\n\t\tawait playground.bootAndSetUpInitialWorker({\n\t\t\tphpVersion: runtimeConfiguration.phpVersion,\n\t\t\twpVersion: runtimeConfiguration.wpVersion,\n\t\t\tsiteUrl: this.siteUrl,\n\t\t\tmountsBeforeWpInstall,\n\t\t\tmountsAfterWpInstall,\n\t\t\twordpressInstallMode:\n\t\t\t\tthis.args.wordpressInstallMode || 'download-and-install',\n\t\t\twordPressZip: wordPressZip && (await wordPressZip!.arrayBuffer()),\n\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\tawait sqliteIntegrationPluginZip?.arrayBuffer(),\n\t\t\tfirstProcessId: 0,\n\t\t\tprocessIdSpaceLength: this.processIdSpaceLength,\n\t\t\tfollowSymlinks,\n\t\t\ttrace,\n\t\t\tinternalCookieStore: this.args.internalCookieStore,\n\t\t\t// We do not enable Xdebug by default for the initial worker\n\t\t\t// because we do not imagine users expect to hit breakpoints\n\t\t\t// until Playground has fully booted.\n\t\t\t// TODO: Consider supporting Xdebug for the initial worker via a dedicated flag.\n\t\t\twithXdebug: false,\n\t\t\tnativeInternalDirPath,\n\t\t});\n\n\t\tif (\n\t\t\tpreinstalledWpContentPath &&\n\t\t\t!this.args['mount-before-install'] &&\n\t\t\t!fs.existsSync(preinstalledWpContentPath)\n\t\t) {\n\t\t\tlogger.log(`Caching preinstalled WordPress for the next boot...`);\n\t\t\tfs.writeFileSync(\n\t\t\t\tpreinstalledWpContentPath,\n\t\t\t\t(await zipDirectory(playground, '/wordpress'))!\n\t\t\t);\n\t\t\tlogger.log(`Cached!`);\n\t\t}\n\n\t\treturn playground;\n\t}\n\n\tasync bootPlayground({\n\t\tworker,\n\t\tfileLockManagerPort,\n\t\tfirstProcessId,\n\t\tnativeInternalDirPath,\n\t}: {\n\t\tworker: SpawnedWorker;\n\t\tfileLockManagerPort: NodeMessagePort;\n\t\tfirstProcessId: number;\n\t\tnativeInternalDirPath: string;\n\t}) {\n\t\tconst playground = consumeAPI<PlaygroundCliBlueprintV1Worker>(\n\t\t\tworker.phpPort\n\t\t);\n\n\t\tawait playground.isConnected();\n\t\tconst runtimeConfiguration = await resolveRuntimeConfiguration(\n\t\t\tthis.getEffectiveBlueprint()\n\t\t);\n\t\tawait playground.useFileLockManager(fileLockManagerPort);\n\t\tawait playground.bootWorker({\n\t\t\tphpVersion: runtimeConfiguration.phpVersion,\n\t\t\tsiteUrl: this.siteUrl,\n\t\t\tmountsBeforeWpInstall: this.args['mount-before-install'] || [],\n\t\t\tmountsAfterWpInstall: this.args['mount'] || [],\n\t\t\tfirstProcessId,\n\t\t\tprocessIdSpaceLength: this.processIdSpaceLength,\n\t\t\tfollowSymlinks: this.args.followSymlinks === true,\n\t\t\ttrace: this.args.experimentalTrace === true,\n\t\t\t// @TODO: Move this to the request handler or else every worker\n\t\t\t// will have a separate cookie store.\n\t\t\tinternalCookieStore: this.args.internalCookieStore,\n\t\t\twithXdebug: !!this.args.xdebug,\n\t\t\tnativeInternalDirPath,\n\t\t});\n\t\tawait playground.isReady();\n\t\treturn playground;\n\t}\n\n\tasync compileInputBlueprint(additionalBlueprintSteps: any[]) {\n\t\tconst blueprint = this.getEffectiveBlueprint();\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\tthis.writeProgressUpdate(\n\t\t\t\tprocess.stdout,\n\t\t\t\tmessage,\n\t\t\t\tprogressReached100\n\t\t\t);\n\t\t});\n\t\treturn await compileBlueprintV1(blueprint as BlueprintV1Declaration, {\n\t\t\tprogress: tracker,\n\t\t\tadditionalSteps: additionalBlueprintSteps,\n\t\t});\n\t}\n\n\tprivate getEffectiveBlueprint() {\n\t\tconst resolvedBlueprint = this.args.blueprint as BlueprintV1Declaration;\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\treturn isBlueprintBundle(resolvedBlueprint)\n\t\t\t? resolvedBlueprint\n\t\t\t: {\n\t\t\t\t\tlogin: this.args.login,\n\t\t\t\t\t...(resolvedBlueprint || {}),\n\t\t\t\t\tpreferredVersions: {\n\t\t\t\t\t\tphp:\n\t\t\t\t\t\t\tthis.args.php ??\n\t\t\t\t\t\t\tresolvedBlueprint?.preferredVersions?.php ??\n\t\t\t\t\t\t\tRecommendedPHPVersion,\n\t\t\t\t\t\twp:\n\t\t\t\t\t\t\tthis.args.wp ??\n\t\t\t\t\t\t\tresolvedBlueprint?.preferredVersions?.wp ??\n\t\t\t\t\t\t\t'latest',\n\t\t\t\t\t\t...(resolvedBlueprint?.preferredVersions || {}),\n\t\t\t\t\t},\n\t\t\t };\n\t}\n\n\twriteProgressUpdate(\n\t\twriteStream: NodeJS.WriteStream,\n\t\tmessage: string,\n\t\tfinalUpdate: boolean\n\t) {\n\t\tif (this.args.verbosity === LogVerbosity.Quiet.name) {\n\t\t\treturn;\n\t\t}\n\t\tif (message === this.lastProgressMessage) {\n\t\t\t// Avoid repeating the same message\n\t\t\treturn;\n\t\t}\n\t\tthis.lastProgressMessage = 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","import fs from 'fs';\nimport path from 'path';\nimport { logger } from '@php-wasm/logger';\nimport {\n\tdir as tmpDir,\n\tsetGracefulCleanup as tmpSetGracefulCleanup,\n} from 'tmp-promise';\n// NOTE: We use ps-man rather than more popular packages because there\n// is no native build required to install the package.\n// @ts-ignore -- There are no types for this package.\nimport ps from 'ps-man';\n\n/**\n * Create a temp dir for the Playground CLI.\n *\n * The temp dir is created in the system temp dir and is named\n * based on the Playground CLI binary name and the process ID.\n *\n * @param substrToIdentifyTempDirs The substring to identify the temp dir.\n * @param autoCleanup Whether to skip cleanup on process exit. Primarily used for unit testing.\n * @returns The path to the temp dir.\n */\nexport async function createPlaygroundCliTempDir(\n\tsubstrToIdentifyTempDirs: string,\n\t// Allow controlling auto-cleanup for test purposes.\n\tautoCleanup = true\n) {\n\tconst nodeBinaryName = path.basename(process.argv0);\n\n\t// We place the binary name before the playground-related fragment\n\t// so we can use the position of the fragment to parse the binary name.\n\t// Otherwise, we would have to parse the binary name from the full path.\n\tconst tempDirPrefix = `${nodeBinaryName}${substrToIdentifyTempDirs}${process.pid}-`;\n\n\tconst nativeDir = await tmpDir({\n\t\tprefix: tempDirPrefix,\n\t\t/*\n\t\t * Allow recursive cleanup on process exit.\n\t\t *\n\t\t * NOTE: I worried about whether this cleanup would follow symlinks\n\t\t * and delete target files instead of unlinking the symlink,\n\t\t * but this feature uses rimraf under the hood which respects symlinks:\n\t\t * https://github.com/raszi/node-tmp/blob/3d2fe387f3f91b13830b9182faa02c3231ea8258/lib/tmp.js#L318\n\t\t */\n\t\tunsafeCleanup: true,\n\t});\n\n\tif (autoCleanup) {\n\t\t// Request graceful cleanup on process exit.\n\t\ttmpSetGracefulCleanup();\n\t}\n\n\treturn nativeDir;\n}\n\n/**\n * Cleanup stale Playground temp dirs.\n *\n * A temp dir is considered stale if it is older than the specified age\n * and the associated process no longer exists.\n *\n * @param substrToIdentifyTempDirs The substring to identify the temp dir.\n * @param staleAgeInMillis The age in milliseconds after which a temp dir is considered stale.\n * @param tempRootDir The root directory of the temp dirs.\n */\nexport async function cleanupStalePlaygroundTempDirs(\n\tsubstrToIdentifyTempDirs: string,\n\tstaleAgeInMillis: number,\n\ttempRootDir: string\n) {\n\tconst stalePlaygroundTempDirs = await findStalePlaygroundTempDirs(\n\t\tsubstrToIdentifyTempDirs,\n\t\tstaleAgeInMillis,\n\t\ttempRootDir\n\t);\n\tconst promisesToRemove = stalePlaygroundTempDirs.map(\n\t\t(stalePlaygroundTempDir) =>\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t// TODO: Non-blocking: Consider how to avoid conflicts with another CLI doing cleanup.\n\t\t\t\tfs.rm(stalePlaygroundTempDir, { recursive: true }, (err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`Failed to delete stale Playground temp dir: ${stalePlaygroundTempDir}`,\n\t\t\t\t\t\t\terr\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t`Deleted stale Playground temp dir: ${stalePlaygroundTempDir}`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t})\n\t);\n\tawait Promise.all(promisesToRemove);\n}\n\nasync function findStalePlaygroundTempDirs(\n\tsubstrToIdentifyTempDirs: string,\n\tstaleAgeInMillis: number,\n\ttempRootDir: string\n) {\n\ttry {\n\t\tconst tempPaths = fs\n\t\t\t.readdirSync(tempRootDir)\n\t\t\t.map((dirName) => path.join(tempRootDir, dirName));\n\n\t\tconst stalePlaygroundTempDirs = [];\n\t\tfor (const tempPath of tempPaths) {\n\t\t\tconst appearsToBeStale = await appearsToBeStalePlaygroundTempDir(\n\t\t\t\tsubstrToIdentifyTempDirs,\n\t\t\t\tstaleAgeInMillis,\n\t\t\t\ttempPath\n\t\t\t);\n\t\t\tif (appearsToBeStale) {\n\t\t\t\tstalePlaygroundTempDirs.push(tempPath);\n\t\t\t}\n\t\t}\n\t\treturn stalePlaygroundTempDirs;\n\t} catch (e) {\n\t\tlogger.warn(`Failed to find stale Playground temp dirs: ${e}`);\n\t\t// Failing to find stale temp dirs should not prevent the CLI from starting.\n\t\treturn [];\n\t}\n}\n\nasync function appearsToBeStalePlaygroundTempDir(\n\tsubstrToIdentifyTempDirs: string,\n\tstaleAgeInMillis: number,\n\tabsolutePath: string\n) {\n\tconst lstat = fs.lstatSync(absolutePath);\n\tif (!lstat.isDirectory()) {\n\t\t// A non-directory cannot be a Playground temp dir.\n\t\treturn false;\n\t}\n\n\tconst dirName = path.basename(absolutePath);\n\tif (!dirName.includes(substrToIdentifyTempDirs)) {\n\t\t// This doesn't look like one of our temp dirs.\n\t\treturn false;\n\t}\n\n\tconst match = dirName.match(\n\t\tnew RegExp(`^(.+)${substrToIdentifyTempDirs}(\\\\d+)-`)\n\t);\n\tif (!match) {\n\t\t// We cannot parse the temp dir name,\n\t\t// so there is nothing more to try.\n\t\treturn false;\n\t}\n\n\tconst info = {\n\t\tabsolutePath,\n\t\texecutableName: match[1],\n\t\tpid: match[2],\n\t};\n\n\tif (await doesProcessExist(info.pid, info.executableName)) {\n\t\t// It looks like the temp dir's process is still running.\n\t\treturn false;\n\t}\n\n\tconst STALE_DATE = Date.now() - staleAgeInMillis;\n\tconst dirStat = fs.statSync(absolutePath);\n\tif (dirStat.mtime.getTime() < STALE_DATE) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nasync function doesProcessExist(pid: string, executableName: string) {\n\t// Define this type because there are no types for ps.list()\n\ttype ProcessInfo = {\n\t\tpid: string;\n\t\tcommand: string;\n\t};\n\t// Look for an existing process with the same PID and executable name.\n\tconst [existingProcess] = await new Promise<ProcessInfo[]>(\n\t\t(resolve, reject) => {\n\t\t\tps.list(\n\t\t\t\t{\n\t\t\t\t\tpid,\n\t\t\t\t\tname: executableName,\n\t\t\t\t\t// Remove path from executable name in the results.\n\t\t\t\t\tclean: true,\n\t\t\t\t},\n\t\t\t\t(err: any, processes: ProcessInfo[]) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolve(processes);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\treturn (\n\t\t!!existingProcess &&\n\t\texistingProcess.pid === pid &&\n\t\texistingProcess.command === executableName\n\t);\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { type Mount } from './mounts';\nimport {\n\ttype X2jOptions,\n\ttype XmlBuilderOptions,\n\tXMLParser,\n\tXMLBuilder,\n} from 'fast-xml-parser';\nimport JSONC from 'jsonc-parser';\n\n/**\n * Create a symlink to temp dir for the Playground CLI.\n *\n * The symlink is created to access the system temp dir\n * inside the current debugging directory.\n *\n * @param nativeDirPath The system temp dir path.\n * @param symlinkPath The symlink name.\n */\nexport async function createPlaygroundCliTempDirSymlink(\n\tnativeDirPath: string,\n\tsymlinkPath: string,\n\tplatform: string\n) {\n\tconst type =\n\t\tplatform === 'win32'\n\t\t\t? // On Windows, creating a 'dir' symlink can require elevated permissions.\n\t\t\t // In this case, let's make junction points because they function like\n\t\t\t // symlinks and do not require elevated permissions.\n\t\t\t 'junction'\n\t\t\t: 'dir';\n\tfs.symlinkSync(nativeDirPath, symlinkPath, type);\n}\n\n/**\n * Remove the temp dir symlink if it exists.\n *\n * @param symlinkPath The symlink path.\n */\nexport async function removePlaygroundCliTempDirSymlink(symlinkPath: string) {\n\ttry {\n\t\tconst stats = fs.lstatSync(symlinkPath);\n\t\tif (stats.isSymbolicLink()) {\n\t\t\tfs.unlinkSync(symlinkPath);\n\t\t}\n\t} catch {\n\t\t// Symlink does not exist or cannot be accessed, nothing to remove\n\t}\n}\n\n/**\n * Filters out mounts that are not in the current working directory\n *\n * @param mounts The Playground CLI mount options.\n */\nfunction filterLocalMounts(cwd: string, mounts: Mount[]) {\n\treturn mounts.filter((mount) => {\n\t\tconst absoluteHostPath = path.resolve(mount.hostPath);\n\t\tconst cwdChildPrefix = path.join(cwd, path.sep);\n\t\treturn (\n\t\t\t// If auto-mounting from the current directory,\n\t\t\t// the entire project directory can be mapped.\n\t\t\tabsoluteHostPath === cwd ||\n\t\t\tabsoluteHostPath.startsWith(cwdChildPrefix)\n\t\t);\n\t});\n}\n\nexport type IDEConfig = {\n\t/**\n\t * The name of the configuration within the IDE configuration.\n\t */\n\tname: string;\n\t/**\n\t * The IDEs to configure.\n\t */\n\tides: string[];\n\t/**\n\t * The web server host.\n\t */\n\thost: string;\n\t/**\n\t * The web server port.\n\t */\n\tport: number;\n\t/**\n\t * The current working directory to consider for debugger path mapping.\n\t */\n\tcwd: string;\n\t/**\n\t * The mounts to consider for debugger path mapping.\n\t */\n\tmounts: Mount[];\n\t/**\n\t * The IDE key to use for the debug configuration. Defaults to 'PLAYGROUNDCLI'.\n\t */\n\tideKey?: string;\n};\n\ntype PhpStormConfigMetaData = {\n\tname?: string;\n\tversion?: string;\n\thost?: string;\n\tuse_path_mappings?: string;\n\t'local-root'?: string;\n\t'remote-root'?: string;\n\t/**\n\t * The type of the server.\n\t */\n\ttype?: 'PhpRemoteDebugRunConfigurationType';\n\tfactoryName?: string;\n\tfilter_connections?: 'FILTER';\n\tserver_name?: string;\n\tsession_id?: string;\n\tv?: string;\n};\n\ntype PhpStormConfigNode = {\n\t':@'?: PhpStormConfigMetaData;\n\tproject?: PhpStormConfigNode[];\n\tcomponent?: PhpStormConfigNode[];\n\tservers?: PhpStormConfigNode[];\n\tserver?: PhpStormConfigNode[];\n\tpath_mappings?: PhpStormConfigNode[];\n\tmapping?: PhpStormConfigNode[];\n\tconfiguration?: PhpStormConfigNode[];\n\tmethod?: PhpStormConfigNode[];\n};\n\ntype VSCodeConfigMetaData = {\n\t[key: string]: string;\n};\n\ntype VSCodeConfigNode = {\n\tname: string;\n\ttype: string;\n\trequest: string;\n\tport: number;\n\tpathMappings: VSCodeConfigMetaData;\n};\n\nconst xmlParserOptions: X2jOptions = {\n\tignoreAttributes: false,\n\tattributeNamePrefix: '',\n\tpreserveOrder: true,\n\tcdataPropName: '__cdata',\n\tcommentPropName: '__xmlComment',\n\tallowBooleanAttributes: true,\n\ttrimValues: true,\n};\nconst xmlBuilderOptions: XmlBuilderOptions = {\n\tignoreAttributes: xmlParserOptions.ignoreAttributes,\n\tattributeNamePrefix: xmlParserOptions.attributeNamePrefix,\n\tpreserveOrder: xmlParserOptions.preserveOrder,\n\tcdataPropName: xmlParserOptions.cdataPropName,\n\tcommentPropName: xmlParserOptions.commentPropName,\n\tsuppressBooleanAttributes: !xmlParserOptions.allowBooleanAttributes,\n\tformat: true,\n\tindentBy: '\\t',\n};\n\nconst jsoncParseOptions: JSONC.ParseOptions = {\n\tallowEmptyContent: true,\n\tallowTrailingComma: true,\n};\n\nexport type PhpStormConfigOptions = {\n\tname: string;\n\thost: string;\n\tport: number;\n\tprojectDir: string;\n\tmappings: Mount[];\n\tideKey: string;\n};\n\n/**\n * Pure function to update PHPStorm XML config with XDebug server and run configuration.\n *\n * @param xmlContent The original XML content of workspace.xml\n * @param options Configuration options for the server\n * @returns Updated XML content\n * @throws Error if XML is invalid or configuration is incompatible\n */\nexport function updatePhpStormConfig(\n\txmlContent: string,\n\toptions: PhpStormConfigOptions\n): string {\n\tconst { name, host, port, mappings, ideKey } = options;\n\n\tconst xmlParser = new XMLParser(xmlParserOptions);\n\n\t// Parse the XML\n\tconst config: PhpStormConfigNode[] = (() => {\n\t\ttry {\n\t\t\treturn xmlParser.parse(xmlContent, true);\n\t\t} catch {\n\t\t\tthrow new Error('PhpStorm configuration file is not valid XML.');\n\t\t}\n\t})();\n\n\t// Create the server element with path mappings\n\tconst serverElement: PhpStormConfigNode = {\n\t\tserver: [\n\t\t\t{\n\t\t\t\tpath_mappings: mappings.map((mapping) => ({\n\t\t\t\t\tmapping: [],\n\t\t\t\t\t':@': {\n\t\t\t\t\t\t'local-root': `$PROJECT_DIR$/${toPosixPath(\n\t\t\t\t\t\t\tpath.relative(options.projectDir, mapping.hostPath)\n\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t'remote-root': mapping.vfsPath,\n\t\t\t\t\t},\n\t\t\t\t})),\n\t\t\t},\n\t\t],\n\t\t':@': {\n\t\t\tname,\n\t\t\t// NOTE: PhpStorm quirk: Xdebug only works when the full URL (including port)\n\t\t\t// is provided in `host`. The separate `port` field is ignored or misinterpreted,\n\t\t\t// so we rely solely on host: \"host:port\".\n\t\t\thost: `${host}:${port}`,\n\t\t\tuse_path_mappings: 'true',\n\t\t},\n\t};\n\n\t// Find or create project element\n\tlet projectElement = config?.find((c: PhpStormConfigNode) => !!c?.project);\n\tif (projectElement) {\n\t\tconst projectVersion = projectElement[':@']?.version;\n\t\tif (projectVersion === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t'PhpStorm IDE integration only supports <project version=\"4\"> in workspace.xml, ' +\n\t\t\t\t\t'but the <project> configuration has no version number.'\n\t\t\t);\n\t\t} else if (projectVersion !== '4') {\n\t\t\tthrow new Error(\n\t\t\t\t'PhpStorm IDE integration only supports <project version=\"4\"> in workspace.xml, ' +\n\t\t\t\t\t`but we found a <project> configuration with version \"${projectVersion}\".`\n\t\t\t);\n\t\t}\n\t}\n\tif (projectElement === undefined) {\n\t\tprojectElement = {\n\t\t\tproject: [],\n\t\t\t':@': { version: '4' },\n\t\t};\n\t\tconfig.push(projectElement);\n\t}\n\n\t// Find or create PhpServers component\n\tlet componentElement = projectElement.project?.find(\n\t\t(c: PhpStormConfigNode) =>\n\t\t\t!!c?.component && c?.[':@']?.name === 'PhpServers'\n\t);\n\tif (componentElement === undefined) {\n\t\tcomponentElement = {\n\t\t\tcomponent: [],\n\t\t\t':@': { name: 'PhpServers' },\n\t\t};\n\n\t\tif (projectElement.project === undefined) {\n\t\t\tprojectElement.project = [];\n\t\t}\n\n\t\tprojectElement.project.push(componentElement);\n\t}\n\n\t// Find or create servers element\n\tlet serversElement = componentElement.component?.find(\n\t\t(c: PhpStormConfigNode) => !!c?.servers\n\t);\n\tif (serversElement === undefined) {\n\t\tserversElement = { servers: [] };\n\n\t\tif (componentElement.component === undefined) {\n\t\t\tcomponentElement.component = [];\n\t\t}\n\n\t\tcomponentElement.component.push(serversElement);\n\t}\n\n\t// Check if server already exists\n\tconst serverElementIndex = serversElement.servers?.findIndex(\n\t\t(c: PhpStormConfigNode) => !!c?.server && c?.[':@']?.name === name\n\t);\n\n\t// Only add server if it doesn't exist\n\tif (serverElementIndex === undefined || serverElementIndex < 0) {\n\t\tif (serversElement.servers === undefined) {\n\t\t\tserversElement.servers = [];\n\t\t}\n\n\t\tserversElement.servers.push(serverElement);\n\t}\n\n\t// Find or create RunManager component\n\tlet runManagerElement = projectElement.project?.find(\n\t\t(c: PhpStormConfigNode) =>\n\t\t\t!!c?.component && c?.[':@']?.name === 'RunManager'\n\t);\n\tif (runManagerElement === undefined) {\n\t\trunManagerElement = {\n\t\t\tcomponent: [],\n\t\t\t':@': { name: 'RunManager' },\n\t\t};\n\n\t\tif (projectElement.project === undefined) {\n\t\t\tprojectElement.project = [];\n\t\t}\n\n\t\tprojectElement.project.push(runManagerElement);\n\t}\n\n\t// Check if run configuration already exists\n\tconst existingConfigIndex =\n\t\trunManagerElement.component?.findIndex(\n\t\t\t(c: PhpStormConfigNode) =>\n\t\t\t\t!!c?.configuration && c?.[':@']?.name === name\n\t\t) ?? -1;\n\n\t// Only add run configuration if it doesn't exist\n\tif (existingConfigIndex < 0) {\n\t\tconst runConfigElement: PhpStormConfigNode = {\n\t\t\tconfiguration: [\n\t\t\t\t{\n\t\t\t\t\tmethod: [],\n\t\t\t\t\t':@': { v: '2' },\n\t\t\t\t},\n\t\t\t],\n\t\t\t':@': {\n\t\t\t\tname: name,\n\t\t\t\ttype: 'PhpRemoteDebugRunConfigurationType',\n\t\t\t\tfactoryName: 'PHP Remote Debug',\n\t\t\t\tfilter_connections: 'FILTER',\n\t\t\t\tserver_name: name,\n\t\t\t\tsession_id: ideKey,\n\t\t\t},\n\t\t};\n\n\t\tif (runManagerElement.component === undefined) {\n\t\t\trunManagerElement.component = [];\n\t\t}\n\n\t\trunManagerElement.component.push(runConfigElement);\n\t}\n\n\t// Build the updated XML\n\tconst xmlBuilder = new XMLBuilder(xmlBuilderOptions);\n\tconst xml = xmlBuilder.build(config);\n\n\t// Validate the generated XML\n\ttry {\n\t\txmlParser.parse(xml, true);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'The resulting PhpStorm configuration file is not valid XML.'\n\t\t);\n\t}\n\n\treturn xml;\n}\n\nexport type VSCodeConfigOptions = {\n\tname: string;\n\tworkspaceDir: string;\n\tmappings: Mount[];\n};\n\n/**\n * Pure function to update VS Code launch.json config with XDebug configuration.\n *\n * @param jsonContent The original JSON content of launch.json\n * @param options Configuration options\n * @returns Updated JSON content\n * @throws Error if JSON is invalid\n */\nexport function updateVSCodeConfig(\n\tjsonContent: string,\n\toptions: VSCodeConfigOptions\n): string {\n\tconst { name, mappings } = options;\n\n\tconst errors: JSONC.ParseError[] = [];\n\n\tlet content = jsonContent;\n\tlet root = JSONC.parseTree(content, errors, jsoncParseOptions);\n\n\tif (root === undefined || errors.length) {\n\t\tthrow new Error('VS Code configuration file is not valid JSON.');\n\t}\n\n\t// Find or create configurations array\n\tlet configurationsNode = JSONC.findNodeAtLocation(root, ['configurations']);\n\n\tif (\n\t\tconfigurationsNode === undefined ||\n\t\tconfigurationsNode.children === undefined\n\t) {\n\t\tconst edits = JSONC.modify(content, ['configurations'], [], {});\n\t\tcontent = JSONC.applyEdits(content, edits);\n\n\t\troot = JSONC.parseTree(content, [], jsoncParseOptions);\n\t\tconfigurationsNode = JSONC.findNodeAtLocation(root!, [\n\t\t\t'configurations',\n\t\t]);\n\t}\n\n\t// Check if configuration already exists\n\tconst configurationIndex = configurationsNode?.children?.findIndex(\n\t\t(child) => JSONC.findNodeAtLocation(child, ['name'])?.value === name\n\t);\n\n\t// Only add configuration if it doesn't exist\n\tif (configurationIndex === undefined || configurationIndex < 0) {\n\t\tconst configuration: VSCodeConfigNode = {\n\t\t\tname: name,\n\t\t\ttype: 'php',\n\t\t\trequest: 'launch',\n\t\t\tport: 9003,\n\t\t\tpathMappings: mappings.reduce((acc, mount) => {\n\t\t\t\tacc[mount.vfsPath] = `\\${workspaceFolder}/${toPosixPath(\n\t\t\t\t\tpath.relative(options.workspaceDir, mount.hostPath)\n\t\t\t\t)}`;\n\t\t\t\treturn acc;\n\t\t\t}, {} as VSCodeConfigMetaData),\n\t\t};\n\n\t\t// Get the current length to append at the end\n\t\tconst currentLength = configurationsNode?.children?.length || 0;\n\n\t\tconst edits = JSONC.modify(\n\t\t\tcontent,\n\t\t\t['configurations', currentLength],\n\t\t\tconfiguration,\n\t\t\t{\n\t\t\t\tformattingOptions: {\n\t\t\t\t\tinsertSpaces: true,\n\t\t\t\t\ttabSize: 4,\n\t\t\t\t\teol: '\\n',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\tcontent = jsoncApplyEdits(content, edits);\n\t}\n\n\treturn content;\n}\n\n/**\n * Implement necessary parameters and path mappings in IDE configuration files.\n *\n * @param name The configuration name.\n * @param mounts The Playground CLI mount options.\n */\nexport async function addXdebugIDEConfig({\n\tname,\n\tides,\n\thost,\n\tport,\n\tcwd,\n\tmounts,\n\tideKey = 'PLAYGROUNDCLI',\n}: IDEConfig) {\n\tconst mappings = filterLocalMounts(cwd, mounts);\n\tconst modifiedConfig: string[] = [];\n\n\t// PHPstorm\n\tif (ides.includes('phpstorm')) {\n\t\tconst phpStormRelativeConfigFilePath = '.idea/workspace.xml';\n\t\tconst phpStormConfigFilePath = path.join(\n\t\t\tcwd,\n\t\t\tphpStormRelativeConfigFilePath\n\t\t);\n\n\t\t// Create a template config file if the IDE directory exists,\n\t\t// or throw an error if IDE integration is requested but the directory is missing.\n\t\tif (!fs.existsSync(phpStormConfigFilePath)) {\n\t\t\tif (fs.existsSync(path.dirname(phpStormConfigFilePath))) {\n\t\t\t\tfs.writeFileSync(\n\t\t\t\t\tphpStormConfigFilePath,\n\t\t\t\t\t'<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<project version=\"4\">\\n</project>'\n\t\t\t\t);\n\t\t\t} else if (ides.length == 1) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`PhpStorm IDE integration requested, but no '.idea' directory was found in the current working directory.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (fs.existsSync(phpStormConfigFilePath)) {\n\t\t\tconst contents = fs.readFileSync(phpStormConfigFilePath, 'utf8');\n\t\t\tconst updatedXml = updatePhpStormConfig(contents, {\n\t\t\t\tname,\n\t\t\t\thost,\n\t\t\t\tport,\n\t\t\t\tprojectDir: cwd,\n\t\t\t\tmappings,\n\t\t\t\tideKey,\n\t\t\t});\n\t\t\tfs.writeFileSync(phpStormConfigFilePath, updatedXml);\n\t\t}\n\n\t\tmodifiedConfig.push(phpStormRelativeConfigFilePath);\n\t}\n\n\t// VSCode\n\tif (ides.includes('vscode')) {\n\t\tconst vsCodeRelativeConfigFilePath = '.vscode/launch.json';\n\t\tconst vsCodeConfigFilePath = path.join(\n\t\t\tcwd,\n\t\t\tvsCodeRelativeConfigFilePath\n\t\t);\n\n\t\t// Create a template config file if the IDE directory exists,\n\t\t// or throw an error if IDE integration is requested but the directory is missing.\n\t\tif (!fs.existsSync(vsCodeConfigFilePath)) {\n\t\t\tif (fs.existsSync(path.dirname(vsCodeConfigFilePath))) {\n\t\t\t\tfs.writeFileSync(\n\t\t\t\t\tvsCodeConfigFilePath,\n\t\t\t\t\t'{\\n \"configurations\": []\\n}'\n\t\t\t\t);\n\t\t\t} else if (ides.length == 1) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`VS Code IDE integration requested, but no '.vscode' directory was found in the current working directory.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (fs.existsSync(vsCodeConfigFilePath)) {\n\t\t\tconst content = fs.readFileSync(vsCodeConfigFilePath, 'utf-8');\n\t\t\tconst updatedJson = updateVSCodeConfig(content, {\n\t\t\t\tname,\n\t\t\t\tworkspaceDir: cwd,\n\t\t\t\tmappings,\n\t\t\t});\n\n\t\t\t// Only write and track the file if changes were made\n\t\t\tif (updatedJson !== content) {\n\t\t\t\tfs.writeFileSync(vsCodeConfigFilePath, updatedJson);\n\t\t\t\tmodifiedConfig.push(vsCodeRelativeConfigFilePath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn modifiedConfig;\n}\n\n/**\n * Remove stale parameters and path mappings in IDE configuration files.\n *\n * @param name The configuration name.\n * @param cwd The current working directory.\n */\nexport async function clearXdebugIDEConfig(name: string, cwd: string) {\n\tconst phpStormConfigFilePath = path.join(cwd, '.idea/workspace.xml');\n\t// PhpStorm\n\tif (fs.existsSync(phpStormConfigFilePath)) {\n\t\tconst contents = fs.readFileSync(phpStormConfigFilePath, 'utf8');\n\t\tconst xmlParser = new XMLParser(xmlParserOptions);\n\t\t// NOTE: Using an IIFE so `config` can remain const.\n\t\tconst config: PhpStormConfigNode[] = (() => {\n\t\t\ttry {\n\t\t\t\treturn xmlParser.parse(contents, true);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'PhpStorm configuration file is not valid XML.'\n\t\t\t\t);\n\t\t\t}\n\t\t})();\n\n\t\tconst projectElement = config.find(\n\t\t\t(c: PhpStormConfigNode) => !!c?.project\n\t\t);\n\t\tconst componentElement = projectElement?.project?.find(\n\t\t\t(c: PhpStormConfigNode) =>\n\t\t\t\t!!c?.component && c?.[':@']?.name === 'PhpServers'\n\t\t);\n\t\tconst serversElement = componentElement?.component?.find(\n\t\t\t(c: PhpStormConfigNode) => !!c?.servers\n\t\t);\n\t\tconst serverElementIndex = serversElement?.servers?.findIndex(\n\t\t\t(c: PhpStormConfigNode) => !!c?.server && c?.[':@']?.name === name\n\t\t);\n\n\t\tif (serverElementIndex !== undefined && serverElementIndex >= 0) {\n\t\t\tserversElement!.servers!.splice(serverElementIndex, 1);\n\n\t\t\tconst xmlBuilder = new XMLBuilder(xmlBuilderOptions);\n\t\t\tconst xml = xmlBuilder.build(config);\n\n\t\t\ttry {\n\t\t\t\txmlParser.parse(xml, true);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'The resulting PhpStorm configuration file is not valid XML.'\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\txml ===\n\t\t\t\t'<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<project version=\"4\">\\n\t<component name=\"PhpServers\">\\n\t\t<servers></servers>\\n\t</component>\\n</project>'\n\t\t\t) {\n\t\t\t\tfs.unlinkSync(phpStormConfigFilePath);\n\t\t\t} else {\n\t\t\t\tfs.writeFileSync(phpStormConfigFilePath, xml);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst vsCodeConfigFilePath = path.join(cwd, '.vscode/launch.json');\n\t// VSCode\n\tif (fs.existsSync(vsCodeConfigFilePath)) {\n\t\tconst errors: JSONC.ParseError[] = [];\n\n\t\tconst content = fs.readFileSync(vsCodeConfigFilePath, 'utf-8');\n\t\tconst root = JSONC.parseTree(content, errors, jsoncParseOptions);\n\n\t\tif (root === undefined || errors.length) {\n\t\t\tthrow new Error('VS Code configuration file is not valid JSON.');\n\t\t}\n\n\t\tconst configurationsNode = JSONC.findNodeAtLocation(root, [\n\t\t\t'configurations',\n\t\t]);\n\n\t\tconst configurationIndex = configurationsNode?.children?.findIndex(\n\t\t\t(child) => JSONC.findNodeAtLocation(child, ['name'])?.value === name\n\t\t);\n\n\t\tif (configurationIndex !== undefined && configurationIndex >= 0) {\n\t\t\tconst edits = JSONC.modify(\n\t\t\t\tcontent,\n\t\t\t\t['configurations', configurationIndex],\n\t\t\t\tundefined,\n\t\t\t\t{\n\t\t\t\t\tformattingOptions: {\n\t\t\t\t\t\tinsertSpaces: true,\n\t\t\t\t\t\ttabSize: 4,\n\t\t\t\t\t\teol: '\\n',\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tconst json = jsoncApplyEdits(content, edits);\n\t\t\tif (json === '{\\n \"configurations\": []\\n}') {\n\t\t\t\tfs.unlinkSync(vsCodeConfigFilePath);\n\t\t\t} else {\n\t\t\t\tfs.writeFileSync(vsCodeConfigFilePath, json);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction jsoncApplyEdits(content: string, edits: JSONC.Edit[]) {\n\tconst errors: JSONC.ParseError[] = [];\n\tconst json = JSONC.applyEdits(content, edits);\n\n\terrors.length = 0;\n\n\tJSONC.parseTree(json, errors, jsoncParseOptions);\n\n\tif (errors.length) {\n\t\tconst formattedErrors = errors\n\t\t\t.map((error) => {\n\t\t\t\treturn {\n\t\t\t\t\tmessage: JSONC.printParseErrorCode(error.error),\n\t\t\t\t\toffset: error.offset,\n\t\t\t\t\tlength: error.length,\n\t\t\t\t\tfragment: json.slice(\n\t\t\t\t\t\tMath.max(0, error.offset - 20),\n\t\t\t\t\t\tMath.min(json.length, error.offset + error.length + 10)\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t})\n\t\t\t.map(\n\t\t\t\t(error) =>\n\t\t\t\t\t`${error.message} at ${error.offset}:${error.length} (${error.fragment})`\n\t\t\t);\n\t\tconst formattedEdits = edits.map(\n\t\t\t(edit) => `At ${edit.offset}:${edit.length} - (${edit.content})`\n\t\t);\n\t\tthrow new Error(\n\t\t\t`VS Code configuration file (.vscode/launch.json) is not valid a JSONC after Playground CLI modifications. This is likely ` +\n\t\t\t\t`a Playground CLI bug. Please report it at https://github.com/WordPress/wordpress-playground/issues and include the contents ` +\n\t\t\t\t`of your \".vscode/launch.json\" file. \\n\\n Applied edits: ${formattedEdits.join(\n\t\t\t\t\t'\\n'\n\t\t\t\t)}\\n\\n The errors are: ${formattedErrors.join('\\n')}`\n\t\t);\n\t}\n\n\treturn json;\n}\n\nfunction toPosixPath(pathStr: string) {\n\treturn pathStr.replaceAll(path.sep, path.posix.sep);\n}\n","import { errorLogPath, logger, LogSeverity } from '@php-wasm/logger';\nimport type {\n\tPHPRequest,\n\tRemoteAPI,\n\tSupportedPHPVersion,\n} from '@php-wasm/universal';\nimport {\n\tPHPResponse,\n\texposeAPI,\n\texposeSyncAPI,\n\tprintDebugDetails,\n} from '@php-wasm/universal';\nimport type {\n\tBlueprintBundle,\n\tBlueprintV1Declaration,\n\tBlueprintV2Declaration,\n} from '@wp-playground/blueprints';\nimport { runBlueprintV1Steps } from '@wp-playground/blueprints';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport fs, { mkdirSync } from 'fs';\nimport type { Server } from 'http';\nimport { MessageChannel as NodeMessageChannel, Worker } from 'worker_threads';\n// @ts-ignore\nimport {\n\texpandAutoMounts,\n\tparseMountDirArguments,\n\tparseMountWithDelimiterArguments,\n} from './mounts';\nimport { startServer } from './start-server';\nimport type {\n\tMount,\n\tPlaygroundCliBlueprintV1Worker,\n} from './blueprints-v1/worker-thread-v1';\nimport type { PlaygroundCliBlueprintV2Worker } from './blueprints-v2/worker-thread-v2';\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 { resolveBlueprint } from './resolve-blueprint';\nimport { BlueprintsV2Handler } from './blueprints-v2/blueprints-v2-handler';\nimport { BlueprintsV1Handler } from './blueprints-v1/blueprints-v1-handler';\nimport { startBridge } from '@php-wasm/xdebug-bridge';\nimport path from 'path';\nimport os from 'os';\nimport {\n\tcleanupStalePlaygroundTempDirs,\n\tcreatePlaygroundCliTempDir,\n} from './temp-dir';\nimport { type WordPressInstallMode } from '@wp-playground/wordpress';\nimport {\n\taddXdebugIDEConfig,\n\tclearXdebugIDEConfig,\n\tcreatePlaygroundCliTempDirSymlink,\n\tremovePlaygroundCliTempDirSymlink,\n} from './xdebug-path-mappings';\n\n// Inlined worker URLs for static analysis by downstream bundlers\n// These are replaced at build time by the Vite plugin in vite.config.ts\ndeclare const __WORKER_V1_URL__: string;\ndeclare const __WORKER_V2_URL__: string;\n\nexport const LogVerbosity = {\n\tQuiet: { name: 'quiet', severity: LogSeverity.Fatal },\n\tNormal: { name: 'normal', severity: LogSeverity.Info },\n\tDebug: { name: 'debug', severity: LogSeverity.Debug },\n} as const;\n\ntype LogVerbosity = (typeof LogVerbosity)[keyof typeof LogVerbosity]['name'];\n\nexport type WorkerType = 'v1' | 'v2';\n\n/**\n * Parse the CLI args and run the appropriate command.\n *\n * @param argsToParse string[] The CLI args to parse.\n */\nexport async function parseOptionsAndRunCLI(argsToParse: string[]) {\n\ttry {\n\t\t/**\n\t\t * @TODO This looks similar to Query API args https://wordpress.github.io/wordpress-playground/developers/apis/query-api/\n\t\t * Perhaps the two could be handled by the same code?\n\t\t */\n\t\tconst yargsObject = yargs(argsToParse)\n\t\t\t.usage('Usage: wp-playground <command> [options]')\n\t\t\t.positional('command', {\n\t\t\t\tdescribe: 'Command to run',\n\t\t\t\tchoices: ['server', 'run-blueprint', 'build-snapshot'] as const,\n\t\t\t\tdemandOption: true,\n\t\t\t})\n\t\t\t.option('outfile', {\n\t\t\t\tdescribe: 'When building, write to this output file.',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'wordpress.zip',\n\t\t\t})\n\t\t\t.option('port', {\n\t\t\t\tdescribe: 'Port to listen on when serving.',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 9400,\n\t\t\t})\n\t\t\t.option('site-url', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Site URL to use for WordPress. Defaults to http://127.0.0.1:{port}',\n\t\t\t\ttype: 'string',\n\t\t\t})\n\t\t\t.option('php', {\n\t\t\t\tdescribe: 'PHP version to use.',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: RecommendedPHPVersion,\n\t\t\t\tchoices: SupportedPHPVersions,\n\t\t\t})\n\t\t\t.option('wp', {\n\t\t\t\tdescribe: 'WordPress version to use.',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'latest',\n\t\t\t})\n\t\t\t// @TODO: Support read-only mounts, e.g. via WORKERFS, a custom\n\t\t\t// ReadOnlyNODEFS, or by copying the files into MEMFS\n\t\t\t.option('mount', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path',\n\t\t\t\ttype: 'array',\n\t\t\t\tstring: true,\n\t\t\t\tcoerce: parseMountWithDelimiterArguments,\n\t\t\t})\n\t\t\t.option('mount-before-install', {\n\t\t\t\tdescribe:\n\t\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\t\ttype: 'array',\n\t\t\t\tstring: true,\n\t\t\t\tcoerce: parseMountWithDelimiterArguments,\n\t\t\t})\n\t\t\t.option('mount-dir', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Mount a directory to the PHP runtime (can be used multiple times). Format: \"/host/path\" \"/vfs/path\"',\n\t\t\t\ttype: 'array',\n\t\t\t\tnargs: 2,\n\t\t\t\tarray: true,\n\t\t\t\tcoerce: parseMountDirArguments,\n\t\t\t})\n\t\t\t.option('mount-dir-before-install', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Mount a directory before WordPress installation (can be used multiple times). Format: \"/host/path\" \"/vfs/path\"',\n\t\t\t\ttype: 'string',\n\t\t\t\tnargs: 2,\n\t\t\t\tarray: true,\n\t\t\t\tcoerce: parseMountDirArguments,\n\t\t\t})\n\t\t\t.option('login', {\n\t\t\t\tdescribe: 'Should log the user in',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t.option('blueprint', {\n\t\t\t\tdescribe: 'Blueprint to execute.',\n\t\t\t\ttype: 'string',\n\t\t\t})\n\t\t\t.option('blueprint-may-read-adjacent-files', {\n\t\t\t\tdescribe:\n\t\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\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t.option('wordpress-install-mode', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Control how Playground prepares WordPress before booting.',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'download-and-install',\n\t\t\t\tchoices: [\n\t\t\t\t\t'download-and-install',\n\t\t\t\t\t'install-from-existing-files',\n\t\t\t\t\t'install-from-existing-files-if-needed',\n\t\t\t\t\t'do-not-attempt-installing',\n\t\t\t\t] as const,\n\t\t\t})\n\t\t\t.option('skip-wordpress-install', {\n\t\t\t\tdescribe: '[Deprecated] Use --wordpress-install-mode instead.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\thidden: true,\n\t\t\t})\n\t\t\t.option('skip-sqlite-setup', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t// Hidden - Deprecated in favor of verbosity\n\t\t\t.option('quiet', {\n\t\t\t\tdescribe: 'Do not output logs and progress messages.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t\thidden: true,\n\t\t\t})\n\t\t\t.option('verbosity', {\n\t\t\t\tdescribe: 'Output logs and progress messages.',\n\t\t\t\ttype: 'string',\n\t\t\t\tchoices: Object.values(LogVerbosity).map(\n\t\t\t\t\t(verbosity) => verbosity.name\n\t\t\t\t),\n\t\t\t\tdefault: 'normal',\n\t\t\t})\n\t\t\t.option('debug', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Print PHP error log content if an error occurs during Playground boot.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t.option('auto-mount', {\n\t\t\t\tdescribe: `Automatically mount the specified directory. If no path is provided, 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\t\ttype: 'string',\n\t\t\t})\n\t\t\t.option('follow-symlinks', {\n\t\t\t\tdescribe:\n\t\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\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t.option('experimental-trace', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Print detailed messages about system behavior to the console. Useful for troubleshooting.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t\t// Hide this option because we want to replace with a more general log-level flag.\n\t\t\t\thidden: true,\n\t\t\t})\n\t\t\t.option('internal-cookie-store', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Enable internal cookie handling. When enabled, Playground will manage cookies internally using ' +\n\t\t\t\t\t'an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled ' +\n\t\t\t\t\t'externally (e.g., by a browser in Node.js environments).',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t.option('xdebug', {\n\t\t\t\tdescribe: 'Enable Xdebug.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t})\n\t\t\t.option('experimental-unsafe-ide-integration', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Enable experimental IDE development tools. This option edits IDE config files ' +\n\t\t\t\t\t'to set Xdebug path mappings and web server details. CAUTION: If there are bugs, ' +\n\t\t\t\t\t'this feature may break your IDE config files. Please consider backing up your IDE configs ' +\n\t\t\t\t\t'before using this feature.',\n\t\t\t\ttype: 'string',\n\t\t\t\t// The empty value means the option is enabled for all\n\t\t\t\t// supported IDEs and, if needed, will create the relevant\n\t\t\t\t// config file for each.\n\t\t\t\tchoices: ['', 'vscode', 'phpstorm'],\n\t\t\t\tcoerce: (value?: string) =>\n\t\t\t\t\tvalue === '' ? ['vscode', 'phpstorm'] : [value],\n\t\t\t})\n\t\t\t.option('experimental-devtools', {\n\t\t\t\tdescribe: 'Enable experimental browser development tools.',\n\t\t\t\ttype: 'boolean',\n\t\t\t})\n\t\t\t.conflicts(\n\t\t\t\t'experimental-unsafe-ide-integration',\n\t\t\t\t'experimental-devtools'\n\t\t\t)\n\t\t\t.option('experimental-multi-worker', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Enable experimental multi-worker support which requires ' +\n\t\t\t\t\t'a /wordpress directory backed by a real filesystem. ' +\n\t\t\t\t\t'Pass a positive number to specify the number of workers to use. ' +\n\t\t\t\t\t'Otherwise, default to the number of CPUs minus 1.',\n\t\t\t\ttype: 'number',\n\t\t\t\tcoerce: (value?: number) => value ?? cpus().length - 1,\n\t\t\t})\n\t\t\t.option('experimental-blueprints-v2-runner', {\n\t\t\t\tdescribe: 'Use the experimental Blueprint V2 runner.',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t\t// Remove the \"hidden\" flag once Blueprint V2 is fully supported\n\t\t\t\thidden: true,\n\t\t\t})\n\t\t\t.option('mode', {\n\t\t\t\tdescribe:\n\t\t\t\t\t'Blueprints v2 runner mode to use. This option is required when using the --experimental-blueprints-v2-runner flag with a blueprint.',\n\t\t\t\ttype: 'string',\n\t\t\t\tchoices: ['create-new-site', 'apply-to-existing-site'],\n\t\t\t\t// Remove the \"hidden\" flag once Blueprint V2 is fully supported\n\t\t\t\thidden: true,\n\t\t\t})\n\t\t\t.showHelpOnFail(false)\n\t\t\t.strictOptions()\n\t\t\t.check(async (args) => {\n\t\t\t\tif (args['skip-wordpress-install'] === true) {\n\t\t\t\t\targs['wordpress-install-mode'] =\n\t\t\t\t\t\t'do-not-attempt-installing';\n\t\t\t\t\targs['wordpressInstallMode'] = 'do-not-attempt-installing';\n\t\t\t\t}\n\n\t\t\t\tif (args.wp !== undefined && !isValidWordPressSlug(args.wp)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Check if is valid URL\n\t\t\t\t\t\tnew URL(args.wp);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthrow new Error(\n\t\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\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (args['site-url'] !== undefined && args['site-url'] !== '') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnew URL(args['site-url']);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Invalid site-url \"${args['site-url']}\". Please provide a valid URL (e.g., http://localhost:8080 or https://example.com)`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (args['auto-mount']) {\n\t\t\t\t\tlet autoMountIsDir = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst autoMountStats = fs.statSync(args['auto-mount']);\n\t\t\t\t\t\tautoMountIsDir = autoMountStats.isDirectory();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tautoMountIsDir = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!autoMountIsDir) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`The specified --auto-mount path is not a directory: '${args['auto-mount']}'.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (args['experimental-multi-worker'] !== undefined) {\n\t\t\t\t\tconst cliCommand = args._[0] as string;\n\t\t\t\t\tif (cliCommand !== 'server') {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t'The --experimental-multi-worker flag is only supported when running the server command.'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (args['experimental-multi-worker'] <= 1) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t'The --experimental-multi-worker flag must be a positive integer greater than 1.'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (args['experimental-blueprints-v2-runner'] === true) {\n\t\t\t\t\tif (args['mode'] !== undefined) {\n\t\t\t\t\t\tif (args['wordpress-install-mode'] !== undefined) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t'The --wordpress-install-mode option cannot be used with the --mode option. Use one or the other.'\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ('skip-sqlite-setup' in args) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t'The --skipSqliteSetup option is not supported in Blueprint V2 mode.'\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (args['auto-mount'] !== undefined) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t'The --mode option cannot be used with --auto-mount because --auto-mount automatically sets the mode.'\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Support the legacy v1 runner options\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\targs['wordpress-install-mode'] ===\n\t\t\t\t\t\t\t'do-not-attempt-installing'\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\targs['mode'] = 'apply-to-existing-site';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\targs['mode'] = 'create-new-site';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support the legacy v1 runner options\n\t\t\t\t\tconst allow = (args['allow'] as string[]) || [];\n\n\t\t\t\t\tif (args['followSymlinks'] === true) {\n\t\t\t\t\t\tallow.push('follow-symlinks');\n\t\t\t\t\t}\n\n\t\t\t\t\tif (args['blueprint-may-read-adjacent-files'] === true) {\n\t\t\t\t\t\tallow.push('read-local-fs');\n\t\t\t\t\t}\n\n\t\t\t\t\targs['allow'] = allow;\n\t\t\t\t} else {\n\t\t\t\t\tif (args['mode'] !== undefined) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t'The --mode option requires the --experimentalBlueprintsV2Runner flag.'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\tyargsObject.wrap(yargsObject.terminalWidth());\n\t\tconst args = await yargsObject.argv;\n\n\t\tconst command = args._[0] as string;\n\n\t\tif (!['run-blueprint', 'server', 'build-snapshot'].includes(command)) {\n\t\t\tyargsObject.showHelp();\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst cliArgs = {\n\t\t\t...args,\n\t\t\tcommand,\n\t\t\tmount: [...(args.mount || []), ...(args['mount-dir'] || [])],\n\t\t\t'mount-before-install': [\n\t\t\t\t...(args['mount-before-install'] || []),\n\t\t\t\t...(args['mount-dir-before-install'] || []),\n\t\t\t],\n\t\t} as RunCLIArgs;\n\n\t\tconst cliServer = await runCLI(cliArgs);\n\t\tif (cliServer === undefined) {\n\t\t\t// No server was started, so we are done with our work.\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tconst cleanUpCliAndExit = (() => {\n\t\t\t// Remember we are already cleaning up to preclude the possibility\n\t\t\t// of multiple, conflicting cleanup attempts.\n\t\t\tlet promiseToCleanup: Promise<void>;\n\n\t\t\treturn async () => {\n\t\t\t\tif (promiseToCleanup !== undefined) {\n\t\t\t\t\tpromiseToCleanup = cliServer[Symbol.asyncDispose]();\n\t\t\t\t}\n\t\t\t\tawait promiseToCleanup;\n\t\t\t\tprocess.exit(0);\n\t\t\t};\n\t\t})();\n\n\t\t// Playground CLI server must be killed to exit. From the terminal,\n\t\t// this may occur via Ctrl+C which sends SIGINT. Let's handle both\n\t\t// SIGINT and SIGTERM (the default kill signal) to make sure we\n\t\t// clean up after ourselves even if this process is being killed.\n\t\t// NOTE: Windows does not support SIGTERM, but Node.js provides some emulation.\n\t\tprocess.on('SIGINT', cleanUpCliAndExit);\n\t\tprocess.on('SIGTERM', cleanUpCliAndExit);\n\t} catch (e) {\n\t\tif (!(e instanceof Error)) {\n\t\t\tthrow e;\n\t\t}\n\t\tconst debug = process.argv.includes('--debug');\n\t\tif (debug) {\n\t\t\tprintDebugDetails(e);\n\t\t} else {\n\t\t\tconst messageChain = [];\n\t\t\tlet currentError = e;\n\t\t\tdo {\n\t\t\t\tmessageChain.push(currentError.message);\n\t\t\t\tcurrentError = currentError.cause as Error;\n\t\t\t} while (currentError instanceof Error);\n\t\t\tconsole.error(\n\t\t\t\t'\\x1b[1m' + messageChain.join(' caused by: ') + '\\x1b[0m'\n\t\t\t);\n\t\t}\n\t\tprocess.exit(1);\n\t}\n}\n\nexport interface RunCLIArgs {\n\tblueprint?:\n\t\t| BlueprintV1Declaration\n\t\t| BlueprintV2Declaration\n\t\t| 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\t'site-url'?: string;\n\tquiet?: boolean;\n\tverbosity?: LogVerbosity;\n\twp?: string;\n\tautoMount?: string;\n\texperimentalMultiWorker?: number;\n\texperimentalTrace?: boolean;\n\tinternalCookieStore?: boolean;\n\t'additional-blueprint-steps'?: any[];\n\txdebug?: boolean | { ideKey?: string };\n\texperimentalUnsafeIdeIntegration?: string[];\n\texperimentalDevtools?: boolean;\n\t'experimental-blueprints-v2-runner'?: boolean;\n\twordpressInstallMode?: WordPressInstallMode;\n\n\t// --------- Blueprint V1 args -----------\n\tskipSqliteSetup?: boolean;\n\tfollowSymlinks?: boolean;\n\t'blueprint-may-read-adjacent-files'?: boolean;\n\n\t// --------- Blueprint V2 args -----------\n\tmode?: 'mount-only' | 'create-new-site' | 'apply-to-existing-site';\n\n\t// --------- Blueprint V2 args (not available via CLI yet) -----------\n\t'db-engine'?: 'sqlite' | 'mysql';\n\t'db-host'?: string;\n\t'db-user'?: string;\n\t'db-pass'?: string;\n\t'db-name'?: string;\n\t'db-path'?: string;\n\t'truncate-new-site-directory'?: boolean;\n\tallow?: string;\n}\n\ntype PlaygroundCliWorker =\n\t| PlaygroundCliBlueprintV1Worker\n\t| PlaygroundCliBlueprintV2Worker;\n\nexport interface RunCLIServer extends AsyncDisposable {\n\tplayground: RemoteAPI<PlaygroundCliWorker>;\n\tserver: Server;\n\tserverUrl: string;\n\n\t[Symbol.asyncDispose](): Promise<void>;\n\n\t// Expose the number of worker threads to the test runner.\n\tworkerThreadCount: number;\n}\n\nconst bold = (text: string) =>\n\tprocess.stdout.isTTY ? '\\x1b[1m' + text + '\\x1b[0m' : text;\n\nconst dim = (text: string) =>\n\tprocess.stdout.isTTY ? `\\x1b[2m${text}\\x1b[0m` : text;\n\nconst italic = (text: string) =>\n\tprocess.stdout.isTTY ? `\\x1b[3m${text}\\x1b[0m` : text;\n\nconst highlight = (text: string) =>\n\tprocess.stdout.isTTY ? `\\x1b[33m${text}\\x1b[0m` : text;\n\n// These overloads are declared for convenience so runCLI() can return\n// different things depending on the CLI command without forcing the\n// callers (mostly automated tests) to check return values.\nexport async function runCLI(\n\targs: RunCLIArgs & { command: 'build-snapshot' | 'run-blueprint' }\n): Promise<void>;\nexport async function runCLI(\n\targs: RunCLIArgs & { command: 'server' }\n): Promise<RunCLIServer>;\nexport async function runCLI(args: RunCLIArgs): Promise<RunCLIServer | void>;\nexport async function runCLI(args: RunCLIArgs): Promise<RunCLIServer | void> {\n\tlet loadBalancer: LoadBalancer;\n\tlet playground: RemoteAPI<PlaygroundCliWorker>;\n\n\tconst playgroundsToCleanUp: Map<\n\t\tWorker,\n\t\tRemoteAPI<PlaygroundCliWorker>\n\t> = new Map();\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 !== undefined) {\n\t\tif (args.autoMount === '') {\n\t\t\t// No auto-mount path was provided, so use the current working directory.\n\t\t\t// Note: We default here instead of in the yargs declaration because\n\t\t\t// it allows us to test the default as part of the runCLI() unit tests.\n\t\t\targs = { ...args, autoMount: process.cwd() };\n\t\t}\n\t\targs = expandAutoMounts(args);\n\t}\n\n\tif (args.wordpressInstallMode === undefined) {\n\t\targs.wordpressInstallMode = 'download-and-install';\n\t}\n\n\t// Keeping 'quiet' option to preserve backward compatibility\n\tif (args.quiet) {\n\t\targs.verbosity = 'quiet';\n\t\tdelete args['quiet'];\n\t}\n\n\t// Promote \"debug\" flag to verbosity but keep args.debug around – the\n\t// program behavior may change in more ways than just logging verbosity\n\t// when debug mode is enabled, e.g. error objects may carry additional details.\n\tif (args.debug) {\n\t\targs.verbosity = 'debug';\n\t} else if (args.verbosity === 'debug') {\n\t\targs.debug = true;\n\t}\n\n\tif (args.verbosity) {\n\t\tconst severity = Object.values(LogVerbosity).find(\n\t\t\t(v) => v.name === args.verbosity\n\t\t)!.severity;\n\t\tlogger.setSeverityFilterLevel(severity);\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 =\n\t\tos.platform() === 'win32'\n\t\t\t? // @TODO: Enable fs-ext here when it works with Windows.\n\t\t\t undefined\n\t\t\t: await import('fs-ext')\n\t\t\t\t\t.then((m) => m.flockSync)\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t'The fs-ext package is not installed. ' +\n\t\t\t\t\t\t\t\t'Internal file locking will not be integrated with ' +\n\t\t\t\t\t\t\t\t'host OS file locking.'\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t});\n\tconst fileLockManager = new FileLockManagerForNode(nativeFlockSync);\n\n\tlet wordPressReady = false;\n\tlet isFirstRequest = true;\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) => {\n\t\t\tconst host = '127.0.0.1';\n\t\t\tconst serverUrl = `http://${host}:${port}`;\n\t\t\tconst siteUrl = args['site-url'] || serverUrl;\n\n\t\t\tconst targetWorkerCount =\n\t\t\t\targs.command === 'server'\n\t\t\t\t\t? args.experimentalMultiWorker ?? 1\n\t\t\t\t\t: 1;\n\t\t\tconst totalWorkersToSpawn =\n\t\t\t\targs.command === 'server'\n\t\t\t\t\t? // Account for the initial worker\n\t\t\t\t\t // which is discarded by the server after setup.\n\t\t\t\t\t targetWorkerCount + 1\n\t\t\t\t\t: targetWorkerCount;\n\n\t\t\tconst processIdSpaceLength = Math.floor(\n\t\t\t\tNumber.MAX_SAFE_INTEGER / totalWorkersToSpawn\n\t\t\t);\n\n\t\t\t/*\n\t\t\t * Use a real temp dir as a target for the following Playground paths\n\t\t\t * so that multiple worker threads can share the same files.\n\t\t\t * - /internal\n\t\t\t * - /tmp\n\t\t\t * - /wordpress\n\t\t\t *\n\t\t\t * Sharing the same files leads to faster boot times and uses less memory\n\t\t\t * because we don't have to create or maintain multiple copies of the same files.\n\t\t\t */\n\t\t\tconst tempDirNameDelimiter = '-playground-cli-site-';\n\t\t\tconst nativeDir = await createPlaygroundCliTempDir(\n\t\t\t\ttempDirNameDelimiter\n\t\t\t);\n\t\t\tlogger.debug(`Native temp dir for VFS root: ${nativeDir.path}`);\n\n\t\t\tconst IDEConfigName = 'WP Playground CLI - Listen for Xdebug';\n\n\t\t\t// Always clean up any existing Playground files symlink in the project root.\n\t\t\tconst symlinkName = '.playground-xdebug-root';\n\t\t\tconst symlinkPath = path.join(process.cwd(), symlinkName);\n\n\t\t\tawait removePlaygroundCliTempDirSymlink(symlinkPath);\n\n\t\t\t// Then, if xdebug, and experimental IDE are enabled,\n\t\t\t// recreate the symlink pointing to the temporary\n\t\t\t// directory and add the new IDE config.\n\t\t\tif (args.xdebug && args.experimentalUnsafeIdeIntegration) {\n\t\t\t\tawait createPlaygroundCliTempDirSymlink(\n\t\t\t\t\tnativeDir.path,\n\t\t\t\t\tsymlinkPath,\n\t\t\t\t\tprocess.platform\n\t\t\t\t);\n\n\t\t\t\tconst symlinkMount: Mount = {\n\t\t\t\t\thostPath: path.join('.', path.sep, symlinkName),\n\t\t\t\t\tvfsPath: '/',\n\t\t\t\t};\n\n\t\t\t\ttry {\n\t\t\t\t\t// NOTE: Both the 'clear' and 'add' operations can throw errors.\n\t\t\t\t\tawait clearXdebugIDEConfig(IDEConfigName, process.cwd());\n\n\t\t\t\t\tconst xdebugOptions =\n\t\t\t\t\t\ttypeof args.xdebug === 'object'\n\t\t\t\t\t\t\t? args.xdebug\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\tconst modifiedConfig = await addXdebugIDEConfig({\n\t\t\t\t\t\tname: IDEConfigName,\n\t\t\t\t\t\thost: host,\n\t\t\t\t\t\tport: port,\n\t\t\t\t\t\tides: args.experimentalUnsafeIdeIntegration!,\n\t\t\t\t\t\tcwd: process.cwd(),\n\t\t\t\t\t\tmounts: [\n\t\t\t\t\t\t\tsymlinkMount,\n\t\t\t\t\t\t\t...(args['mount-before-install'] || []),\n\t\t\t\t\t\t\t...(args.mount || []),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tideKey: xdebugOptions?.ideKey,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Display IDE-specific instructions\n\t\t\t\t\tconst ides = args.experimentalUnsafeIdeIntegration;\n\t\t\t\t\tconst hasVSCode = ides.includes('vscode');\n\t\t\t\t\tconst hasPhpStorm = ides.includes('phpstorm');\n\n\t\t\t\t\tconsole.log('');\n\t\t\t\t\tconsole.log(bold(`Xdebug configured successfully`));\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\thighlight(`Updated IDE config: `) +\n\t\t\t\t\t\t\tmodifiedConfig.join(' ')\n\t\t\t\t\t);\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\thighlight('Playground source root: ') +\n\t\t\t\t\t\t\t`.playground-xdebug-root` +\n\t\t\t\t\t\t\titalic(\n\t\t\t\t\t\t\t\tdim(\n\t\t\t\t\t\t\t\t\t` – you can set breakpoints and preview Playground's VFS structure in there.`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\tif (hasVSCode) {\n\t\t\t\t\t\tconsole.log(bold('VS Code / Cursor instructions:'));\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t' 1. Ensure you have installed an IDE extension for PHP Debugging'\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` (The ${bold('PHP Debug')} extension by ${bold(\n\t\t\t\t\t\t\t\t'Xdebug'\n\t\t\t\t\t\t\t)} has been a solid option)`\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t' 2. Open the Run and Debug panel on the left sidebar'\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` 3. Select \"${italic(\n\t\t\t\t\t\t\t\tIDEConfigName\n\t\t\t\t\t\t\t)}\" from the dropdown`\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(' 3. Click \"start debugging\"');\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t' 5. Set a breakpoint. For example, in .playground-xdebug-root/wordpress/index.php'\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t' 6. Visit Playground in your browser to hit the breakpoint'\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (hasPhpStorm) {\n\t\t\t\t\t\t\tconsole.log('');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (hasPhpStorm) {\n\t\t\t\t\t\tconsole.log(bold('PhpStorm instructions:'));\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` 1. Choose \"${italic(\n\t\t\t\t\t\t\t\tIDEConfigName\n\t\t\t\t\t\t\t)}\" debug configuration in the toolbar`\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(' 2. Click the debug button (bug icon)`');\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t' 3. Set a breakpoint. For example, in .playground-xdebug-root/wordpress/index.php'\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t' 4. Visit Playground in your browser to hit the breakpoint'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconsole.log('');\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow new Error('Could not configure Xdebug', {\n\t\t\t\t\t\tcause: error,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// We do not know the system temp dir,\n\t\t\t// but we can try to infer from the location of the current temp dir.\n\t\t\tconst tempDirRoot = path.dirname(nativeDir.path);\n\n\t\t\tconst twoDaysInMillis = 2 * 24 * 60 * 60 * 1000;\n\t\t\tconst tempDirStaleAgeInMillis = twoDaysInMillis;\n\n\t\t\t// NOTE: This is an async operation, but we do not care to block on it.\n\t\t\t// Let's let the cleanup happen as the main thread has time.\n\t\t\tcleanupStalePlaygroundTempDirs(\n\t\t\t\ttempDirNameDelimiter,\n\t\t\t\ttempDirStaleAgeInMillis,\n\t\t\t\ttempDirRoot\n\t\t\t);\n\n\t\t\t// NOTE: We do not add mount declarations for /internal here\n\t\t\t// because it will be mounted as part of php-wasm init.\n\t\t\tconst nativeInternalDirPath = path.join(nativeDir.path, 'internal');\n\t\t\tmkdirSync(nativeInternalDirPath);\n\n\t\t\tconst userProvidableNativeSubdirs = [\n\t\t\t\t'wordpress',\n\t\t\t\t// Note: These dirs are from Emscripten's \"default dirs\" list:\n\t\t\t\t// https://github.com/emscripten-core/emscripten/blob/f431ec220e472e1f8d3db6b52fe23fb377facf30/src/lib/libfs.js#L1400-L1402\n\t\t\t\t//\n\t\t\t\t// Any Playground process with multiple workers may assume\n\t\t\t\t// these are part of a shared filesystem, so let's recognize\n\t\t\t\t// them explicitly here.\n\t\t\t\t'tmp',\n\t\t\t\t'home',\n\t\t\t];\n\n\t\t\tfor (const subdirName of userProvidableNativeSubdirs) {\n\t\t\t\tconst isMountingSubdirName = (mount: Mount) =>\n\t\t\t\t\tmount.vfsPath === `/${subdirName}`;\n\t\t\t\tconst thisSubdirHasAMount =\n\t\t\t\t\targs['mount-before-install']?.some(isMountingSubdirName) ||\n\t\t\t\t\targs['mount']?.some(isMountingSubdirName);\n\t\t\t\tif (!thisSubdirHasAMount) {\n\t\t\t\t\t// The user hasn't requested mounting a different native dir for this path,\n\t\t\t\t\t// so let's create a mount from within our native temp dir.\n\t\t\t\t\tconst nativeSubdirPath = path.join(\n\t\t\t\t\t\tnativeDir.path,\n\t\t\t\t\t\tsubdirName\n\t\t\t\t\t);\n\t\t\t\t\tmkdirSync(nativeSubdirPath);\n\n\t\t\t\t\tif (args['mount-before-install'] === undefined) {\n\t\t\t\t\t\targs['mount-before-install'] = [];\n\t\t\t\t\t}\n\n\t\t\t\t\t// Make the real mount first so any further subdirs are mounted into it.\n\t\t\t\t\targs['mount-before-install'].unshift({\n\t\t\t\t\t\tvfsPath: `/${subdirName}`,\n\t\t\t\t\t\thostPath: nativeSubdirPath,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (args['mount-before-install']) {\n\t\t\t\tfor (const mount of args['mount-before-install']) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`Mount before WP install: ${mount.vfsPath} -> ${mount.hostPath}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (args['mount']) {\n\t\t\t\tfor (const mount of args['mount']) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`Mount after WP install: ${mount.vfsPath} -> ${mount.hostPath}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet handler: BlueprintsV1Handler | BlueprintsV2Handler;\n\t\t\tif (args['experimental-blueprints-v2-runner']) {\n\t\t\t\thandler = new BlueprintsV2Handler(args, {\n\t\t\t\t\tsiteUrl,\n\t\t\t\t\tprocessIdSpaceLength,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\thandler = new BlueprintsV1Handler(args, {\n\t\t\t\t\tsiteUrl,\n\t\t\t\t\tprocessIdSpaceLength,\n\t\t\t\t});\n\n\t\t\t\tif (typeof args.blueprint === 'string') {\n\t\t\t\t\targs.blueprint = await resolveBlueprint({\n\t\t\t\t\t\tsourceString: args.blueprint,\n\t\t\t\t\t\tblueprintMayReadAdjacentFiles:\n\t\t\t\t\t\t\targs['blueprint-may-read-adjacent-files'] === true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remember whether we are already disposing so we can avoid:\n\t\t\t// - we can avoid multiple, conflicting dispose attempts\n\t\t\t// - logging that a worker exited while the CLI itself is exiting\n\t\t\tlet disposing = false;\n\t\t\tconst disposeCLI = async function disposeCLI() {\n\t\t\t\tif (disposing) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tdisposing = true;\n\t\t\t\tawait Promise.all(\n\t\t\t\t\t[...playgroundsToCleanUp].map(\n\t\t\t\t\t\tasync ([worker, playground]) => {\n\t\t\t\t\t\t\tawait playground.dispose();\n\t\t\t\t\t\t\tawait worker.terminate();\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t\tif (server) {\n\t\t\t\t\tawait new Promise((resolve) => server.close(resolve));\n\t\t\t\t}\n\t\t\t\tawait nativeDir.cleanup();\n\t\t\t};\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 promisedWorkers = spawnWorkerThreads(\n\t\t\t\ttotalWorkersToSpawn,\n\t\t\t\thandler.getWorkerType(),\n\t\t\t\t({ exitCode, workerIndex }) => {\n\t\t\t\t\t// We are already disposing, so worker exit is expected\n\t\t\t\t\t// and does not need to be logged.\n\t\t\t\t\tif (disposing) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (exitCode !== 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t`Worker ${workerIndex} exited with code ${exitCode}\\n`\n\t\t\t\t\t);\n\t\t\t\t\t// @TODO: Should we respawn the worker if it exited with an error and the CLI is not shutting down?\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tlogger.log(`Starting up workers`);\n\n\t\t\ttry {\n\t\t\t\tconst workers = await promisedWorkers;\n\n\t\t\t\tconst fileLockManagerPort = await exposeFileLockManager(\n\t\t\t\t\tfileLockManager\n\t\t\t\t);\n\n\t\t\t\t// NOTE: Using a free-standing block to isolate initial boot vars\n\t\t\t\t// while keeping the logic inline.\n\t\t\t\t{\n\t\t\t\t\t// Boot the primary worker using the handler\n\t\t\t\t\tconst initialWorker = workers.shift()!;\n\t\t\t\t\tconst initialPlayground =\n\t\t\t\t\t\tawait handler.bootAndSetUpInitialPlayground(\n\t\t\t\t\t\t\tinitialWorker.phpPort,\n\t\t\t\t\t\t\tfileLockManagerPort,\n\t\t\t\t\t\t\tnativeInternalDirPath\n\t\t\t\t\t\t);\n\t\t\t\t\tplaygroundsToCleanUp.set(\n\t\t\t\t\t\tinitialWorker.worker,\n\t\t\t\t\t\tinitialPlayground\n\t\t\t\t\t);\n\n\t\t\t\t\tawait initialPlayground.isReady();\n\t\t\t\t\twordPressReady = true;\n\t\t\t\t\tlogger.log(`Booted!`);\n\n\t\t\t\t\tloadBalancer = new LoadBalancer(initialPlayground);\n\n\t\t\t\t\tif (!args['experimental-blueprints-v2-runner']) {\n\t\t\t\t\t\tconst compiledBlueprint = await (\n\t\t\t\t\t\t\thandler as BlueprintsV1Handler\n\t\t\t\t\t\t).compileInputBlueprint(\n\t\t\t\t\t\t\targs['additional-blueprint-steps'] || []\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (compiledBlueprint) {\n\t\t\t\t\t\t\tlogger.log(`Running the Blueprint...`);\n\t\t\t\t\t\t\tawait runBlueprintV1Steps(\n\t\t\t\t\t\t\t\tcompiledBlueprint,\n\t\t\t\t\t\t\t\tinitialPlayground\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tlogger.log(`Finished running the blueprint`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (args.command === 'build-snapshot') {\n\t\t\t\t\t\tawait zipSite(playground, args.outfile as string);\n\t\t\t\t\t\tlogger.log(`WordPress exported to ${args.outfile}`);\n\t\t\t\t\t\tawait disposeCLI();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else if (args.command === 'run-blueprint') {\n\t\t\t\t\t\tlogger.log(`Blueprint executed`);\n\t\t\t\t\t\tawait disposeCLI();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// We discard the initial Playground worker because it can\n\t\t\t\t\t// be configured differently than post-boot workers.\n\t\t\t\t\t// For example, we do not enable Xdebug by default for the initial worker.\n\t\t\t\t\tawait loadBalancer.removeWorker(initialPlayground);\n\t\t\t\t\tawait initialPlayground.dispose();\n\t\t\t\t\tawait initialWorker.worker.terminate();\n\t\t\t\t\tplaygroundsToCleanUp.delete(initialWorker.worker);\n\t\t\t\t}\n\n\t\t\t\tlogger.log(`Preparing workers...`);\n\n\t\t\t\t// Boot additional workers using the handler\n\t\t\t\tconst initialWorkerProcessIdSpace = processIdSpaceLength;\n\t\t\t\t// Just take the first Playground instance to be returned to the caller.\n\t\t\t\t[playground] = await Promise.all(\n\t\t\t\t\tworkers.map(async (worker, index) => {\n\t\t\t\t\t\tconst firstProcessId =\n\t\t\t\t\t\t\tinitialWorkerProcessIdSpace +\n\t\t\t\t\t\t\tindex * processIdSpaceLength;\n\n\t\t\t\t\t\tconst fileLockManagerPort = await exposeFileLockManager(\n\t\t\t\t\t\t\tfileLockManager\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst additionalPlayground =\n\t\t\t\t\t\t\tawait handler.bootPlayground({\n\t\t\t\t\t\t\t\tworker,\n\t\t\t\t\t\t\t\tfileLockManagerPort,\n\t\t\t\t\t\t\t\tfirstProcessId,\n\t\t\t\t\t\t\t\tnativeInternalDirPath,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tplaygroundsToCleanUp.set(\n\t\t\t\t\t\t\tworker.worker,\n\t\t\t\t\t\t\tadditionalPlayground\n\t\t\t\t\t\t);\n\t\t\t\t\t\tloadBalancer.addWorker(additionalPlayground);\n\n\t\t\t\t\t\treturn additionalPlayground;\n\t\t\t\t\t})\n\t\t\t\t);\n\n\t\t\t\tlogger.log(\n\t\t\t\t\t`WordPress is running on ${serverUrl} with ${targetWorkerCount} worker(s)`\n\t\t\t\t);\n\n\t\t\t\tif (args.xdebug && args.experimentalDevtools) {\n\t\t\t\t\tconst bridge = await startBridge({\n\t\t\t\t\t\tphpInstance: playground,\n\t\t\t\t\t\tphpRoot: '/wordpress',\n\t\t\t\t\t});\n\n\t\t\t\t\tbridge.start();\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tplayground,\n\t\t\t\t\tserver,\n\t\t\t\t\tserverUrl,\n\t\t\t\t\t[Symbol.asyncDispose]: disposeCLI,\n\t\t\t\t\tworkerThreadCount: targetWorkerCount,\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\tlet phpLogs = '';\n\t\t\t\tif (await playground?.fileExists(errorLogPath)) {\n\t\t\t\t\tphpLogs = await playground.readFileAsText(errorLogPath);\n\t\t\t\t}\n\t\t\t\tawait disposeCLI();\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\t// Clear the playground_auto_login_already_happened cookie on the first request.\n\t\t\t// Otherwise the first Playground CLI server started on the machine will set it,\n\t\t\t// all the subsequent runs will get the stale cookie, and the auto-login will\n\t\t\t// assume they don't have to auto-login again.\n\t\t\tif (isFirstRequest) {\n\t\t\t\tisFirstRequest = false;\n\t\t\t\tconst headers: Record<string, string[]> = {\n\t\t\t\t\t'Content-Type': ['text/plain'],\n\t\t\t\t\t'Content-Length': ['0'],\n\t\t\t\t\tLocation: [request.url],\n\t\t\t\t};\n\t\t\t\tif (\n\t\t\t\t\trequest.headers?.['cookie']?.includes(\n\t\t\t\t\t\t'playground_auto_login_already_happened'\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\theaders['Set-Cookie'] = [\n\t\t\t\t\t\t'playground_auto_login_already_happened=1; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/',\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\treturn new PHPResponse(302, headers, new Uint8Array());\n\t\t\t}\n\t\t\treturn await loadBalancer.handleRequest(request);\n\t\t},\n\t});\n}\n\nexport type SpawnedWorker = {\n\tworker: Worker;\n\tphpPort: NodeMessagePort;\n};\n\nasync function spawnWorkerThreads(\n\tcount: number,\n\tworkerType: WorkerType,\n\tonWorkerExit: (options: { exitCode: number; workerIndex: number }) => void\n): Promise<SpawnedWorker[]> {\n\tconst promises = [];\n\tfor (let i = 0; i < count; i++) {\n\t\tconst worker = await spawnWorkerThread(workerType);\n\t\tconst onExit: (code: number) => void = (code: number) => {\n\t\t\tonWorkerExit({\n\t\t\t\texitCode: code,\n\t\t\t\tworkerIndex: i,\n\t\t\t});\n\t\t};\n\t\tpromises.push(\n\t\t\tnew Promise<{ worker: Worker; phpPort: NodeMessagePort }>(\n\t\t\t\t(resolve, reject) => {\n\t\t\t\t\tworker.once('message', function (message: any) {\n\t\t\t\t\t\t// Let the worker confirm it has initialized.\n\t\t\t\t\t\t// We could use the 'online' event to detect start of JS execution,\n\t\t\t\t\t\t// but that would miss initialization errors.\n\t\t\t\t\t\tif (message.command === 'worker-script-initialized') {\n\t\t\t\t\t\t\tresolve({ worker, phpPort: message.phpPort });\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tworker.once('error', function (e: Error) {\n\t\t\t\t\t\tconsole.error(e);\n\t\t\t\t\t\tconst error = new Error(\n\t\t\t\t\t\t\t`Worker failed to load worker. ${\n\t\t\t\t\t\t\t\te.message ? `Original error: ${e.message}` : ''\n\t\t\t\t\t\t\t}`\n\t\t\t\t\t\t);\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t});\n\t\t\t\t\tworker.once('exit', onExit);\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n\treturn Promise.all(promises);\n}\n\n/**\n * A statically analyzable function that spawns a worker thread of a given type.\n *\n * **Important:** This function builds to code that has the worker URL hardcoded\n * inline, e.g. `new Worker(new URL('./worker-thread-v1.js', import.meta.url))`.\n * This allows the downstream consumers to statically analyze the code, recognize\n * it uses workers, create new entrypoints, and rewrite the new Worker() calls.\n *\n * @param workerType\n * @returns\n */\nasync function spawnWorkerThread(workerType: 'v1' | 'v2') {\n\t/**\n\t * When running the CLI from source via `node cli.ts`, the Vite-provided\n\t * __WORKER_V1_URL__ and __WORKER_V2_URL__ are undefined. Let's set them to\n\t * the correct paths.\n\t */\n\tif (typeof __WORKER_V1_URL__ === 'undefined') {\n\t\t// @ts-expect-error\n\t\tglobalThis['__WORKER_V1_URL__'] = './blueprints-v1/worker-thread-v1.ts';\n\t}\n\tif (typeof __WORKER_V2_URL__ === 'undefined') {\n\t\t// @ts-expect-error\n\t\tglobalThis['__WORKER_V2_URL__'] = './blueprints-v2/worker-thread-v2.ts';\n\t}\n\tif (workerType === 'v1') {\n\t\treturn new Worker(new URL(__WORKER_V1_URL__, import.meta.url));\n\t} else {\n\t\treturn new Worker(new URL(__WORKER_V2_URL__, import.meta.url));\n\t}\n}\n\n/**\n * Expose the file lock manager API on a MessagePort and return it.\n *\n * @see comlink-sync.ts\n * @see phpwasm-emscripten-library-file-locking-for-node.js\n */\nasync function exposeFileLockManager(fileLockManager: FileLockManagerForNode) {\n\tconst { port1, port2 } = new NodeMessageChannel();\n\tif (await jspi()) {\n\t\t/**\n\t\t * When JSPI is available, the worker thread expects an asynchronous API.\n\t\t *\n\t\t * @see worker-thread.ts\n\t\t * @see comlink-sync.ts\n\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t */\n\t\texposeAPI(fileLockManager, null, port1);\n\t} else {\n\t\t/**\n\t\t * When JSPI is not available, the worker thread expects a synchronous API.\n\t\t *\n\t\t * @see worker-thread.ts\n\t\t * @see comlink-sync.ts\n\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t */\n\t\tawait exposeSyncAPI(fileLockManager, port1);\n\t}\n\treturn port2;\n}\n\nasync function zipSite(\n\tplayground: RemoteAPI<PlaygroundCliWorker>,\n\toutfile: string\n) {\n\tawait playground.run({\n\t\tcode: `<?php\n\t\t$zip = new ZipArchive();\n\t\tif(false === $zip->open('/tmp/build.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE)) {\n\t\t\tthrow new Exception('Failed to create ZIP');\n\t\t}\n\t\t$files = new RecursiveIteratorIterator(\n\t\t\tnew RecursiveDirectoryIterator('/wordpress')\n\t\t);\n\t\tforeach ($files as $file) {\n\t\t\techo $file . PHP_EOL;\n\t\t\tif (!$file->isFile()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$zip->addFile($file->getPathname(), $file->getPathname());\n\t\t}\n\t\t$zip->close();\n\n\t`,\n\t});\n\tconst zip = await playground.readFileAsBuffer('/tmp/build.zip');\n\tfs.writeFileSync(outfile, zip);\n}\n"],"names":["startServer","options","app","express","server","resolve","reject","address","req","res","phpResponse","parseHeaders","bufferRequestBody","error","logger","PHPResponse","key","port","body","chunk","requestHeaders","i","LoadBalancer","initialWorker","worker","workerIndex","workerLoad","removedWorker","request","smallestWorkerLoad","promiseForResponse","isValidWordPressSlug","version","resolveBlueprint","sourceString","blueprintMayReadAdjacentFiles","resolveRemoteBlueprint","blueprintPath","path","fs","stat","extension","ZipFilesystem","blueprintText","contextPath","nodeJsFilesystem","NodeJsFilesystem","OverlayFilesystem","InMemoryFilesystem","BlueprintsV2Handler","args","phpPort","fileLockManagerPort","nativeInternalDirPath","playground","consumeAPI","workerBootArgs","firstProcessId","writeStream","message","finalUpdate","CACHE_FOLDER","os","fetchSqliteIntegration","monitor","cachedDownload","remoteUrl","cacheKey","artifactPath","downloadTo","readAsFile","localPath","reader","tmpPath","writer","done","value","err","fileName","basename","BlueprintsV1Handler","wpDetails","wordPressZip","preinstalledWpContentPath","EmscriptenDownloadMonitor","progressReached100","e","loaded","total","percentProgress","resolveWordPressRelease","sqliteIntegrationPluginZip","followSymlinks","trace","mountsBeforeWpInstall","mountsAfterWpInstall","runtimeConfiguration","resolveRuntimeConfiguration","zipDirectory","additionalBlueprintSteps","blueprint","tracker","ProgressTracker","lastCaption","progressInteger","compileBlueprintV1","resolvedBlueprint","isBlueprintBundle","RecommendedPHPVersion","LogVerbosity","createPlaygroundCliTempDir","substrToIdentifyTempDirs","autoCleanup","tempDirPrefix","nativeDir","tmpDir","tmpSetGracefulCleanup","cleanupStalePlaygroundTempDirs","staleAgeInMillis","tempRootDir","promisesToRemove","findStalePlaygroundTempDirs","stalePlaygroundTempDir","tempPaths","dirName","stalePlaygroundTempDirs","tempPath","appearsToBeStalePlaygroundTempDir","absolutePath","match","info","doesProcessExist","STALE_DATE","pid","executableName","existingProcess","ps","processes","createPlaygroundCliTempDirSymlink","nativeDirPath","symlinkPath","platform","type","removePlaygroundCliTempDirSymlink","filterLocalMounts","cwd","mounts","mount","absoluteHostPath","cwdChildPrefix","xmlParserOptions","xmlBuilderOptions","jsoncParseOptions","updatePhpStormConfig","xmlContent","name","host","mappings","ideKey","xmlParser","XMLParser","config","serverElement","mapping","toPosixPath","projectElement","c","projectVersion","componentElement","serversElement","serverElementIndex","runManagerElement","runConfigElement","xml","XMLBuilder","updateVSCodeConfig","jsonContent","errors","content","root","JSONC","configurationsNode","edits","configurationIndex","child","configuration","acc","currentLength","jsoncApplyEdits","addXdebugIDEConfig","ides","modifiedConfig","phpStormRelativeConfigFilePath","phpStormConfigFilePath","contents","updatedXml","vsCodeRelativeConfigFilePath","vsCodeConfigFilePath","updatedJson","clearXdebugIDEConfig","json","formattedErrors","formattedEdits","edit","pathStr","LogSeverity","parseOptionsAndRunCLI","argsToParse","yargsObject","yargs","SupportedPHPVersions","parseMountWithDelimiterArguments","parseMountDirArguments","verbosity","cpus","autoMountIsDir","allow","command","cliArgs","cliServer","runCLI","cleanUpCliAndExit","promiseToCleanup","printDebugDetails","messageChain","currentError","bold","text","dim","italic","highlight","loadBalancer","playgroundsToCleanUp","expandAutoMounts","severity","v","nativeFlockSync","m","fileLockManager","FileLockManagerForNode","wordPressReady","isFirstRequest","serverUrl","siteUrl","targetWorkerCount","totalWorkersToSpawn","processIdSpaceLength","tempDirNameDelimiter","IDEConfigName","symlinkName","symlinkMount","xdebugOptions","hasVSCode","hasPhpStorm","tempDirRoot","tempDirStaleAgeInMillis","mkdirSync","userProvidableNativeSubdirs","subdirName","isMountingSubdirName","nativeSubdirPath","handler","disposing","disposeCLI","promisedWorkers","spawnWorkerThreads","exitCode","workers","exposeFileLockManager","initialPlayground","compiledBlueprint","runBlueprintV1Steps","zipSite","initialWorkerProcessIdSpace","index","additionalPlayground","startBridge","phpLogs","errorLogPath","headers","count","workerType","onWorkerExit","promises","spawnWorkerThread","onExit","code","Worker","_documentCurrentScript","port1","port2","NodeMessageChannel","jspi","exposeAPI","exposeSyncAPI","outfile","zip"],"mappings":"ipCAcA,eAAsBA,GACrBC,EAC+B,CAC/B,MAAMC,EAAMC,GAAA,EAENC,EAAS,MAAM,IAAI,QAEvB,CAACC,EAASC,IAAW,CACtB,MAAMF,EAASF,EAAI,OAAOD,EAAQ,KAAM,IAAM,CAC7C,MAAMM,EAAUH,EAAO,QAAA,EACnBG,IAAY,MAAQ,OAAOA,GAAY,SAC1CD,EAAO,IAAI,MAAM,iCAAiC,CAAC,EAEnDD,EAAQD,CAAM,CAEhB,CAAC,CACF,CAAC,EAEDF,EAAI,IAAI,IAAK,MAAOM,EAAKC,IAAQ,CAChC,IAAIC,EACJ,GAAI,CACHA,EAAc,MAAMT,EAAQ,cAAc,CACzC,IAAKO,EAAI,IACT,QAASG,GAAaH,CAAG,EACzB,OAAQA,EAAI,OACZ,KAAM,MAAMI,GAAkBJ,CAAG,CAAA,CACjC,CACF,OAASK,EAAO,CACfC,EAAAA,OAAO,MAAMD,CAAK,EAClBH,EAAcK,EAAAA,YAAY,YAAY,GAAG,CAC1C,CAEAN,EAAI,WAAaC,EAAY,eAC7B,UAAWM,KAAON,EAAY,QAC7BD,EAAI,UAAUO,EAAKN,EAAY,QAAQM,CAAG,CAAC,EAE5CP,EAAI,IAAIC,EAAY,KAAK,CAC1B,CAAC,EAGD,MAAMO,EADUb,EAAO,QAAA,EACgB,KACvC,OAAO,MAAMH,EAAQ,OAAOG,EAAQa,CAAI,CACzC,CAEA,MAAML,GAAoB,MAAOJ,GAChC,MAAM,IAAI,QAASH,GAAY,CAC9B,MAAMa,EAAqB,CAAA,EAC3BV,EAAI,GAAG,OAASW,GAAU,CACzBD,EAAK,KAAKC,CAAK,CAChB,CAAC,EACDX,EAAI,GAAG,MAAO,IAAM,CACnBH,EAAQ,IAAI,WAAW,OAAO,OAAOa,CAAI,CAAC,CAAC,CAC5C,CAAC,CACF,CAAC,EAEIP,GAAgBH,GAAyC,CAC9D,MAAMY,EAAyC,CAAA,EAC/C,GAAIZ,EAAI,YAAcA,EAAI,WAAW,OACpC,QAASa,EAAI,EAAGA,EAAIb,EAAI,WAAW,OAAQa,GAAK,EAC/CD,EAAeZ,EAAI,WAAWa,CAAC,EAAE,aAAa,EAC7Cb,EAAI,WAAWa,EAAI,CAAC,EAGvB,OAAOD,CACR,EC/DO,MAAME,EAAa,CAGzB,YAMCC,EACC,CATF,KAAA,YAA4B,CAAA,EAU3B,KAAK,UAAUA,CAAa,CAC7B,CAEA,UAAUC,EAAwC,CACjD,KAAK,YAAY,KAAK,CACrB,OAAAA,EACA,mBAAoB,GAAI,CACxB,CACF,CACA,MAAM,aAAaA,EAAwC,CAC1D,MAAMC,EAAc,KAAK,YAAY,UACnCC,GAAeA,EAAW,SAAWF,CAAA,EAEvC,GAAIC,IAAgB,GACnB,OAGD,KAAM,CAACE,CAAa,EAAI,KAAK,YAAY,OAAOF,EAAa,CAAC,EAI9D,MAAM,QAAQ,WAAWE,EAAc,cAAc,CACtD,CAEA,MAAM,cAAcC,EAAqB,CACxC,IAAIC,EAAqB,KAAK,YAAY,CAAC,EAM3C,QAASR,EAAI,EAAGA,EAAI,KAAK,YAAY,OAAQA,IAAK,CACjD,MAAMK,EAAa,KAAK,YAAYL,CAAC,EAEpCK,EAAW,eAAe,KAC1BG,EAAmB,eAAe,OAElCA,EAAqBH,EAEvB,CAIA,MAAMI,EAAqBD,EAAmB,OAAO,QAAQD,CAAO,EACpE,OAAAC,EAAmB,eAAe,IAAIC,CAAkB,EAGvDA,EAA2B,IAAMF,EAAQ,IAEnCE,EAAmB,QAAQ,IAAM,CACvCD,EAAmB,eAAe,OAAOC,CAAkB,CAC5D,CAAC,CACF,CACD,CC/DO,SAASC,GAAqBC,EAA0B,CAG9D,MADC,gGACqB,KAAKA,CAAO,CACnC,CCMA,eAAsBC,GAAiB,CACtC,aAAAC,EACA,8BAAAC,CACD,EAA4B,CAC3B,GAAI,CAACD,EACJ,OAGD,GACCA,EAAa,WAAW,SAAS,GACjCA,EAAa,WAAW,UAAU,EAElC,OAAO,MAAME,EAAAA,uBAAuBF,CAAY,EAMjD,IAAIG,EAAgBC,EAAK,QAAQ,QAAQ,IAAA,EAAOJ,CAAY,EAC5D,GAAI,CAACK,EAAG,WAAWF,CAAa,EAC/B,MAAM,IAAI,MAAM,kCAAkCA,CAAa,EAAE,EAGlE,MAAMG,EAAOD,EAAG,SAASF,CAAa,EAKtC,GAJIG,EAAK,gBACRH,EAAgBC,EAAK,KAAKD,EAAe,gBAAgB,GAGtD,CAACG,EAAK,OAAA,GAAYA,EAAK,iBAC1B,MAAM,IAAI,MACT,qDAAqDH,CAAa,EAAA,EAIpE,MAAMI,EAAYH,EAAK,QAAQD,CAAa,EAC5C,OAAQI,EAAA,CACP,IAAK,OACJ,OAAOC,EAAAA,cAAc,gBACpBH,EAAG,aAAaF,CAAa,EAAE,MAAA,EAEjC,IAAK,QAAS,CACb,MAAMM,EAAgBJ,EAAG,aAAaF,EAAe,OAAO,EAC5D,GAAI,CACH,KAAK,MAAMM,CAAa,CACzB,MAAQ,CACP,MAAM,IAAI,MACT,qBAAqBN,CAAa,2BAAA,CAEpC,CAEA,MAAMO,EAAcN,EAAK,QAAQD,CAAa,EACxCQ,EAAmB,IAAIC,EAAAA,iBAAiBF,CAAW,EACzD,OAAO,IAAIG,EAAAA,kBAAkB,CAC5B,IAAIC,qBAAmB,CACtB,iBAAkBL,CAAA,CAClB,EAKD,CACC,KAAKL,EAAM,CACV,GAAI,CAACH,EACJ,MAAM,IAAI,MACT,kEAAkEG,CAAI;AAAA;AAAA,+JAAA,EAMxE,OAAOO,EAAiB,KAAKP,CAAI,CAClC,CAAA,CACD,CACA,CACF,CACA,QACC,MAAM,IAAI,MACT,yCAAyCG,CAAS,4CAAA,CACnD,CAEH,CC3FO,MAAMQ,EAAoB,CAQhC,YACCC,EACAjD,EAIC,CAZF,KAAQ,oBAAsB,GAa7B,KAAK,KAAOiD,EACZ,KAAK,QAAUjD,EAAQ,QACvB,KAAK,qBAAuBA,EAAQ,qBACpC,KAAK,WAAaiD,EAAK,GACxB,CAEA,eAA4B,CAC3B,MAAO,IACR,CAEA,MAAM,8BACLC,EACAC,EACAC,EACC,CACD,MAAMC,EACLC,EAAAA,WAAWJ,CAAO,EAEnB,MAAMG,EAAW,mBAAmBF,CAAmB,EAEvD,MAAMI,EAAiB,CACtB,GAAG,KAAK,KACR,WAAY,KAAK,WACjB,QAAS,KAAK,QACd,eAAgB,EAChB,qBAAsB,KAAK,qBAC3B,MAAO,KAAK,KAAK,OAAS,GAC1B,UAAW,KAAK,KAAK,UAKrB,WAAY,GACZ,OAAQ,OACR,sBAAAH,CAAA,EAGD,aAAMC,EAAW,0BAA0BE,CAAc,EAClDF,CACR,CAEA,MAAM,eAAe,CACpB,OAAA9B,EACA,oBAAA4B,EACA,eAAAK,EACA,sBAAAJ,CAAA,EAME,CACF,MAAMC,EACLC,EAAAA,WAAW/B,EAAO,OAAO,EAE1B,MAAM8B,EAAW,mBAAmBF,CAAmB,EAEvD,MAAMI,EAA0C,CAC/C,GAAG,KAAK,KACR,WAAY,KAAK,WACjB,QAAS,KAAK,QACd,eAAAC,EACA,qBAAsB,KAAK,qBAC3B,MAAO,KAAK,KAAK,OAAS,GAC1B,WAAY,CAAC,CAAC,KAAK,KAAK,OACxB,sBAAAJ,EACA,sBAAuB,KAAK,KAAK,sBAAsB,GAAK,CAAA,EAC5D,qBAAsB,KAAK,KAAK,OAAS,CAAA,CAAC,EAG3C,aAAMC,EAAW,WAAWE,CAAc,EAEnCF,CACR,CAEA,oBACCI,EACAC,EACAC,EACC,CACGD,IAAY,KAAK,sBAIrB,KAAK,oBAAsBA,EAEvBD,EAAY,OAEfA,EAAY,SAAS,CAAC,EACtBA,EAAY,MAAMC,CAAO,EACzBD,EAAY,UAAU,CAAC,EAEnBE,GACHF,EAAY,MAAM;AAAA,CAAI,GAIvBA,EAAY,MAAM,GAAGC,CAAO;AAAA,CAAI,EAElC,CACD,CC7HO,MAAME,EAAevB,EAAK,KAAKwB,EAAG,QAAA,EAAW,uBAAuB,EAE3E,eAAsBC,GACrBC,EACC,CAMD,OALkB,MAAMC,GACvB,0FACA,aACAD,CAAA,CAGF,CAIA,eAAsBC,GACrBC,EACAC,EACAH,EACC,CACD,MAAMI,EAAe9B,EAAK,KAAKuB,EAAcM,CAAQ,EACrD,OAAK5B,EAAG,WAAW6B,CAAY,IAC9B7B,EAAG,cAAcsB,CAAY,EAC7B,MAAMQ,GAAWH,EAAWE,EAAcJ,CAAO,GAE3CM,GAAWF,CAAY,CAC/B,CAEA,eAAeC,GACdH,EACAK,EACAP,EACC,CAED,MAAMQ,GADW,MAAMR,EAAQ,aAAa,MAAME,CAAS,CAAC,GACpC,KAAM,UAAA,EACxBO,EAAU,GAAGF,CAAS,WACtBG,EAASnC,EAAG,kBAAkBkC,CAAO,EAC3C,OAAa,CACZ,KAAM,CAAE,KAAAE,EAAM,MAAAC,CAAA,EAAU,MAAMJ,EAAO,KAAA,EAIrC,GAHII,GACHF,EAAO,MAAME,CAAK,EAEfD,EACH,KAEF,CACAD,EAAO,MAAA,EACFA,EAAO,QACX,MAAM,IAAI,QAAQ,CAACrE,EAASC,IAAW,CACtCoE,EAAO,GAAG,SAAU,IAAM,CACzBnC,EAAG,WAAWkC,EAASF,CAAS,EAChClE,EAAQ,IAAI,CACb,CAAC,EACDqE,EAAO,GAAG,QAAUG,GAAa,CAChCtC,EAAG,WAAWkC,CAAO,EACrBnE,EAAOuE,CAAG,CACX,CAAC,CACF,CAAC,CAEH,CAEO,SAASP,GAAWhC,EAAcwC,EAAyB,CACjE,OAAO,IAAI,KAAK,CAACvC,EAAG,aAAaD,CAAI,CAAC,EAAeyC,WAASzC,CAAI,CAAC,CACpE,CClCO,MAAM0C,EAAoB,CAOhC,YACC9B,EACAjD,EAIC,CAZF,KAAQ,oBAAsB,GAa7B,KAAK,KAAOiD,EACZ,KAAK,QAAUjD,EAAQ,QACvB,KAAK,qBAAuBA,EAAQ,oBACrC,CAEA,eAA4B,CAC3B,MAAO,IACR,CAEA,MAAM,8BACLkD,EACAC,EACAC,EACC,CACD,IAAI4B,EACAC,EACAC,EAGJ,MAAMnB,EAAU,IAAIoB,4BACpB,GAAI,KAAK,KAAK,uBAAyB,uBAAwB,CAC9D,IAAIC,EAAqB,GACzBrB,EAAQ,iBAAiB,WACxBsB,GACI,CACJ,GAAID,EACH,OAKD,KAAM,CAAE,OAAAE,EAAQ,MAAAC,CAAA,EAAUF,EAAE,OAEtBG,EAAkB,KAAK,MAC5B,KAAK,IAAI,IAAM,IAAMF,EAAUC,CAAK,CAAA,EAErCH,EAAqBI,IAAoB,IAEzC,KAAK,oBACJ,QAAQ,OACR,yBAAyBA,CAAe,OACxCJ,CAAA,CAEF,CAAS,EAETJ,EAAY,MAAMS,GAAAA,wBAAwB,KAAK,KAAK,EAAE,EACtDP,EAA4B7C,EAAK,KAChCuB,EACA,8BAA8BoB,EAAU,OAAO,MAAA,EAEhDC,EAAe3C,EAAG,WAAW4C,CAAyB,EACnDb,GAAWa,CAAyB,EACpC,MAAMlB,GACNgB,EAAU,WACV,GAAGA,EAAU,OAAO,OACpBjB,CAAA,EAEHlD,EAAAA,OAAO,IACN,mCAAmCmE,GAAW,UAAU,EAAA,CAE1D,CAEAnE,SAAO,IAAI,uCAAuC,EAElD,MAAM6E,EAA6B,KAAK,KAAK,gBAC1C,OACA,MAAM5B,GAAuBC,CAAO,EAEjC4B,EAAiB,KAAK,KAAK,iBAAmB,GAC9CC,EAAQ,KAAK,KAAK,oBAAsB,GAExCC,EAAwB,KAAK,KAAK,sBAAsB,GAAK,CAAA,EAC7DC,EAAuB,KAAK,KAAK,OAAS,CAAA,EAE1CzC,EAAaC,EAAAA,WAA2CJ,CAAO,EAGrE,MAAMG,EAAW,YAAA,EAEjBxC,SAAO,IAAI,sBAAsB,EAEjC,MAAMkF,EAAuB,MAAMC,EAAAA,4BAClC,KAAK,sBAAA,CAAsB,EAG5B,aAAM3C,EAAW,mBAAmBF,CAAmB,EACvD,MAAME,EAAW,0BAA0B,CAC1C,WAAY0C,EAAqB,WACjC,UAAWA,EAAqB,UAChC,QAAS,KAAK,QACd,sBAAAF,EACA,qBAAAC,EACA,qBACC,KAAK,KAAK,sBAAwB,uBACnC,aAAcb,GAAiB,MAAMA,EAAc,YAAA,EACnD,2BACC,MAAMS,GAA4B,YAAA,EACnC,eAAgB,EAChB,qBAAsB,KAAK,qBAC3B,eAAAC,EACA,MAAAC,EACA,oBAAqB,KAAK,KAAK,oBAK/B,WAAY,GACZ,sBAAAxC,CAAA,CACA,EAGA8B,GACA,CAAC,KAAK,KAAK,sBAAsB,GACjC,CAAC5C,EAAG,WAAW4C,CAAyB,IAExCrE,SAAO,IAAI,qDAAqD,EAChEyB,EAAG,cACF4C,EACC,MAAMe,EAAAA,aAAa5C,EAAY,YAAY,CAAA,EAE7CxC,SAAO,IAAI,SAAS,GAGdwC,CACR,CAEA,MAAM,eAAe,CACpB,OAAA9B,EACA,oBAAA4B,EACA,eAAAK,EACA,sBAAAJ,CAAA,EAME,CACF,MAAMC,EAAaC,EAAAA,WAClB/B,EAAO,OAAA,EAGR,MAAM8B,EAAW,YAAA,EACjB,MAAM0C,EAAuB,MAAMC,EAAAA,4BAClC,KAAK,sBAAA,CAAsB,EAE5B,aAAM3C,EAAW,mBAAmBF,CAAmB,EACvD,MAAME,EAAW,WAAW,CAC3B,WAAY0C,EAAqB,WACjC,QAAS,KAAK,QACd,sBAAuB,KAAK,KAAK,sBAAsB,GAAK,CAAA,EAC5D,qBAAsB,KAAK,KAAK,OAAY,CAAA,EAC5C,eAAAvC,EACA,qBAAsB,KAAK,qBAC3B,eAAgB,KAAK,KAAK,iBAAmB,GAC7C,MAAO,KAAK,KAAK,oBAAsB,GAGvC,oBAAqB,KAAK,KAAK,oBAC/B,WAAY,CAAC,CAAC,KAAK,KAAK,OACxB,sBAAAJ,CAAA,CACA,EACD,MAAMC,EAAW,QAAA,EACVA,CACR,CAEA,MAAM,sBAAsB6C,EAAiC,CAC5D,MAAMC,EAAY,KAAK,sBAAA,EAEjBC,EAAU,IAAIC,kBACpB,IAAIC,EAAc,GACdlB,EAAqB,GACzB,OAAAgB,EAAQ,iBAAiB,WAAaf,GAAW,CAChD,GAAID,EACH,OAEDA,EAAqBC,EAAE,OAAO,WAAa,IAG3C,MAAMkB,EAAkB,KAAK,MAAMlB,EAAE,OAAO,QAAQ,EACpDiB,EACCjB,EAAE,OAAO,SAAWiB,GAAe,wBACpC,MAAM5C,EAAU,GAAG4C,EAAY,KAAA,CAAM,MAAMC,CAAe,IAC1D,KAAK,oBACJ,QAAQ,OACR7C,EACA0B,CAAA,CAEF,CAAC,EACM,MAAMoB,EAAAA,mBAAmBL,EAAqC,CACpE,SAAUC,EACV,gBAAiBF,CAAA,CACjB,CACF,CAEQ,uBAAwB,CAC/B,MAAMO,EAAoB,KAAK,KAAK,UAQpC,OAAOC,EAAAA,kBAAkBD,CAAiB,EACvCA,EACA,CACA,MAAO,KAAK,KAAK,MACjB,GAAIA,GAAqB,CAAA,EACzB,kBAAmB,CAClB,IACC,KAAK,KAAK,KACVA,GAAmB,mBAAmB,KACtCE,EAAAA,sBACD,GACC,KAAK,KAAK,IACVF,GAAmB,mBAAmB,IACtC,SACD,GAAIA,GAAmB,mBAAqB,CAAA,CAAC,CAC9C,CAEJ,CAEA,oBACChD,EACAC,EACAC,EACC,CACG,KAAK,KAAK,YAAciD,EAAa,MAAM,MAG3ClD,IAAY,KAAK,sBAIrB,KAAK,oBAAsBA,EAEvBD,EAAY,OAEfA,EAAY,SAAS,CAAC,EACtBA,EAAY,MAAMC,CAAO,EACzBD,EAAY,UAAU,CAAC,EAEnBE,GACHF,EAAY,MAAM;AAAA,CAAI,GAIvBA,EAAY,MAAM,GAAGC,CAAO;AAAA,CAAI,EAElC,CACD,CCpRA,eAAsBmD,GACrBC,EAEAC,EAAc,GACb,CAMD,MAAMC,EAAgB,GALC3E,EAAK,SAAS,QAAQ,KAAK,CAKX,GAAGyE,CAAwB,GAAG,QAAQ,GAAG,IAE1EG,EAAY,MAAMC,OAAO,CAC9B,OAAQF,EASR,cAAe,EAAA,CACf,EAED,OAAID,GAEHI,sBAAA,EAGMF,CACR,CAYA,eAAsBG,GACrBN,EACAO,EACAC,EACC,CAMD,MAAMC,GAL0B,MAAMC,GACrCV,EACAO,EACAC,CAAA,GAEgD,IAC/CG,GACA,IAAI,QAAerH,GAAY,CAE9BkC,EAAG,GAAGmF,EAAwB,CAAE,UAAW,EAAA,EAAS7C,GAAQ,CACvDA,EACH/D,EAAAA,OAAO,KACN,+CAA+C4G,CAAsB,GACrE7C,CAAA,EAGD/D,EAAAA,OAAO,KACN,sCAAsC4G,CAAsB,EAAA,EAG9DrH,EAAA,CACD,CAAC,CACF,CAAC,CAAA,EAEH,MAAM,QAAQ,IAAImH,CAAgB,CACnC,CAEA,eAAeC,GACdV,EACAO,EACAC,EACC,CACD,GAAI,CACH,MAAMI,EAAYpF,EAChB,YAAYgF,CAAW,EACvB,IAAKK,GAAYtF,EAAK,KAAKiF,EAAaK,CAAO,CAAC,EAE5CC,EAA0B,CAAA,EAChC,UAAWC,KAAYH,EACG,MAAMI,GAC9BhB,EACAO,EACAQ,CAAA,GAGAD,EAAwB,KAAKC,CAAQ,EAGvC,OAAOD,CACR,OAASvC,EAAG,CACXxE,OAAAA,EAAAA,OAAO,KAAK,8CAA8CwE,CAAC,EAAE,EAEtD,CAAA,CACR,CACD,CAEA,eAAeyC,GACdhB,EACAO,EACAU,EACC,CAED,GAAI,CADUzF,EAAG,UAAUyF,CAAY,EAC5B,cAEV,MAAO,GAGR,MAAMJ,EAAUtF,EAAK,SAAS0F,CAAY,EAC1C,GAAI,CAACJ,EAAQ,SAASb,CAAwB,EAE7C,MAAO,GAGR,MAAMkB,EAAQL,EAAQ,MACrB,IAAI,OAAO,QAAQb,CAAwB,SAAS,CAAA,EAErD,GAAI,CAACkB,EAGJ,MAAO,GAGR,MAAMC,EAAO,CAEZ,eAAgBD,EAAM,CAAC,EACvB,IAAKA,EAAM,CAAC,CAAA,EAGb,GAAI,MAAME,GAAiBD,EAAK,IAAKA,EAAK,cAAc,EAEvD,MAAO,GAGR,MAAME,EAAa,KAAK,IAAA,EAAQd,EAEhC,OADgB/E,EAAG,SAASyF,CAAY,EAC5B,MAAM,QAAA,EAAYI,CAK/B,CAEA,eAAeD,GAAiBE,EAAaC,EAAwB,CAOpE,KAAM,CAACC,CAAe,EAAI,MAAM,IAAI,QACnC,CAAClI,EAASC,IAAW,CACpBkI,GAAG,KACF,CACC,IAAAH,EACA,KAAMC,EAEN,MAAO,EAAA,EAER,CAACzD,EAAU4D,IAA6B,CACnC5D,EACHvE,EAAOuE,CAAG,EAEVxE,EAAQoI,CAAS,CAEnB,CAAA,CAEF,CAAA,EAED,MACC,CAAC,CAACF,GACFA,EAAgB,MAAQF,GACxBE,EAAgB,UAAYD,CAE9B,CCvLA,eAAsBI,GACrBC,EACAC,EACAC,EACC,CACD,MAAMC,EACLD,IAAa,QAIV,WACA,MACJtG,EAAG,YAAYoG,EAAeC,EAAaE,CAAI,CAChD,CAOA,eAAsBC,GAAkCH,EAAqB,CAC5E,GAAI,CACWrG,EAAG,UAAUqG,CAAW,EAC5B,kBACTrG,EAAG,WAAWqG,CAAW,CAE3B,MAAQ,CAER,CACD,CAOA,SAASI,GAAkBC,EAAaC,EAAiB,CACxD,OAAOA,EAAO,OAAQC,GAAU,CAC/B,MAAMC,EAAmB9G,EAAK,QAAQ6G,EAAM,QAAQ,EAC9CE,EAAiB/G,EAAK,KAAK2G,EAAK3G,EAAK,GAAG,EAC9C,OAGC8G,IAAqBH,GACrBG,EAAiB,WAAWC,CAAc,CAE5C,CAAC,CACF,CA2EA,MAAMC,EAA+B,CACpC,iBAAkB,GAClB,oBAAqB,GACrB,cAAe,GACf,cAAe,UACf,gBAAiB,eACjB,uBAAwB,GACxB,WAAY,EACb,EACMC,GAAuC,CAC5C,iBAAkBD,EAAiB,iBACnC,oBAAqBA,EAAiB,oBACtC,cAAeA,EAAiB,cAChC,cAAeA,EAAiB,cAChC,gBAAiBA,EAAiB,gBAClC,0BAA2B,CAACA,EAAiB,uBAC7C,OAAQ,GACR,SAAU,GACX,EAEME,EAAwC,CAC7C,kBAAmB,GACnB,mBAAoB,EACrB,EAmBO,SAASC,GACfC,EACAzJ,EACS,CACT,KAAM,CAAE,KAAA0J,EAAM,KAAAC,EAAM,KAAA3I,EAAM,SAAA4I,EAAU,OAAAC,GAAW7J,EAEzC8J,EAAY,IAAIC,EAAAA,UAAUV,CAAgB,EAG1CW,GAAgC,IAAM,CAC3C,GAAI,CACH,OAAOF,EAAU,MAAML,EAAY,EAAI,CACxC,MAAQ,CACP,MAAM,IAAI,MAAM,+CAA+C,CAChE,CACD,GAAA,EAGMQ,EAAoC,CACzC,OAAQ,CACP,CACC,cAAeL,EAAS,IAAKM,IAAa,CACzC,QAAS,CAAA,EACT,KAAM,CACL,aAAc,iBAAiBC,GAC9B9H,EAAK,SAASrC,EAAQ,WAAYkK,EAAQ,QAAQ,CAAA,CAClD,GACD,cAAeA,EAAQ,OAAA,CACxB,EACC,CAAA,CACH,EAED,KAAM,CACL,KAAAR,EAIA,KAAM,GAAGC,CAAI,IAAI3I,CAAI,GACrB,kBAAmB,MAAA,CACpB,EAID,IAAIoJ,EAAiBJ,GAAQ,KAAMK,GAA0B,CAAC,CAACA,GAAG,OAAO,EACzE,GAAID,EAAgB,CACnB,MAAME,EAAiBF,EAAe,IAAI,GAAG,QAC7C,GAAIE,IAAmB,OACtB,MAAM,IAAI,MACT,uIAAA,EAGF,GAAWA,IAAmB,IAC7B,MAAM,IAAI,MACT,uIACyDA,CAAc,IAAA,CAG1E,CACIF,IAAmB,SACtBA,EAAiB,CAChB,QAAS,CAAA,EACT,KAAM,CAAE,QAAS,GAAA,CAAI,EAEtBJ,EAAO,KAAKI,CAAc,GAI3B,IAAIG,EAAmBH,EAAe,SAAS,KAC7CC,GACA,CAAC,CAACA,GAAG,WAAaA,IAAI,IAAI,GAAG,OAAS,YAAA,EAEpCE,IAAqB,SACxBA,EAAmB,CAClB,UAAW,CAAA,EACX,KAAM,CAAE,KAAM,YAAA,CAAa,EAGxBH,EAAe,UAAY,SAC9BA,EAAe,QAAU,CAAA,GAG1BA,EAAe,QAAQ,KAAKG,CAAgB,GAI7C,IAAIC,EAAiBD,EAAiB,WAAW,KAC/CF,GAA0B,CAAC,CAACA,GAAG,OAAA,EAE7BG,IAAmB,SACtBA,EAAiB,CAAE,QAAS,EAAC,EAEzBD,EAAiB,YAAc,SAClCA,EAAiB,UAAY,CAAA,GAG9BA,EAAiB,UAAU,KAAKC,CAAc,GAI/C,MAAMC,EAAqBD,EAAe,SAAS,UACjDH,GAA0B,CAAC,CAACA,GAAG,QAAUA,IAAI,IAAI,GAAG,OAASX,CAAA,GAI3De,IAAuB,QAAaA,EAAqB,KACxDD,EAAe,UAAY,SAC9BA,EAAe,QAAU,CAAA,GAG1BA,EAAe,QAAQ,KAAKP,CAAa,GAI1C,IAAIS,EAAoBN,EAAe,SAAS,KAC9CC,GACA,CAAC,CAACA,GAAG,WAAaA,IAAI,IAAI,GAAG,OAAS,YAAA,EAuBxC,GArBIK,IAAsB,SACzBA,EAAoB,CACnB,UAAW,CAAA,EACX,KAAM,CAAE,KAAM,YAAA,CAAa,EAGxBN,EAAe,UAAY,SAC9BA,EAAe,QAAU,CAAA,GAG1BA,EAAe,QAAQ,KAAKM,CAAiB,IAK7CA,EAAkB,WAAW,UAC3BL,GACA,CAAC,CAACA,GAAG,eAAiBA,IAAI,IAAI,GAAG,OAASX,CAAA,GACvC,IAGoB,EAAG,CAC5B,MAAMiB,EAAuC,CAC5C,cAAe,CACd,CACC,OAAQ,CAAA,EACR,KAAM,CAAE,EAAG,GAAA,CAAI,CAChB,EAED,KAAM,CACL,KAAAjB,EACA,KAAM,qCACN,YAAa,mBACb,mBAAoB,SACpB,YAAaA,EACb,WAAYG,CAAA,CACb,EAGGa,EAAkB,YAAc,SACnCA,EAAkB,UAAY,CAAA,GAG/BA,EAAkB,UAAU,KAAKC,CAAgB,CAClD,CAIA,MAAMC,EADa,IAAIC,EAAAA,WAAWvB,EAAiB,EAC5B,MAAMU,CAAM,EAGnC,GAAI,CACHF,EAAU,MAAMc,EAAK,EAAI,CAC1B,MAAQ,CACP,MAAM,IAAI,MACT,6DAAA,CAEF,CAEA,OAAOA,CACR,CAgBO,SAASE,GACfC,EACA/K,EACS,CACT,KAAM,CAAE,KAAA0J,EAAM,SAAAE,CAAA,EAAa5J,EAErBgL,EAA6B,CAAA,EAEnC,IAAIC,EAAUF,EACVG,EAAOC,EAAM,UAAUF,EAASD,EAAQzB,CAAiB,EAE7D,GAAI2B,IAAS,QAAaF,EAAO,OAChC,MAAM,IAAI,MAAM,+CAA+C,EAIhE,IAAII,EAAqBD,EAAM,mBAAmBD,EAAM,CAAC,gBAAgB,CAAC,EAE1E,GACCE,IAAuB,QACvBA,EAAmB,WAAa,OAC/B,CACD,MAAMC,EAAQF,EAAM,OAAOF,EAAS,CAAC,gBAAgB,EAAG,CAAA,EAAI,EAAE,EAC9DA,EAAUE,EAAM,WAAWF,EAASI,CAAK,EAEzCH,EAAOC,EAAM,UAAUF,EAAS,CAAA,EAAI1B,CAAiB,EACrD6B,EAAqBD,EAAM,mBAAmBD,EAAO,CACpD,gBAAA,CACA,CACF,CAGA,MAAMI,EAAqBF,GAAoB,UAAU,UACvDG,GAAUJ,EAAM,mBAAmBI,EAAO,CAAC,MAAM,CAAC,GAAG,QAAU7B,CAAA,EAIjE,GAAI4B,IAAuB,QAAaA,EAAqB,EAAG,CAC/D,MAAME,EAAkC,CACvC,KAAA9B,EACA,KAAM,MACN,QAAS,SACT,KAAM,KACN,aAAcE,EAAS,OAAO,CAAC6B,EAAKvC,KACnCuC,EAAIvC,EAAM,OAAO,EAAI,uBAAuBiB,GAC3C9H,EAAK,SAASrC,EAAQ,aAAckJ,EAAM,QAAQ,CAAA,CAClD,GACMuC,GACL,CAAA,CAA0B,CAAA,EAIxBC,EAAgBN,GAAoB,UAAU,QAAU,EAExDC,EAAQF,EAAM,OACnBF,EACA,CAAC,iBAAkBS,CAAa,EAChCF,EACA,CACC,kBAAmB,CAClB,aAAc,GACd,QAAS,EACT,IAAK;AAAA,CAAA,CACN,CACD,EAGDP,EAAUU,GAAgBV,EAASI,CAAK,CACzC,CAEA,OAAOJ,CACR,CAQA,eAAsBW,GAAmB,CACxC,KAAAlC,EACA,KAAAmC,EACA,KAAAlC,EACA,KAAA3I,EACA,IAAAgI,EACA,OAAAC,EACA,OAAAY,EAAS,eACV,EAAc,CACb,MAAMD,EAAWb,GAAkBC,EAAKC,CAAM,EACxC6C,EAA2B,CAAA,EAGjC,GAAID,EAAK,SAAS,UAAU,EAAG,CAC9B,MAAME,EAAiC,sBACjCC,EAAyB3J,EAAK,KACnC2G,EACA+C,CAAA,EAKD,GAAI,CAACzJ,EAAG,WAAW0J,CAAsB,GACxC,GAAI1J,EAAG,WAAWD,EAAK,QAAQ2J,CAAsB,CAAC,EACrD1J,EAAG,cACF0J,EACA;AAAA;AAAA,WAAA,UAESH,EAAK,QAAU,EACzB,MAAM,IAAI,MACT,0GAAA,EAKH,GAAIvJ,EAAG,WAAW0J,CAAsB,EAAG,CAC1C,MAAMC,EAAW3J,EAAG,aAAa0J,EAAwB,MAAM,EACzDE,EAAa1C,GAAqByC,EAAU,CACjD,KAAAvC,EACA,KAAAC,EACA,KAAA3I,EACA,WAAYgI,EACZ,SAAAY,EACA,OAAAC,CAAA,CACA,EACDvH,EAAG,cAAc0J,EAAwBE,CAAU,CACpD,CAEAJ,EAAe,KAAKC,CAA8B,CACnD,CAGA,GAAIF,EAAK,SAAS,QAAQ,EAAG,CAC5B,MAAMM,EAA+B,sBAC/BC,EAAuB/J,EAAK,KACjC2G,EACAmD,CAAA,EAKD,GAAI,CAAC7J,EAAG,WAAW8J,CAAoB,GACtC,GAAI9J,EAAG,WAAWD,EAAK,QAAQ+J,CAAoB,CAAC,EACnD9J,EAAG,cACF8J,EACA;AAAA;AAAA,EAAA,UAESP,EAAK,QAAU,EACzB,MAAM,IAAI,MACT,2GAAA,EAKH,GAAIvJ,EAAG,WAAW8J,CAAoB,EAAG,CACxC,MAAMnB,EAAU3I,EAAG,aAAa8J,EAAsB,OAAO,EACvDC,EAAcvB,GAAmBG,EAAS,CAC/C,KAAAvB,EACA,aAAcV,EACd,SAAAY,CAAA,CACA,EAGGyC,IAAgBpB,IACnB3I,EAAG,cAAc8J,EAAsBC,CAAW,EAClDP,EAAe,KAAKK,CAA4B,EAElD,CACD,CAEA,OAAOL,CACR,CAQA,eAAsBQ,GAAqB5C,EAAcV,EAAa,CACrE,MAAMgD,EAAyB3J,EAAK,KAAK2G,EAAK,qBAAqB,EAEnE,GAAI1G,EAAG,WAAW0J,CAAsB,EAAG,CAC1C,MAAMC,EAAW3J,EAAG,aAAa0J,EAAwB,MAAM,EACzDlC,EAAY,IAAIC,EAAAA,UAAUV,CAAgB,EAE1CW,GAAgC,IAAM,CAC3C,GAAI,CACH,OAAOF,EAAU,MAAMmC,EAAU,EAAI,CACtC,MAAQ,CACP,MAAM,IAAI,MACT,+CAAA,CAEF,CACD,GAAA,EASMzB,EAPiBR,EAAO,KAC5BK,GAA0B,CAAC,CAACA,GAAG,OAAA,GAEQ,SAAS,KAChDA,GACA,CAAC,CAACA,GAAG,WAAaA,IAAI,IAAI,GAAG,OAAS,YAAA,GAEC,WAAW,KAClDA,GAA0B,CAAC,CAACA,GAAG,OAAA,EAE3BI,EAAqBD,GAAgB,SAAS,UAClDH,GAA0B,CAAC,CAACA,GAAG,QAAUA,IAAI,IAAI,GAAG,OAASX,CAAA,EAG/D,GAAIe,IAAuB,QAAaA,GAAsB,EAAG,CAChED,EAAgB,QAAS,OAAOC,EAAoB,CAAC,EAGrD,MAAMG,EADa,IAAIC,EAAAA,WAAWvB,EAAiB,EAC5B,MAAMU,CAAM,EAEnC,GAAI,CACHF,EAAU,MAAMc,EAAK,EAAI,CAC1B,MAAQ,CACP,MAAM,IAAI,MACT,6DAAA,CAEF,CAGCA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA,YAEAtI,EAAG,WAAW0J,CAAsB,EAEpC1J,EAAG,cAAc0J,EAAwBpB,CAAG,CAE9C,CACD,CAEA,MAAMwB,EAAuB/J,EAAK,KAAK2G,EAAK,qBAAqB,EAEjE,GAAI1G,EAAG,WAAW8J,CAAoB,EAAG,CACxC,MAAMpB,EAA6B,CAAA,EAE7BC,EAAU3I,EAAG,aAAa8J,EAAsB,OAAO,EACvDlB,EAAOC,EAAM,UAAUF,EAASD,EAAQzB,CAAiB,EAE/D,GAAI2B,IAAS,QAAaF,EAAO,OAChC,MAAM,IAAI,MAAM,+CAA+C,EAOhE,MAAMM,EAJqBH,EAAM,mBAAmBD,EAAM,CACzD,gBAAA,CACA,GAE8C,UAAU,UACvDK,GAAUJ,EAAM,mBAAmBI,EAAO,CAAC,MAAM,CAAC,GAAG,QAAU7B,CAAA,EAGjE,GAAI4B,IAAuB,QAAaA,GAAsB,EAAG,CAChE,MAAMD,EAAQF,EAAM,OACnBF,EACA,CAAC,iBAAkBK,CAAkB,EACrC,OACA,CACC,kBAAmB,CAClB,aAAc,GACd,QAAS,EACT,IAAK;AAAA,CAAA,CACN,CACD,EAGKiB,EAAOZ,GAAgBV,EAASI,CAAK,EACvCkB,IAAS;AAAA;AAAA,GACZjK,EAAG,WAAW8J,CAAoB,EAElC9J,EAAG,cAAc8J,EAAsBG,CAAI,CAE7C,CACD,CACD,CAEA,SAASZ,GAAgBV,EAAiBI,EAAqB,CAC9D,MAAML,EAA6B,CAAA,EAC7BuB,EAAOpB,EAAM,WAAWF,EAASI,CAAK,EAM5C,GAJAL,EAAO,OAAS,EAEhBG,EAAM,UAAUoB,EAAMvB,EAAQzB,CAAiB,EAE3CyB,EAAO,OAAQ,CAClB,MAAMwB,EAAkBxB,EACtB,IAAKpK,IACE,CACN,QAASuK,EAAM,oBAAoBvK,EAAM,KAAK,EAC9C,OAAQA,EAAM,OACd,OAAQA,EAAM,OACd,SAAU2L,EAAK,MACd,KAAK,IAAI,EAAG3L,EAAM,OAAS,EAAE,EAC7B,KAAK,IAAI2L,EAAK,OAAQ3L,EAAM,OAASA,EAAM,OAAS,EAAE,CAAA,CACvD,EAED,EACA,IACCA,GACA,GAAGA,EAAM,OAAO,OAAOA,EAAM,MAAM,IAAIA,EAAM,MAAM,KAAKA,EAAM,QAAQ,GAAA,EAEnE6L,EAAiBpB,EAAM,IAC3BqB,GAAS,MAAMA,EAAK,MAAM,IAAIA,EAAK,MAAM,OAAOA,EAAK,OAAO,GAAA,EAE9D,MAAM,IAAI,MACT;AAAA;AAAA,kBAE4DD,EAAe,KACzE;AAAA,CAAA,CACA;AAAA;AAAA,mBAAwBD,EAAgB,KAAK;AAAA,CAAI,CAAC,EAAA,CAEtD,CAEA,OAAOD,CACR,CAEA,SAASpC,GAAYwC,EAAiB,CACrC,OAAOA,EAAQ,WAAWtK,EAAK,IAAKA,EAAK,MAAM,GAAG,CACnD,CCvnBO,MAAMuE,EAAe,CAC3B,MAAO,CAAE,KAAM,QAAS,SAAUgG,EAAAA,YAAY,KAAA,EAC9C,OAAQ,CAAE,KAAM,SAAU,SAAUA,EAAAA,YAAY,IAAA,EAChD,MAAO,CAAE,KAAM,QAAS,SAAUA,EAAAA,YAAY,KAAA,CAC/C,EAWA,eAAsBC,GAAsBC,EAAuB,CAClE,GAAI,CAKH,MAAMC,EAAcC,GAAMF,CAAW,EACnC,MAAM,0CAA0C,EAChD,WAAW,UAAW,CACtB,SAAU,iBACV,QAAS,CAAC,SAAU,gBAAiB,gBAAgB,EACrD,aAAc,EAAA,CACd,EACA,OAAO,UAAW,CAClB,SAAU,4CACV,KAAM,SACN,QAAS,eAAA,CACT,EACA,OAAO,OAAQ,CACf,SAAU,kCACV,KAAM,SACN,QAAS,IAAA,CACT,EACA,OAAO,WAAY,CACnB,SACC,qEACD,KAAM,QAAA,CACN,EACA,OAAO,MAAO,CACd,SAAU,sBACV,KAAM,SACN,QAASnG,EAAAA,sBACT,QAASsG,EAAAA,oBAAA,CACT,EACA,OAAO,KAAM,CACb,SAAU,4BACV,KAAM,SACN,QAAS,QAAA,CACT,EAGA,OAAO,QAAS,CAChB,SACC,kGACD,KAAM,QACN,OAAQ,GACR,OAAQC,EAAAA,gCAAA,CACR,EACA,OAAO,uBAAwB,CAC/B,SACC,gIACD,KAAM,QACN,OAAQ,GACR,OAAQA,EAAAA,gCAAA,CACR,EACA,OAAO,YAAa,CACpB,SACC,sGACD,KAAM,QACN,MAAO,EACP,MAAO,GACP,OAAQC,EAAAA,sBAAA,CACR,EACA,OAAO,2BAA4B,CACnC,SACC,iHACD,KAAM,SACN,MAAO,EACP,MAAO,GACP,OAAQA,EAAAA,sBAAA,CACR,EACA,OAAO,QAAS,CAChB,SAAU,yBACV,KAAM,UACN,QAAS,EAAA,CACT,EACA,OAAO,YAAa,CACpB,SAAU,wBACV,KAAM,QAAA,CACN,EACA,OAAO,oCAAqC,CAC5C,SACC,0HACD,KAAM,UACN,QAAS,EAAA,CACT,EACA,OAAO,yBAA0B,CACjC,SACC,4DACD,KAAM,SACN,QAAS,uBACT,QAAS,CACR,uBACA,8BACA,wCACA,2BAAA,CACD,CACA,EACA,OAAO,yBAA0B,CACjC,SAAU,qDACV,KAAM,UACN,OAAQ,EAAA,CACR,EACA,OAAO,oBAAqB,CAC5B,SACC,qFACD,KAAM,UACN,QAAS,EAAA,CACT,EAEA,OAAO,QAAS,CAChB,SAAU,4CACV,KAAM,UACN,QAAS,GACT,OAAQ,EAAA,CACR,EACA,OAAO,YAAa,CACpB,SAAU,qCACV,KAAM,SACN,QAAS,OAAO,OAAOvG,CAAY,EAAE,IACnCwG,GAAcA,EAAU,IAAA,EAE1B,QAAS,QAAA,CACT,EACA,OAAO,QAAS,CAChB,SACC,yEACD,KAAM,UACN,QAAS,EAAA,CACT,EACA,OAAO,aAAc,CACrB,SAAU,gQACV,KAAM,QAAA,CACN,EACA,OAAO,kBAAmB,CAC1B,SACC;AAAA,uHACD,KAAM,UACN,QAAS,EAAA,CACT,EACA,OAAO,qBAAsB,CAC7B,SACC,4FACD,KAAM,UACN,QAAS,GAET,OAAQ,EAAA,CACR,EACA,OAAO,wBAAyB,CAChC,SACC,uPAGD,KAAM,UACN,QAAS,EAAA,CACT,EACA,OAAO,SAAU,CACjB,SAAU,iBACV,KAAM,UACN,QAAS,EAAA,CACT,EACA,OAAO,sCAAuC,CAC9C,SACC,qRAID,KAAM,SAIN,QAAS,CAAC,GAAI,SAAU,UAAU,EAClC,OAASzI,GACRA,IAAU,GAAK,CAAC,SAAU,UAAU,EAAI,CAACA,CAAK,CAAA,CAC/C,EACA,OAAO,wBAAyB,CAChC,SAAU,iDACV,KAAM,SAAA,CACN,EACA,UACA,sCACA,uBAAA,EAEA,OAAO,4BAA6B,CACpC,SACC,gOAID,KAAM,SACN,OAASA,GAAmBA,GAAS0I,EAAAA,KAAA,EAAO,OAAS,CAAA,CACrD,EACA,OAAO,oCAAqC,CAC5C,SAAU,4CACV,KAAM,UACN,QAAS,GAET,OAAQ,EAAA,CACR,EACA,OAAO,OAAQ,CACf,SACC,sIACD,KAAM,SACN,QAAS,CAAC,kBAAmB,wBAAwB,EAErD,OAAQ,EAAA,CACR,EACA,eAAe,EAAK,EACpB,cAAA,EACA,MAAM,MAAOpK,GAAS,CAOtB,GANIA,EAAK,wBAAwB,IAAM,KACtCA,EAAK,wBAAwB,EAC5B,4BACDA,EAAK,qBAA0B,6BAG5BA,EAAK,KAAO,QAAa,CAACnB,GAAqBmB,EAAK,EAAE,EACzD,GAAI,CAEH,IAAI,IAAIA,EAAK,EAAE,CAChB,MAAQ,CACP,MAAM,IAAI,MACT,oIAAA,CAEF,CAGD,GAAIA,EAAK,UAAU,IAAM,QAAaA,EAAK,UAAU,IAAM,GAC1D,GAAI,CACH,IAAI,IAAIA,EAAK,UAAU,CAAC,CACzB,MAAQ,CACP,MAAM,IAAI,MACT,qBAAqBA,EAAK,UAAU,CAAC,oFAAA,CAEvC,CAGD,GAAIA,EAAK,YAAY,EAAG,CACvB,IAAIqK,EAAiB,GACrB,GAAI,CAEHA,EADuBhL,EAAG,SAASW,EAAK,YAAY,CAAC,EACrB,YAAA,CACjC,MAAQ,CACPqK,EAAiB,EAClB,CAEA,GAAI,CAACA,EACJ,MAAM,IAAI,MACT,wDAAwDrK,EAAK,YAAY,CAAC,IAAA,CAG7E,CAEA,GAAIA,EAAK,2BAA2B,IAAM,OAAW,CAEpD,GADmBA,EAAK,EAAE,CAAC,IACR,SAClB,MAAM,IAAI,MACT,yFAAA,EAGF,GAAIA,EAAK,2BAA2B,GAAK,EACxC,MAAM,IAAI,MACT,iFAAA,CAGH,CAEA,GAAIA,EAAK,mCAAmC,IAAM,GAAM,CACvD,GAAIA,EAAK,OAAY,OAAW,CAC/B,GAAIA,EAAK,wBAAwB,IAAM,OACtC,MAAM,IAAI,MACT,kGAAA,EAGF,GAAI,sBAAuBA,EAC1B,MAAM,IAAI,MACT,qEAAA,EAGF,GAAIA,EAAK,YAAY,IAAM,OAC1B,MAAM,IAAI,MACT,sGAAA,CAGH,MAGEA,EAAK,wBAAwB,IAC7B,4BAEAA,EAAK,KAAU,yBAEfA,EAAK,KAAU,kBAKjB,MAAMsK,EAAStK,EAAK,OAAyB,CAAA,EAEzCA,EAAK,iBAAsB,IAC9BsK,EAAM,KAAK,iBAAiB,EAGzBtK,EAAK,mCAAmC,IAAM,IACjDsK,EAAM,KAAK,eAAe,EAG3BtK,EAAK,MAAWsK,CACjB,SACKtK,EAAK,OAAY,OACpB,MAAM,IAAI,MACT,uEAAA,EAKH,MAAO,EACR,CAAC,EAEF8J,EAAY,KAAKA,EAAY,eAAe,EAC5C,MAAM9J,EAAO,MAAM8J,EAAY,KAEzBS,EAAUvK,EAAK,EAAE,CAAC,EAEnB,CAAC,gBAAiB,SAAU,gBAAgB,EAAE,SAASuK,CAAO,IAClET,EAAY,SAAA,EACZ,QAAQ,KAAK,CAAC,GAGf,MAAMU,EAAU,CACf,GAAGxK,EACH,QAAAuK,EACA,MAAO,CAAC,GAAIvK,EAAK,OAAS,CAAA,EAAK,GAAIA,EAAK,WAAW,GAAK,EAAG,EAC3D,uBAAwB,CACvB,GAAIA,EAAK,sBAAsB,GAAK,CAAA,EACpC,GAAIA,EAAK,0BAA0B,GAAK,CAAA,CAAC,CAC1C,EAGKyK,EAAY,MAAMC,GAAOF,CAAO,EAClCC,IAAc,QAEjB,QAAQ,KAAK,CAAC,EAGf,MAAME,GAAqB,IAAM,CAGhC,IAAIC,EAEJ,MAAO,UAAY,CACdA,IAAqB,SACxBA,EAAmBH,EAAU,OAAO,YAAY,EAAA,GAEjD,MAAMG,EACN,QAAQ,KAAK,CAAC,CACf,CACD,GAAA,EAOA,QAAQ,GAAG,SAAUD,CAAiB,EACtC,QAAQ,GAAG,UAAWA,CAAiB,CACxC,OAASvI,EAAG,CACX,GAAI,EAAEA,aAAa,OAClB,MAAMA,EAGP,GADc,QAAQ,KAAK,SAAS,SAAS,EAE5CyI,EAAAA,kBAAkBzI,CAAC,MACb,CACN,MAAM0I,EAAe,CAAA,EACrB,IAAIC,EAAe3I,EACnB,GACC0I,EAAa,KAAKC,EAAa,OAAO,EACtCA,EAAeA,EAAa,YACpBA,aAAwB,OACjC,QAAQ,MACP,UAAYD,EAAa,KAAK,cAAc,EAAI,SAAA,CAElD,CACA,QAAQ,KAAK,CAAC,CACf,CACD,CAgEA,MAAME,EAAQC,GACb,QAAQ,OAAO,MAAQ,UAAYA,EAAO,UAAYA,EAEjDC,GAAOD,GACZ,QAAQ,OAAO,MAAQ,UAAUA,CAAI,UAAYA,EAE5CE,EAAUF,GACf,QAAQ,OAAO,MAAQ,UAAUA,CAAI,UAAYA,EAE5CG,GAAaH,GAClB,QAAQ,OAAO,MAAQ,WAAWA,CAAI,UAAYA,EAYnD,eAAsBP,GAAO1K,EAAgD,CAC5E,IAAIqL,EACAjL,EAEJ,MAAMkL,MAGE,IAmCR,GA7BItL,EAAK,YAAc,SAClBA,EAAK,YAAc,KAItBA,EAAO,CAAE,GAAGA,EAAM,UAAW,QAAQ,KAAI,GAE1CA,EAAOuL,EAAAA,iBAAiBvL,CAAI,GAGzBA,EAAK,uBAAyB,SACjCA,EAAK,qBAAuB,wBAIzBA,EAAK,QACRA,EAAK,UAAY,QACjB,OAAOA,EAAK,OAMTA,EAAK,MACRA,EAAK,UAAY,QACPA,EAAK,YAAc,UAC7BA,EAAK,MAAQ,IAGVA,EAAK,UAAW,CACnB,MAAMwL,EAAW,OAAO,OAAO7H,CAAY,EAAE,KAC3C8H,GAAMA,EAAE,OAASzL,EAAK,SAAA,EACrB,SACHpC,EAAAA,OAAO,uBAAuB4N,CAAQ,CACvC,CAIA,MAAME,EACL9K,EAAG,SAAA,IAAe,QAEf,OACA,KAAM,QAAO,QAAQ,EACpB,KAAM+K,GAAMA,EAAE,SAAS,EACvB,MAAM,IAAM,CACZ/N,EAAAA,OAAO,KACN,8GAAA,CAKF,CAAC,EACCgO,EAAkB,IAAIC,GAAAA,uBAAuBH,CAAe,EAElE,IAAII,EAAiB,GACjBC,EAAiB,GAErBnO,OAAAA,EAAAA,OAAO,IAAI,0BAA0B,EAE9Bd,GAAY,CAClB,KAAMkD,EAAK,KACX,OAAQ,MAAO9C,EAAgBa,IAAiB,CAC/C,MAAM2I,EAAO,YACPsF,EAAY,UAAUtF,CAAI,IAAI3I,CAAI,GAClCkO,EAAUjM,EAAK,UAAU,GAAKgM,EAE9BE,EACLlM,EAAK,UAAY,SACdA,EAAK,yBAA2B,EAChC,EACEmM,EACLnM,EAAK,UAAY,SAGdkM,EAAoB,EACpBA,EAEEE,EAAuB,KAAK,MACjC,OAAO,iBAAmBD,CAAA,EAarBE,EAAuB,wBACvBrI,EAAY,MAAMJ,GACvByI,CAAA,EAEDzO,EAAAA,OAAO,MAAM,iCAAiCoG,EAAU,IAAI,EAAE,EAE9D,MAAMsI,EAAgB,wCAGhBC,EAAc,0BACd7G,EAActG,EAAK,KAAK,QAAQ,IAAA,EAAOmN,CAAW,EAOxD,GALA,MAAM1G,GAAkCH,CAAW,EAK/C1F,EAAK,QAAUA,EAAK,iCAAkC,CACzD,MAAMwF,GACLxB,EAAU,KACV0B,EACA,QAAQ,QAAA,EAGT,MAAM8G,EAAsB,CAC3B,SAAUpN,EAAK,KAAK,IAAKA,EAAK,IAAKmN,CAAW,EAC9C,QAAS,GAAA,EAGV,GAAI,CAEH,MAAMlD,GAAqBiD,EAAe,QAAQ,IAAA,CAAK,EAEvD,MAAMG,EACL,OAAOzM,EAAK,QAAW,SACpBA,EAAK,OACL,OACE6I,EAAiB,MAAMF,GAAmB,CAC/C,KAAM2D,EACN,KAAA5F,EACA,KAAA3I,EACA,KAAMiC,EAAK,iCACX,IAAK,QAAQ,IAAA,EACb,OAAQ,CACPwM,EACA,GAAIxM,EAAK,sBAAsB,GAAK,CAAA,EACpC,GAAIA,EAAK,OAAS,CAAA,CAAC,EAEpB,OAAQyM,GAAe,MAAA,CACvB,EAGK7D,EAAO5I,EAAK,iCACZ0M,EAAY9D,EAAK,SAAS,QAAQ,EAClC+D,EAAc/D,EAAK,SAAS,UAAU,EAE5C,QAAQ,IAAI,EAAE,EACd,QAAQ,IAAIoC,EAAK,gCAAgC,CAAC,EAClD,QAAQ,IACPI,GAAU,sBAAsB,EAC/BvC,EAAe,KAAK,GAAG,CAAA,EAEzB,QAAQ,IACPuC,GAAU,0BAA0B,EACnC,0BACAD,EACCD,GACC,6EAAA,CACD,CACD,EAEF,QAAQ,IAAI,EAAE,EAEVwB,IACH,QAAQ,IAAI1B,EAAK,gCAAgC,CAAC,EAClD,QAAQ,IACP,mEAAA,EAED,QAAQ,IACP,aAAaA,EAAK,WAAW,CAAC,iBAAiBA,EAC9C,QAAA,CACA,2BAAA,EAEF,QAAQ,IACP,uDAAA,EAED,QAAQ,IACP,gBAAgBG,EACfmB,CAAA,CACA,qBAAA,EAEF,QAAQ,IAAI,8BAA8B,EAC1C,QAAQ,IACP,oFAAA,EAED,QAAQ,IACP,6DAAA,EAEGK,GACH,QAAQ,IAAI,EAAE,GAIZA,IACH,QAAQ,IAAI3B,EAAK,wBAAwB,CAAC,EAC1C,QAAQ,IACP,gBAAgBG,EACfmB,CAAA,CACA,sCAAA,EAEF,QAAQ,IAAI,yCAAyC,EACrD,QAAQ,IACP,oFAAA,EAED,QAAQ,IACP,6DAAA,GAIF,QAAQ,IAAI,EAAE,CACf,OAAS3O,EAAO,CACf,MAAM,IAAI,MAAM,6BAA8B,CAC7C,MAAOA,CAAA,CACP,CACF,CACD,CAIA,MAAMiP,GAAcxN,EAAK,QAAQ4E,EAAU,IAAI,EAGzC6I,GADkB,EAAI,GAAK,GAAK,GAAK,IAK3C1I,GACCkI,EACAQ,GACAD,EAAA,EAKD,MAAMzM,EAAwBf,EAAK,KAAK4E,EAAU,KAAM,UAAU,EAClE8I,EAAAA,UAAU3M,CAAqB,EAE/B,MAAM4M,GAA8B,CACnC,YAOA,MACA,MAAA,EAGD,UAAWC,KAAcD,GAA6B,CACrD,MAAME,EAAwBhH,GAC7BA,EAAM,UAAY,IAAI+G,CAAU,GAIjC,GAAI,EAFHhN,EAAK,sBAAsB,GAAG,KAAKiN,CAAoB,GACvDjN,EAAK,OAAU,KAAKiN,CAAoB,GACf,CAGzB,MAAMC,EAAmB9N,EAAK,KAC7B4E,EAAU,KACVgJ,CAAA,EAEDF,EAAAA,UAAUI,CAAgB,EAEtBlN,EAAK,sBAAsB,IAAM,SACpCA,EAAK,sBAAsB,EAAI,CAAA,GAIhCA,EAAK,sBAAsB,EAAE,QAAQ,CACpC,QAAS,IAAIgN,CAAU,GACvB,SAAUE,CAAA,CACV,CACF,CACD,CAEA,GAAIlN,EAAK,sBAAsB,EAC9B,UAAWiG,KAASjG,EAAK,sBAAsB,EAC9CpC,EAAAA,OAAO,MACN,4BAA4BqI,EAAM,OAAO,OAAOA,EAAM,QAAQ,EAAA,EAIjE,GAAIjG,EAAK,MACR,UAAWiG,KAASjG,EAAK,MACxBpC,EAAAA,OAAO,MACN,2BAA2BqI,EAAM,OAAO,OAAOA,EAAM,QAAQ,EAAA,EAKhE,IAAIkH,EACAnN,EAAK,mCAAmC,EAC3CmN,EAAU,IAAIpN,GAAoBC,EAAM,CACvC,QAAAiM,EACA,qBAAAG,CAAA,CACA,GAEDe,EAAU,IAAIrL,GAAoB9B,EAAM,CACvC,QAAAiM,EACA,qBAAAG,CAAA,CACA,EAEG,OAAOpM,EAAK,WAAc,WAC7BA,EAAK,UAAY,MAAMjB,GAAiB,CACvC,aAAciB,EAAK,UACnB,8BACCA,EAAK,mCAAmC,IAAM,EAAA,CAC/C,IAOH,IAAIoN,EAAY,GAChB,MAAMC,EAAa,gBAA4B,CAC1CD,IAIJA,EAAY,GACZ,MAAM,QAAQ,IACb,CAAC,GAAG9B,CAAoB,EAAE,IACzB,MAAO,CAAChN,EAAQ8B,CAAU,IAAM,CAC/B,MAAMA,EAAW,QAAA,EACjB,MAAM9B,EAAO,UAAA,CACd,CAAA,CACD,EAEGpB,GACH,MAAM,IAAI,QAASC,GAAYD,EAAO,MAAMC,CAAO,CAAC,EAErD,MAAM6G,EAAU,QAAA,EACjB,EAIMsJ,GAAkBC,GACvBpB,EACAgB,EAAQ,cAAA,EACR,CAAC,CAAE,SAAAK,EAAU,YAAAjP,KAAkB,CAG1B6O,GAIAI,IAAa,GAIjB5P,EAAAA,OAAO,MACN,UAAUW,CAAW,qBAAqBiP,CAAQ;AAAA,CAAA,CAGpD,CAAA,EAGD5P,SAAO,IAAI,qBAAqB,EAEhC,GAAI,CACH,MAAM6P,EAAU,MAAMH,GAEhBpN,EAAsB,MAAMwN,GACjC9B,CAAA,EAKD,CAEC,MAAMvN,EAAgBoP,EAAQ,MAAA,EACxBE,EACL,MAAMR,EAAQ,8BACb9O,EAAc,QACd6B,EACAC,CAAA,EAaF,GAXAmL,EAAqB,IACpBjN,EAAc,OACdsP,CAAA,EAGD,MAAMA,EAAkB,QAAA,EACxB7B,EAAiB,GACjBlO,SAAO,IAAI,SAAS,EAEpByN,EAAe,IAAIjN,GAAauP,CAAiB,EAE7C,CAAC3N,EAAK,mCAAmC,EAAG,CAC/C,MAAM4N,EAAoB,MACzBT,EACC,sBACDnN,EAAK,4BAA4B,GAAK,CAAA,CAAC,EAGpC4N,IACHhQ,SAAO,IAAI,0BAA0B,EACrC,MAAMiQ,EAAAA,oBACLD,EACAD,CAAA,EAED/P,SAAO,IAAI,gCAAgC,EAE7C,CAEA,GAAIoC,EAAK,UAAY,iBAAkB,CACtC,MAAM8N,GAAQ1N,EAAYJ,EAAK,OAAiB,EAChDpC,EAAAA,OAAO,IAAI,yBAAyBoC,EAAK,OAAO,EAAE,EAClD,MAAMqN,EAAA,EACN,MACD,SAAWrN,EAAK,UAAY,gBAAiB,CAC5CpC,SAAO,IAAI,oBAAoB,EAC/B,MAAMyP,EAAA,EACN,MACD,CAKA,MAAMhC,EAAa,aAAasC,CAAiB,EACjD,MAAMA,EAAkB,QAAA,EACxB,MAAMtP,EAAc,OAAO,UAAA,EAC3BiN,EAAqB,OAAOjN,EAAc,MAAM,CACjD,CAEAT,SAAO,IAAI,sBAAsB,EAGjC,MAAMmQ,EAA8B3B,EAEpC,OAAChM,CAAU,EAAI,MAAM,QAAQ,IAC5BqN,EAAQ,IAAI,MAAOnP,EAAQ0P,IAAU,CACpC,MAAMzN,EACLwN,EACAC,EAAQ5B,EAEHlM,GAAsB,MAAMwN,GACjC9B,CAAA,EAGKqC,EACL,MAAMd,EAAQ,eAAe,CAC5B,OAAA7O,EACA,oBAAA4B,GACA,eAAAK,EACA,sBAAAJ,CAAA,CACA,EAEF,OAAAmL,EAAqB,IACpBhN,EAAO,OACP2P,CAAA,EAED5C,EAAa,UAAU4C,CAAoB,EAEpCA,CACR,CAAC,CAAA,EAGFrQ,EAAAA,OAAO,IACN,2BAA2BoO,CAAS,SAASE,CAAiB,YAAA,EAG3DlM,EAAK,QAAUA,EAAK,uBACR,MAAMkO,eAAY,CAChC,YAAa9N,EACb,QAAS,YAAA,CACT,GAEM,MAAA,EAGD,CACN,WAAAA,EACA,OAAAlD,EACA,UAAA8O,EACA,CAAC,OAAO,YAAY,EAAGqB,EACvB,kBAAmBnB,CAAA,CAErB,OAASvO,EAAO,CACf,GAAI,CAACqC,EAAK,MACT,MAAMrC,EAEP,IAAIwQ,EAAU,GACd,MAAI,MAAM/N,GAAY,WAAWgO,EAAAA,YAAY,IAC5CD,EAAU,MAAM/N,EAAW,eAAegO,cAAY,GAEvD,MAAMf,EAAA,EACA,IAAI,MAAMc,EAAS,CAAE,MAAOxQ,EAAO,CAC1C,CACD,EACA,MAAM,cAAce,EAAqB,CACxC,GAAI,CAACoN,EACJ,OAAOjO,EAAAA,YAAY,YAClB,IACA,4BAAA,EAOF,GAAIkO,EAAgB,CACnBA,EAAiB,GACjB,MAAMsC,EAAoC,CACzC,eAAgB,CAAC,YAAY,EAC7B,iBAAkB,CAAC,GAAG,EACtB,SAAU,CAAC3P,EAAQ,GAAG,CAAA,EAEvB,OACCA,EAAQ,SAAU,QAAW,SAC5B,wCAAA,IAGD2P,EAAQ,YAAY,EAAI,CACvB,oGAAA,GAGK,IAAIxQ,EAAAA,YAAY,IAAKwQ,EAAS,IAAI,UAAY,CACtD,CACA,OAAO,MAAMhD,EAAa,cAAc3M,CAAO,CAChD,CAAA,CACA,CACF,CAOA,eAAe6O,GACde,EACAC,EACAC,EAC2B,CAC3B,MAAMC,EAAW,CAAA,EACjB,QAAStQ,EAAI,EAAGA,EAAImQ,EAAOnQ,IAAK,CAC/B,MAAMG,EAAS,MAAMoQ,GAAkBH,CAAU,EAC3CI,EAAkCC,GAAiB,CACxDJ,EAAa,CACZ,SAAUI,EACV,YAAazQ,CAAA,CACb,CACF,EACAsQ,EAAS,KACR,IAAI,QACH,CAACtR,EAASC,IAAW,CACpBkB,EAAO,KAAK,UAAW,SAAUmC,EAAc,CAI1CA,EAAQ,UAAY,6BACvBtD,EAAQ,CAAE,OAAAmB,EAAQ,QAASmC,EAAQ,QAAS,CAE9C,CAAC,EACDnC,EAAO,KAAK,QAAS,SAAU8D,EAAU,CACxC,QAAQ,MAAMA,CAAC,EACf,MAAMzE,EAAQ,IAAI,MACjB,iCACCyE,EAAE,QAAU,mBAAmBA,EAAE,OAAO,GAAK,EAC9C,EAAA,EAEDhF,EAAOO,CAAK,CACb,CAAC,EACDW,EAAO,KAAK,OAAQqQ,CAAM,CAC3B,CAAA,CACD,CAEF,CACA,OAAO,QAAQ,IAAIF,CAAQ,CAC5B,CAaA,eAAeC,GAAkBH,EAAyB,CAczD,OAAIA,IAAe,KACX,IAAIM,EAAAA,OAAO,IAAI,IAAI,yBAAmB,OAAA,SAAA,IAAA,QAAA,KAAA,EAAA,cAAA,UAAA,EAAA,KAAAC,GAAAA,EAAA,QAAA,YAAA,IAAA,UAAAA,EAAA,KAAA,IAAA,IAAA,uBAAA,SAAA,OAAA,EAAgB,IAAA,CAAA,EAEtD,IAAID,EAAAA,OAAO,IAAI,IAAI,yBAAmB,OAAA,SAAA,IAAA,QAAA,KAAA,EAAA,cAAA,UAAA,EAAA,KAAAC,GAAAA,EAAA,QAAA,YAAA,IAAA,UAAAA,EAAA,KAAA,IAAA,IAAA,uBAAA,SAAA,OAAA,EAAgB,IAAA,CAAA,CAE/D,CAQA,eAAepB,GAAsB9B,EAAyC,CAC7E,KAAM,CAAE,MAAAmD,EAAO,MAAAC,CAAA,EAAU,IAAIC,EAAAA,eAC7B,OAAI,MAAMC,GAAAA,OAQTC,YAAUvD,EAAiB,KAAMmD,CAAK,EAStC,MAAMK,EAAAA,cAAcxD,EAAiBmD,CAAK,EAEpCC,CACR,CAEA,eAAelB,GACd1N,EACAiP,EACC,CACD,MAAMjP,EAAW,IAAI,CACpB,KAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAkBN,EACD,MAAMkP,EAAM,MAAMlP,EAAW,iBAAiB,gBAAgB,EAC9Df,EAAG,cAAcgQ,EAASC,CAAG,CAC9B"}
|