@wp-playground/cli 3.1.22 → 3.1.25
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/package.json +14 -14
- package/sqlite-database-integration.zip +0 -0
- package/worker-thread-v1.cjs +1 -1
- package/worker-thread-v1.cjs.map +1 -1
- package/worker-thread-v1.js +7 -6
- package/worker-thread-v1.js.map +1 -1
- package/worker-thread-v2.cjs +4 -4
- package/worker-thread-v2.cjs.map +1 -1
- package/worker-thread-v2.js +16 -15
- package/worker-thread-v2.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wp-playground/cli",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.25",
|
|
4
4
|
"description": "WordPress Playground CLI",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"bin": {
|
|
35
35
|
"wp-playground-cli": "wp-playground.js"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "e662afdd9a3b6dad3e194ad34d80421c539b7386",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@zip.js/zip.js": "2.7.57",
|
|
40
40
|
"ajv": "8.12.0",
|
|
@@ -62,18 +62,18 @@
|
|
|
62
62
|
"ws": "8.18.0",
|
|
63
63
|
"xml2js": "0.6.2",
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
|
-
"@wp-playground/common": "3.1.
|
|
66
|
-
"@php-wasm/logger": "3.1.
|
|
67
|
-
"@php-wasm/progress": "3.1.
|
|
68
|
-
"@php-wasm/universal": "3.1.
|
|
69
|
-
"@wp-playground/blueprints": "3.1.
|
|
70
|
-
"@wp-playground/wordpress": "3.1.
|
|
71
|
-
"@php-wasm/node": "3.1.
|
|
72
|
-
"@php-wasm/util": "3.1.
|
|
73
|
-
"@php-wasm/cli-util": "3.1.
|
|
74
|
-
"@wp-playground/storage": "3.1.
|
|
75
|
-
"@php-wasm/xdebug-bridge": "3.1.
|
|
76
|
-
"@wp-playground/tools": "3.1.
|
|
65
|
+
"@wp-playground/common": "3.1.25",
|
|
66
|
+
"@php-wasm/logger": "3.1.25",
|
|
67
|
+
"@php-wasm/progress": "3.1.25",
|
|
68
|
+
"@php-wasm/universal": "3.1.25",
|
|
69
|
+
"@wp-playground/blueprints": "3.1.25",
|
|
70
|
+
"@wp-playground/wordpress": "3.1.25",
|
|
71
|
+
"@php-wasm/node": "3.1.25",
|
|
72
|
+
"@php-wasm/util": "3.1.25",
|
|
73
|
+
"@php-wasm/cli-util": "3.1.25",
|
|
74
|
+
"@wp-playground/storage": "3.1.25",
|
|
75
|
+
"@php-wasm/xdebug-bridge": "3.1.25",
|
|
76
|
+
"@wp-playground/tools": "3.1.25"
|
|
77
77
|
},
|
|
78
78
|
"packageManager": "npm@10.9.2",
|
|
79
79
|
"overrides": {
|
|
Binary file
|
package/worker-thread-v1.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("@php-wasm/node"),b=require("@php-wasm/progress"),n=require("@php-wasm/universal"),q=require("@php-wasm/util"),I=require("@wp-playground/common"),u=require("@wp-playground/wordpress"),W=require("tls"),P=require("worker_threads"),i=require("./run-cli-b6r6MAhq.cjs"),k=require("@php-wasm/logger");function H(r,e,...t){console.log(performance.now().toFixed(6).padStart(15,"0"),r.toString().padStart(16,"0"),q.sprintf(e,...t))}class w extends n.PHPWorker{constructor(e){super(void 0,e),this.bootedRequestHandler=!1,this.bootedWordPress=!1}async useFileLockManager(e){this.fileLockManager=await n.consumeAPISync(e)}async bootWordPress(e,t){if(this.bootedWordPress)throw new Error("WordPress already booted");this.bootedWordPress=!0;const{siteUrl:s,phpVersion:o,wordpressInstallMode:m,wordPressZip:c,sqliteIntegrationPluginZip:d,dataSqlPath:y,constants:f}=e;try{await u.bootWordPress(this.__internal_getRequestHandler(),{siteUrl:s,phpVersion:o,wordpressInstallMode:m,wordPressZip:c!==void 0?new File([c],"wordpress.zip"):void 0,sqliteIntegrationPluginZip:d!==void 0?new File([d],"sqlite-integration-plugin.zip"):void 0,createFiles:{"/internal/shared/ca-bundle.crt":W.rootCertificates.join(`
|
|
2
|
-
`)},phpIniEntries:{"openssl.cafile":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""},dataSqlPath:y,constants:f});const a=n.consumeAPI(t);await a.applyPostInstallMountsToAllWorkers(),a[n.releaseApiProxy](),p()}catch(a){throw h(a),a}}async bootRequestHandler(e){if(this.bootedRequestHandler)throw new Error("Playground already booted");this.bootedRequestHandler=!0;try{const t=await u.bootRequestHandler({siteUrl:e.siteUrl,phpVersion:e.phpVersion,maxPhpInstances:1,createPhpRuntime:R(e,this.fileLockManager),onPHPInstanceCreated:async o=>{await i.mountResources(o,e.mountsBeforeWpInstall),this.bootedWordPress&&await i.mountResources(o,e.mountsAfterWpInstall)},sapiName:"cli",cookieStore:!1,pathAliases:e.pathAliases,spawnHandler:()=>n.sandboxedSpawnHandlerFactory(()=>{let o=e;return this.bootedWordPress||(o={...e,mountsAfterWpInstall:[]}),v(o,this.fileLockManager)})});this.__internal_setRequestHandler(t);const s=await t.getPrimaryPhp();await this.setPrimaryPHP(s),p()}catch(t){throw h(t),t}}async mountAfterWordPressInstall(e){this.bootedWordPress=!0,await i.mountResources(this.__internal_getPHP(),e)}async dispose(){await this[Symbol.asyncDispose]()}}function R(r,e){return async()=>await g.loadNodeRuntime(r.phpVersion||I.RecommendedPHPVersion,{fileLockManager:e,emscriptenOptions:{processId:r.processId,trace:r.trace?H:void 0,nativeInternalDirPath:r.nativeInternalDirPath},followSymlinks:r.followSymlinks,extensions:r.extensions})}async function v(r,e){const t=await i.spawnWorkerThread("v1"),s=n.consumeAPI(t.phpPort);return s.useFileLockManager(e),await s.bootRequestHandler({...r,processId:t.processId}),{php:s,reap:()=>{try{s.dispose()}catch{}try{t.worker.terminate()}catch{}}}}process.on("unhandledRejection",r=>{k.logger.error("Unhandled rejection:",r)});const l=new P.MessageChannel,[p,h]=n.exposeAPI(new w(new b.EmscriptenDownloadMonitor),void 0,l.port1);P.parentPort?.postMessage({command:"worker-script-initialized",phpPort:l.port2},[l.port2]);exports.PlaygroundCliBlueprintV1Worker=w;
|
|
2
|
+
`)},phpIniEntries:{"openssl.cafile":"/internal/shared/ca-bundle.crt","curl.cainfo":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""},dataSqlPath:y,constants:f});const a=n.consumeAPI(t);await a.applyPostInstallMountsToAllWorkers(),a[n.releaseApiProxy](),p()}catch(a){throw h(a),a}}async bootRequestHandler(e){if(this.bootedRequestHandler)throw new Error("Playground already booted");this.bootedRequestHandler=!0;try{const t=await u.bootRequestHandler({siteUrl:e.siteUrl,phpVersion:e.phpVersion,maxPhpInstances:1,createPhpRuntime:R(e,this.fileLockManager),onPHPInstanceCreated:async o=>{await i.mountResources(o,e.mountsBeforeWpInstall),this.bootedWordPress&&await i.mountResources(o,e.mountsAfterWpInstall)},sapiName:"cli",cookieStore:!1,pathAliases:e.pathAliases,spawnHandler:()=>n.sandboxedSpawnHandlerFactory(()=>{let o=e;return this.bootedWordPress||(o={...e,mountsAfterWpInstall:[]}),v(o,this.fileLockManager)})});this.__internal_setRequestHandler(t);const s=await t.getPrimaryPhp();await this.setPrimaryPHP(s),p()}catch(t){throw h(t),t}}async mountAfterWordPressInstall(e){this.bootedWordPress=!0,await i.mountResources(this.__internal_getPHP(),e)}async dispose(){await this[Symbol.asyncDispose]()}}function R(r,e){return async()=>await g.loadNodeRuntime(r.phpVersion||I.RecommendedPHPVersion,{fileLockManager:e,emscriptenOptions:{processId:r.processId,trace:r.trace?H:void 0,nativeInternalDirPath:r.nativeInternalDirPath},followSymlinks:r.followSymlinks,extensions:r.extensions})}async function v(r,e){const t=await i.spawnWorkerThread("v1"),s=n.consumeAPI(t.phpPort);return s.useFileLockManager(e),await s.bootRequestHandler({...r,processId:t.processId}),{php:s,reap:()=>{try{s.dispose()}catch{}try{t.worker.terminate()}catch{}}}}process.on("unhandledRejection",r=>{k.logger.error("Unhandled rejection:",r)});const l=new P.MessageChannel,[p,h]=n.exposeAPI(new w(new b.EmscriptenDownloadMonitor),void 0,l.port1);P.parentPort?.postMessage({command:"worker-script-initialized",phpPort:l.port2},[l.port2]);exports.PlaygroundCliBlueprintV1Worker=w;
|
|
3
3
|
//# sourceMappingURL=worker-thread-v1.cjs.map
|
package/worker-thread-v1.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-thread-v1.cjs","sources":["../../../../packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts"],"sourcesContent":["import type { FileLockManager } from '@php-wasm/universal';\nimport { loadNodeRuntime, type PHPExtension } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { AllPHPVersion, PathAlias } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport {\n\ttype WordPressInstallMode,\n\tbootRequestHandler,\n\tbootWordPress,\n} from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { mountResources } from '../mounts';\nimport { logger } from '@php-wasm/logger';\nimport { spawnWorkerThread } from '../run-cli';\n\nimport type { Mount } from '@php-wasm/cli-util';\n\nexport type WorkerBootWordPressOptions = {\n\tsiteUrl: string;\n\tphpVersion?: string;\n\twpVersion?: string;\n\twordpressInstallMode: WordPressInstallMode;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tdataSqlPath?: string;\n\t/**\n\t * PHP constants to define via php.defineConstant().\n\t */\n\tconstants?: Record<string, string | number | boolean>;\n};\n\ninterface WorkerBootRequestHandlerOptions {\n\tsiteUrl: string;\n\tphpVersion: AllPHPVersion;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\tfollowSymlinks: boolean;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliBlueprintV1Worker extends PHPWorker {\n\tbootedRequestHandler = false;\n\tbootedWordPress = false;\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\toptions: WorkerBootWordPressOptions,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tif (this.bootedWordPress) {\n\t\t\tthrow new Error('WordPress already booted');\n\t\t}\n\t\tthis.bootedWordPress = true;\n\t\tconst {\n\t\t\tsiteUrl,\n\t\t\tphpVersion,\n\t\t\twordpressInstallMode,\n\t\t\twordPressZip,\n\t\t\tsqliteIntegrationPluginZip,\n\t\t\tdataSqlPath,\n\t\t\tconstants,\n\t\t} = options;\n\n\t\ttry {\n\t\t\tawait bootWordPress(this.__internal_getRequestHandler()!, {\n\t\t\t\tsiteUrl,\n\t\t\t\tphpVersion,\n\t\t\t\twordpressInstallMode,\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// TODO: Are these redundant creations?\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\tdataSqlPath,\n\t\t\t\tconstants,\n\t\t\t});\n\n\t\t\t// Notify all workers to apply post-install mounts.\n\t\t\tconst postInstall = consumeAPI<{\n\t\t\t\tapplyPostInstallMountsToAllWorkers: () => Promise<void>;\n\t\t\t}>(workerPostInstallMountsPort);\n\t\t\tawait postInstall.applyPostInstallMountsToAllWorkers();\n\t\t\tpostInstall[releaseApiProxy]();\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync bootRequestHandler(options: WorkerBootRequestHandlerOptions) {\n\t\tif (this.bootedRequestHandler) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.bootedRequestHandler = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl: options.siteUrl,\n\t\t\t\tphpVersion: options.phpVersion,\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tcreatePhpRuntime: createPhpRuntimeFactory(\n\t\t\t\t\toptions,\n\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t),\n\t\t\t\tonPHPInstanceCreated: async (php) => {\n\t\t\t\t\tawait mountResources(php, options.mountsBeforeWpInstall);\n\n\t\t\t\t\t// NOTE: We currently create all request workers up front\n\t\t\t\t\t// and apply post-install mounts to all the workers immediately\n\t\t\t\t\t// following WordPress install. But if we start creating\n\t\t\t\t\t// request-handling workers on-demand, we will to apply post-install\n\t\t\t\t\t// mounts here.\n\t\t\t\t\tif (this.bootedWordPress) {\n\t\t\t\t\t\tawait mountResources(php, options.mountsAfterWpInstall);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcookieStore: false,\n\t\t\t\tpathAliases: options.pathAliases,\n\t\t\t\tspawnHandler: () =>\n\t\t\t\t\tsandboxedSpawnHandlerFactory(() => {\n\t\t\t\t\t\tlet effectiveOptions = options;\n\t\t\t\t\t\tif (!this.bootedWordPress) {\n\t\t\t\t\t\t\t// WordPress is not yet booted so skip the post-install mounts.\n\t\t\t\t\t\t\teffectiveOptions = {\n\t\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\t\tmountsAfterWpInstall: [],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn createPHPWorker(\n\t\t\t\t\t\t\teffectiveOptions,\n\t\t\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\t// Make sure workers not involved in the WordPress install\n\t\t// process know whether WordPress booted so they can\n\t\t// apply post-install mounts when spawning new PHP workers.\n\t\tthis.bootedWordPress = true;\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Returns a factory function that starts a new PHP runtime in the currently\n * running process. This is used for rotating the PHP runtime periodically.\n */\nfunction createPhpRuntimeFactory(\n\toptions: WorkerBootRequestHandlerOptions,\n\tfileLockManager: FileLockManager\n) {\n\treturn async () => {\n\t\treturn await loadNodeRuntime(\n\t\t\toptions.phpVersion || RecommendedPHPVersion,\n\t\t\t{\n\t\t\t\tfileLockManager,\n\t\t\t\temscriptenOptions: {\n\t\t\t\t\tprocessId: options.processId,\n\t\t\t\t\ttrace: options.trace ? tracePhpWasm : undefined,\n\t\t\t\t\tnativeInternalDirPath: options.nativeInternalDirPath,\n\t\t\t\t},\n\t\t\t\tfollowSymlinks: options.followSymlinks,\n\t\t\t\textensions: options.extensions,\n\t\t\t}\n\t\t);\n\t};\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t// NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\toptions: Omit<WorkerBootRequestHandlerOptions, 'processId'>,\n\tfileLockManager: FileLockManager\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v1');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV1Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootRequestHandler({\n\t\t...options,\n\t\tprocessId: spawnedWorker.processId,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV1Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["tracePhpWasm","processId","format","args","sprintf","PlaygroundCliBlueprintV1Worker","PHPWorker","monitor","port","consumeAPISync","options","workerPostInstallMountsPort","siteUrl","phpVersion","wordpressInstallMode","wordPressZip","sqliteIntegrationPluginZip","dataSqlPath","constants","bootWordPress","rootCertificates","postInstall","consumeAPI","releaseApiProxy","setApiReady","e","setAPIError","requestHandler","bootRequestHandler","createPhpRuntimeFactory","php","mountResources","sandboxedSpawnHandlerFactory","effectiveOptions","createPHPWorker","primaryPhp","mounts","fileLockManager","loadNodeRuntime","RecommendedPHPVersion","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker-thread-v1.cjs","sources":["../../../../packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts"],"sourcesContent":["import type { FileLockManager } from '@php-wasm/universal';\nimport { loadNodeRuntime, type PHPExtension } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { AllPHPVersion, PathAlias } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport {\n\ttype WordPressInstallMode,\n\tbootRequestHandler,\n\tbootWordPress,\n} from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { mountResources } from '../mounts';\nimport { logger } from '@php-wasm/logger';\nimport { spawnWorkerThread } from '../run-cli';\n\nimport type { Mount } from '@php-wasm/cli-util';\n\nexport type WorkerBootWordPressOptions = {\n\tsiteUrl: string;\n\tphpVersion?: string;\n\twpVersion?: string;\n\twordpressInstallMode: WordPressInstallMode;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tdataSqlPath?: string;\n\t/**\n\t * PHP constants to define via php.defineConstant().\n\t */\n\tconstants?: Record<string, string | number | boolean>;\n};\n\ninterface WorkerBootRequestHandlerOptions {\n\tsiteUrl: string;\n\tphpVersion: AllPHPVersion;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\tfollowSymlinks: boolean;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliBlueprintV1Worker extends PHPWorker {\n\tbootedRequestHandler = false;\n\tbootedWordPress = false;\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\toptions: WorkerBootWordPressOptions,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tif (this.bootedWordPress) {\n\t\t\tthrow new Error('WordPress already booted');\n\t\t}\n\t\tthis.bootedWordPress = true;\n\t\tconst {\n\t\t\tsiteUrl,\n\t\t\tphpVersion,\n\t\t\twordpressInstallMode,\n\t\t\twordPressZip,\n\t\t\tsqliteIntegrationPluginZip,\n\t\t\tdataSqlPath,\n\t\t\tconstants,\n\t\t} = options;\n\n\t\ttry {\n\t\t\tawait bootWordPress(this.__internal_getRequestHandler()!, {\n\t\t\t\tsiteUrl,\n\t\t\t\tphpVersion,\n\t\t\t\twordpressInstallMode,\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// TODO: Are these redundant creations?\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\t'curl.cainfo': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\tdataSqlPath,\n\t\t\t\tconstants,\n\t\t\t});\n\n\t\t\t// Notify all workers to apply post-install mounts.\n\t\t\tconst postInstall = consumeAPI<{\n\t\t\t\tapplyPostInstallMountsToAllWorkers: () => Promise<void>;\n\t\t\t}>(workerPostInstallMountsPort);\n\t\t\tawait postInstall.applyPostInstallMountsToAllWorkers();\n\t\t\tpostInstall[releaseApiProxy]();\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync bootRequestHandler(options: WorkerBootRequestHandlerOptions) {\n\t\tif (this.bootedRequestHandler) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.bootedRequestHandler = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl: options.siteUrl,\n\t\t\t\tphpVersion: options.phpVersion,\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tcreatePhpRuntime: createPhpRuntimeFactory(\n\t\t\t\t\toptions,\n\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t),\n\t\t\t\tonPHPInstanceCreated: async (php) => {\n\t\t\t\t\tawait mountResources(php, options.mountsBeforeWpInstall);\n\n\t\t\t\t\t// NOTE: We currently create all request workers up front\n\t\t\t\t\t// and apply post-install mounts to all the workers immediately\n\t\t\t\t\t// following WordPress install. But if we start creating\n\t\t\t\t\t// request-handling workers on-demand, we will to apply post-install\n\t\t\t\t\t// mounts here.\n\t\t\t\t\tif (this.bootedWordPress) {\n\t\t\t\t\t\tawait mountResources(php, options.mountsAfterWpInstall);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcookieStore: false,\n\t\t\t\tpathAliases: options.pathAliases,\n\t\t\t\tspawnHandler: () =>\n\t\t\t\t\tsandboxedSpawnHandlerFactory(() => {\n\t\t\t\t\t\tlet effectiveOptions = options;\n\t\t\t\t\t\tif (!this.bootedWordPress) {\n\t\t\t\t\t\t\t// WordPress is not yet booted so skip the post-install mounts.\n\t\t\t\t\t\t\teffectiveOptions = {\n\t\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\t\tmountsAfterWpInstall: [],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn createPHPWorker(\n\t\t\t\t\t\t\teffectiveOptions,\n\t\t\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\t// Make sure workers not involved in the WordPress install\n\t\t// process know whether WordPress booted so they can\n\t\t// apply post-install mounts when spawning new PHP workers.\n\t\tthis.bootedWordPress = true;\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Returns a factory function that starts a new PHP runtime in the currently\n * running process. This is used for rotating the PHP runtime periodically.\n */\nfunction createPhpRuntimeFactory(\n\toptions: WorkerBootRequestHandlerOptions,\n\tfileLockManager: FileLockManager\n) {\n\treturn async () => {\n\t\treturn await loadNodeRuntime(\n\t\t\toptions.phpVersion || RecommendedPHPVersion,\n\t\t\t{\n\t\t\t\tfileLockManager,\n\t\t\t\temscriptenOptions: {\n\t\t\t\t\tprocessId: options.processId,\n\t\t\t\t\ttrace: options.trace ? tracePhpWasm : undefined,\n\t\t\t\t\tnativeInternalDirPath: options.nativeInternalDirPath,\n\t\t\t\t},\n\t\t\t\tfollowSymlinks: options.followSymlinks,\n\t\t\t\textensions: options.extensions,\n\t\t\t}\n\t\t);\n\t};\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t// NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\toptions: Omit<WorkerBootRequestHandlerOptions, 'processId'>,\n\tfileLockManager: FileLockManager\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v1');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV1Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootRequestHandler({\n\t\t...options,\n\t\tprocessId: spawnedWorker.processId,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV1Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["tracePhpWasm","processId","format","args","sprintf","PlaygroundCliBlueprintV1Worker","PHPWorker","monitor","port","consumeAPISync","options","workerPostInstallMountsPort","siteUrl","phpVersion","wordpressInstallMode","wordPressZip","sqliteIntegrationPluginZip","dataSqlPath","constants","bootWordPress","rootCertificates","postInstall","consumeAPI","releaseApiProxy","setApiReady","e","setAPIError","requestHandler","bootRequestHandler","createPhpRuntimeFactory","php","mountResources","sandboxedSpawnHandlerFactory","effectiveOptions","createPHPWorker","primaryPhp","mounts","fileLockManager","loadNodeRuntime","RecommendedPHPVersion","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
package/worker-thread-v1.js
CHANGED
|
@@ -38,17 +38,17 @@ class F extends b {
|
|
|
38
38
|
const {
|
|
39
39
|
siteUrl: o,
|
|
40
40
|
phpVersion: s,
|
|
41
|
-
wordpressInstallMode:
|
|
41
|
+
wordpressInstallMode: u,
|
|
42
42
|
wordPressZip: l,
|
|
43
43
|
sqliteIntegrationPluginZip: d,
|
|
44
|
-
dataSqlPath:
|
|
45
|
-
constants:
|
|
44
|
+
dataSqlPath: P,
|
|
45
|
+
constants: m
|
|
46
46
|
} = e;
|
|
47
47
|
try {
|
|
48
48
|
await R(this.__internal_getRequestHandler(), {
|
|
49
49
|
siteUrl: o,
|
|
50
50
|
phpVersion: s,
|
|
51
|
-
wordpressInstallMode:
|
|
51
|
+
wordpressInstallMode: u,
|
|
52
52
|
wordPressZip: l !== void 0 ? new File([l], "wordpress.zip") : void 0,
|
|
53
53
|
sqliteIntegrationPluginZip: d !== void 0 ? new File(
|
|
54
54
|
[d],
|
|
@@ -61,11 +61,12 @@ class F extends b {
|
|
|
61
61
|
},
|
|
62
62
|
phpIniEntries: {
|
|
63
63
|
"openssl.cafile": "/internal/shared/ca-bundle.crt",
|
|
64
|
+
"curl.cainfo": "/internal/shared/ca-bundle.crt",
|
|
64
65
|
allow_url_fopen: "1",
|
|
65
66
|
disable_functions: ""
|
|
66
67
|
},
|
|
67
|
-
dataSqlPath:
|
|
68
|
-
constants:
|
|
68
|
+
dataSqlPath: P,
|
|
69
|
+
constants: m
|
|
69
70
|
});
|
|
70
71
|
const a = h(t);
|
|
71
72
|
await a.applyPostInstallMountsToAllWorkers(), a[I](), c();
|
package/worker-thread-v1.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-thread-v1.js","sources":["../../../../packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts"],"sourcesContent":["import type { FileLockManager } from '@php-wasm/universal';\nimport { loadNodeRuntime, type PHPExtension } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { AllPHPVersion, PathAlias } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport {\n\ttype WordPressInstallMode,\n\tbootRequestHandler,\n\tbootWordPress,\n} from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { mountResources } from '../mounts';\nimport { logger } from '@php-wasm/logger';\nimport { spawnWorkerThread } from '../run-cli';\n\nimport type { Mount } from '@php-wasm/cli-util';\n\nexport type WorkerBootWordPressOptions = {\n\tsiteUrl: string;\n\tphpVersion?: string;\n\twpVersion?: string;\n\twordpressInstallMode: WordPressInstallMode;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tdataSqlPath?: string;\n\t/**\n\t * PHP constants to define via php.defineConstant().\n\t */\n\tconstants?: Record<string, string | number | boolean>;\n};\n\ninterface WorkerBootRequestHandlerOptions {\n\tsiteUrl: string;\n\tphpVersion: AllPHPVersion;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\tfollowSymlinks: boolean;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliBlueprintV1Worker extends PHPWorker {\n\tbootedRequestHandler = false;\n\tbootedWordPress = false;\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\toptions: WorkerBootWordPressOptions,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tif (this.bootedWordPress) {\n\t\t\tthrow new Error('WordPress already booted');\n\t\t}\n\t\tthis.bootedWordPress = true;\n\t\tconst {\n\t\t\tsiteUrl,\n\t\t\tphpVersion,\n\t\t\twordpressInstallMode,\n\t\t\twordPressZip,\n\t\t\tsqliteIntegrationPluginZip,\n\t\t\tdataSqlPath,\n\t\t\tconstants,\n\t\t} = options;\n\n\t\ttry {\n\t\t\tawait bootWordPress(this.__internal_getRequestHandler()!, {\n\t\t\t\tsiteUrl,\n\t\t\t\tphpVersion,\n\t\t\t\twordpressInstallMode,\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// TODO: Are these redundant creations?\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\tdataSqlPath,\n\t\t\t\tconstants,\n\t\t\t});\n\n\t\t\t// Notify all workers to apply post-install mounts.\n\t\t\tconst postInstall = consumeAPI<{\n\t\t\t\tapplyPostInstallMountsToAllWorkers: () => Promise<void>;\n\t\t\t}>(workerPostInstallMountsPort);\n\t\t\tawait postInstall.applyPostInstallMountsToAllWorkers();\n\t\t\tpostInstall[releaseApiProxy]();\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync bootRequestHandler(options: WorkerBootRequestHandlerOptions) {\n\t\tif (this.bootedRequestHandler) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.bootedRequestHandler = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl: options.siteUrl,\n\t\t\t\tphpVersion: options.phpVersion,\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tcreatePhpRuntime: createPhpRuntimeFactory(\n\t\t\t\t\toptions,\n\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t),\n\t\t\t\tonPHPInstanceCreated: async (php) => {\n\t\t\t\t\tawait mountResources(php, options.mountsBeforeWpInstall);\n\n\t\t\t\t\t// NOTE: We currently create all request workers up front\n\t\t\t\t\t// and apply post-install mounts to all the workers immediately\n\t\t\t\t\t// following WordPress install. But if we start creating\n\t\t\t\t\t// request-handling workers on-demand, we will to apply post-install\n\t\t\t\t\t// mounts here.\n\t\t\t\t\tif (this.bootedWordPress) {\n\t\t\t\t\t\tawait mountResources(php, options.mountsAfterWpInstall);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcookieStore: false,\n\t\t\t\tpathAliases: options.pathAliases,\n\t\t\t\tspawnHandler: () =>\n\t\t\t\t\tsandboxedSpawnHandlerFactory(() => {\n\t\t\t\t\t\tlet effectiveOptions = options;\n\t\t\t\t\t\tif (!this.bootedWordPress) {\n\t\t\t\t\t\t\t// WordPress is not yet booted so skip the post-install mounts.\n\t\t\t\t\t\t\teffectiveOptions = {\n\t\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\t\tmountsAfterWpInstall: [],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn createPHPWorker(\n\t\t\t\t\t\t\teffectiveOptions,\n\t\t\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\t// Make sure workers not involved in the WordPress install\n\t\t// process know whether WordPress booted so they can\n\t\t// apply post-install mounts when spawning new PHP workers.\n\t\tthis.bootedWordPress = true;\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Returns a factory function that starts a new PHP runtime in the currently\n * running process. This is used for rotating the PHP runtime periodically.\n */\nfunction createPhpRuntimeFactory(\n\toptions: WorkerBootRequestHandlerOptions,\n\tfileLockManager: FileLockManager\n) {\n\treturn async () => {\n\t\treturn await loadNodeRuntime(\n\t\t\toptions.phpVersion || RecommendedPHPVersion,\n\t\t\t{\n\t\t\t\tfileLockManager,\n\t\t\t\temscriptenOptions: {\n\t\t\t\t\tprocessId: options.processId,\n\t\t\t\t\ttrace: options.trace ? tracePhpWasm : undefined,\n\t\t\t\t\tnativeInternalDirPath: options.nativeInternalDirPath,\n\t\t\t\t},\n\t\t\t\tfollowSymlinks: options.followSymlinks,\n\t\t\t\textensions: options.extensions,\n\t\t\t}\n\t\t);\n\t};\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t// NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\toptions: Omit<WorkerBootRequestHandlerOptions, 'processId'>,\n\tfileLockManager: FileLockManager\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v1');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV1Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootRequestHandler({\n\t\t...options,\n\t\tprocessId: spawnedWorker.processId,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV1Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["tracePhpWasm","processId","format","args","sprintf","PlaygroundCliBlueprintV1Worker","PHPWorker","monitor","port","consumeAPISync","options","workerPostInstallMountsPort","siteUrl","phpVersion","wordpressInstallMode","wordPressZip","sqliteIntegrationPluginZip","dataSqlPath","constants","bootWordPress","rootCertificates","postInstall","consumeAPI","releaseApiProxy","setApiReady","e","setAPIError","requestHandler","bootRequestHandler","createPhpRuntimeFactory","php","mountResources","sandboxedSpawnHandlerFactory","effectiveOptions","createPHPWorker","primaryPhp","mounts","fileLockManager","loadNodeRuntime","RecommendedPHPVersion","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker-thread-v1.js","sources":["../../../../packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts"],"sourcesContent":["import type { FileLockManager } from '@php-wasm/universal';\nimport { loadNodeRuntime, type PHPExtension } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { AllPHPVersion, PathAlias } from '@php-wasm/universal';\nimport {\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n} from '@php-wasm/universal';\nimport { sprintf } from '@php-wasm/util';\nimport { RecommendedPHPVersion } from '@wp-playground/common';\nimport {\n\ttype WordPressInstallMode,\n\tbootRequestHandler,\n\tbootWordPress,\n} from '@wp-playground/wordpress';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { mountResources } from '../mounts';\nimport { logger } from '@php-wasm/logger';\nimport { spawnWorkerThread } from '../run-cli';\n\nimport type { Mount } from '@php-wasm/cli-util';\n\nexport type WorkerBootWordPressOptions = {\n\tsiteUrl: string;\n\tphpVersion?: string;\n\twpVersion?: string;\n\twordpressInstallMode: WordPressInstallMode;\n\twordPressZip?: ArrayBuffer;\n\tsqliteIntegrationPluginZip?: ArrayBuffer;\n\tdataSqlPath?: string;\n\t/**\n\t * PHP constants to define via php.defineConstant().\n\t */\n\tconstants?: Record<string, string | number | boolean>;\n};\n\ninterface WorkerBootRequestHandlerOptions {\n\tsiteUrl: string;\n\tphpVersion: AllPHPVersion;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\tfollowSymlinks: boolean;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\nexport class PlaygroundCliBlueprintV1Worker extends PHPWorker {\n\tbootedRequestHandler = false;\n\tbootedWordPress = false;\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\toptions: WorkerBootWordPressOptions,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tif (this.bootedWordPress) {\n\t\t\tthrow new Error('WordPress already booted');\n\t\t}\n\t\tthis.bootedWordPress = true;\n\t\tconst {\n\t\t\tsiteUrl,\n\t\t\tphpVersion,\n\t\t\twordpressInstallMode,\n\t\t\twordPressZip,\n\t\t\tsqliteIntegrationPluginZip,\n\t\t\tdataSqlPath,\n\t\t\tconstants,\n\t\t} = options;\n\n\t\ttry {\n\t\t\tawait bootWordPress(this.__internal_getRequestHandler()!, {\n\t\t\t\tsiteUrl,\n\t\t\t\tphpVersion,\n\t\t\t\twordpressInstallMode,\n\t\t\t\twordPressZip:\n\t\t\t\t\twordPressZip !== undefined\n\t\t\t\t\t\t? new File([wordPressZip], 'wordpress.zip')\n\t\t\t\t\t\t: undefined,\n\t\t\t\tsqliteIntegrationPluginZip:\n\t\t\t\t\tsqliteIntegrationPluginZip !== undefined\n\t\t\t\t\t\t? new File(\n\t\t\t\t\t\t\t\t[sqliteIntegrationPluginZip],\n\t\t\t\t\t\t\t\t'sqlite-integration-plugin.zip'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// TODO: Are these redundant creations?\n\t\t\t\tcreateFiles: {\n\t\t\t\t\t'/internal/shared/ca-bundle.crt':\n\t\t\t\t\t\trootCertificates.join('\\n'),\n\t\t\t\t},\n\t\t\t\tphpIniEntries: {\n\t\t\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\t'curl.cainfo': '/internal/shared/ca-bundle.crt',\n\t\t\t\t\tallow_url_fopen: '1',\n\t\t\t\t\tdisable_functions: '',\n\t\t\t\t},\n\t\t\t\tdataSqlPath,\n\t\t\t\tconstants,\n\t\t\t});\n\n\t\t\t// Notify all workers to apply post-install mounts.\n\t\t\tconst postInstall = consumeAPI<{\n\t\t\t\tapplyPostInstallMountsToAllWorkers: () => Promise<void>;\n\t\t\t}>(workerPostInstallMountsPort);\n\t\t\tawait postInstall.applyPostInstallMountsToAllWorkers();\n\t\t\tpostInstall[releaseApiProxy]();\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync bootRequestHandler(options: WorkerBootRequestHandlerOptions) {\n\t\tif (this.bootedRequestHandler) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.bootedRequestHandler = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl: options.siteUrl,\n\t\t\t\tphpVersion: options.phpVersion,\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tcreatePhpRuntime: createPhpRuntimeFactory(\n\t\t\t\t\toptions,\n\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t),\n\t\t\t\tonPHPInstanceCreated: async (php) => {\n\t\t\t\t\tawait mountResources(php, options.mountsBeforeWpInstall);\n\n\t\t\t\t\t// NOTE: We currently create all request workers up front\n\t\t\t\t\t// and apply post-install mounts to all the workers immediately\n\t\t\t\t\t// following WordPress install. But if we start creating\n\t\t\t\t\t// request-handling workers on-demand, we will to apply post-install\n\t\t\t\t\t// mounts here.\n\t\t\t\t\tif (this.bootedWordPress) {\n\t\t\t\t\t\tawait mountResources(php, options.mountsAfterWpInstall);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcookieStore: false,\n\t\t\t\tpathAliases: options.pathAliases,\n\t\t\t\tspawnHandler: () =>\n\t\t\t\t\tsandboxedSpawnHandlerFactory(() => {\n\t\t\t\t\t\tlet effectiveOptions = options;\n\t\t\t\t\t\tif (!this.bootedWordPress) {\n\t\t\t\t\t\t\t// WordPress is not yet booted so skip the post-install mounts.\n\t\t\t\t\t\t\teffectiveOptions = {\n\t\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\t\tmountsAfterWpInstall: [],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn createPHPWorker(\n\t\t\t\t\t\t\teffectiveOptions,\n\t\t\t\t\t\t\tthis.fileLockManager!\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\t// Make sure workers not involved in the WordPress install\n\t\t// process know whether WordPress booted so they can\n\t\t// apply post-install mounts when spawning new PHP workers.\n\t\tthis.bootedWordPress = true;\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Returns a factory function that starts a new PHP runtime in the currently\n * running process. This is used for rotating the PHP runtime periodically.\n */\nfunction createPhpRuntimeFactory(\n\toptions: WorkerBootRequestHandlerOptions,\n\tfileLockManager: FileLockManager\n) {\n\treturn async () => {\n\t\treturn await loadNodeRuntime(\n\t\t\toptions.phpVersion || RecommendedPHPVersion,\n\t\t\t{\n\t\t\t\tfileLockManager,\n\t\t\t\temscriptenOptions: {\n\t\t\t\t\tprocessId: options.processId,\n\t\t\t\t\ttrace: options.trace ? tracePhpWasm : undefined,\n\t\t\t\t\tnativeInternalDirPath: options.nativeInternalDirPath,\n\t\t\t\t},\n\t\t\t\tfollowSymlinks: options.followSymlinks,\n\t\t\t\textensions: options.extensions,\n\t\t\t}\n\t\t);\n\t};\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t// NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\toptions: Omit<WorkerBootRequestHandlerOptions, 'processId'>,\n\tfileLockManager: FileLockManager\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v1');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV1Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootRequestHandler({\n\t\t...options,\n\t\tprocessId: spawnedWorker.processId,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV1Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["tracePhpWasm","processId","format","args","sprintf","PlaygroundCliBlueprintV1Worker","PHPWorker","monitor","port","consumeAPISync","options","workerPostInstallMountsPort","siteUrl","phpVersion","wordpressInstallMode","wordPressZip","sqliteIntegrationPluginZip","dataSqlPath","constants","bootWordPress","rootCertificates","postInstall","consumeAPI","releaseApiProxy","setApiReady","e","setAPIError","requestHandler","bootRequestHandler","createPhpRuntimeFactory","php","mountResources","sandboxedSpawnHandlerFactory","effectiveOptions","createPHPWorker","primaryPhp","mounts","fileLockManager","loadNodeRuntime","RecommendedPHPVersion","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
package/worker-thread-v2.cjs
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
`),l.lastWriteWasProgress&&(l.lastWriteWasProgress=!1),process.stdout.write(s)},stderr(s){process.stdout.write(`
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
`),l.lastWriteWasProgress&&(l.lastWriteWasProgress=!1),process.stderr.write(s)}};class T extends a.PHPWorker{constructor(e){super(void 0,e),this.booted=!1,this.blueprintTargetResolved=!1,this.phpInstancesThatNeedMountsAfterTargetResolved=new Set}async useFileLockManager(e){this.fileLockManager=await a.consumeAPISync(e)}async bootWordPress(e,t){const
|
|
10
|
-
`)}),await m.preloadPhpInfoRoute(
|
|
9
|
+
`),l.lastWriteWasProgress&&(l.lastWriteWasProgress=!1),process.stderr.write(s)}};class T extends a.PHPWorker{constructor(e){super(void 0,e),this.booted=!1,this.blueprintTargetResolved=!1,this.phpInstancesThatNeedMountsAfterTargetResolved=new Set}async useFileLockManager(e){this.fileLockManager=await a.consumeAPISync(e)}async bootWordPress(e,t){const n=await this.__internal_getRequestHandler().getPrimaryPhp();if(n.defineConstant("WP_DEBUG","true"),n.defineConstant("WP_DEBUG_LOG","true"),n.defineConstant("WP_DEBUG_DISPLAY","false"),n.defineConstant("WP_HOME",e.siteUrl),n.defineConstant("WP_SITEURL",e.siteUrl),await a.setPhpIniEntries(n,{"openssl.cafile":"/internal/shared/ca-bundle.crt","curl.cainfo":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""}),await m.setupPlatformLevelMuPlugins(n),await a.writeFiles(n,"/",{"/internal/shared/ca-bundle.crt":$.rootCertificates.join(`
|
|
10
|
+
`)}),await m.preloadPhpInfoRoute(n,_.joinPaths(new URL(e.siteUrl).pathname,"phpinfo.php")),e.mode==="mount-only"){await this.applyPostInstallMountsToAllWorkers(t);return}await this.runBlueprintV2({...e},t)}async bootWorker(e){await this.bootRequestHandler({...e,onPHPInstanceCreated:async t=>{await W(t,e.mountsBeforeWpInstall||[]),await W(t,e.mountsAfterWpInstall||[]),t.isDir("/wordpress/wp-content")||t.mkdir("/wordpress/wp-content"),t.isDir("/wordpress/wp-content/database")||t.mkdir("/wordpress/wp-content/database"),t.isFile("/wordpress/wp-content/database/.htaccess")||t.writeFile("/wordpress/wp-content/database/.htaccess","deny from all"),t.isFile("/wordpress/wp-content/database/index.php")||t.writeFile("/wordpress/wp-content/database/index.php","deny from all")},spawnHandler:()=>a.sandboxedSpawnHandlerFactory(()=>A(e,this.fileLockManager))})}async runBlueprintV2(e,t){const n=this.__internal_getRequestHandler(),{php:w,reap:f}=await n.instanceManager.acquirePHPInstance(),h=this.__internal_getPHP();let P=()=>{};if(typeof e.blueprint=="string"){const i=b.resolve(process.cwd(),e.blueprint);I.existsSync(i)&&(h.mkdir("/internal/shared/cwd"),P=await h.mount("/internal/shared/cwd",y.createNodeFsMountHandler(b.dirname(i))),e.blueprint=b.join("/internal/shared/cwd",b.basename(e.blueprint)))}try{const c=["mode","db-engine","db-host","db-user","db-pass","db-name","db-path","truncate-new-site-directory","allow"].filter(r=>r in e).map(r=>`--${r}=${e[r]}`);c.push(`--site-url=${e.siteUrl}`);const d=await v.runBlueprintV2({php:w,blueprint:e.blueprint,blueprintOverrides:{additionalSteps:e["additional-blueprint-steps"],wordpressVersion:e.wp},cliArgs:c,onMessage:async r=>{switch(r.type){case"blueprint.target_resolved":{this.blueprintTargetResolved||(this.blueprintTargetResolved=!0,await this.applyPostInstallMountsToAllWorkers(t));break}case"blueprint.progress":{const p=`${r.caption.trim()} – ${r.progress.toFixed(2)}%`;l.progress(p);break}case"blueprint.error":{const p="\x1B[31m",u="\x1B[1m",o="\x1B[0m";e.verbosity==="debug"&&r.details?l.stderr(`${p}${u}Fatal error:${o} Uncaught ${r.details.exception}: ${r.details.message}
|
|
11
11
|
at ${r.details.file}:${r.details.line}
|
|
12
12
|
`+(r.details.trace?r.details.trace+`
|
|
13
|
-
`:"")):l.stderr(`${p}${u}Error:${
|
|
14
|
-
`);break}}}});if(e.verbosity==="debug"&&(d.stdout.pipeTo(new WritableStream({write(r){process.stdout.write(r)}})),d.stderr.pipeTo(new WritableStream({write(r){process.stderr.write(r)}}))),await d.finished,await d.exitCode!==0){const r=await a.PHPResponse.fromStreamedResponse(d);throw new a.PHPExecutionFailureError(`PHP.run() failed with exit code ${r.exitCode}. ${r.errors} ${r.text}`,r,"request")}}catch(i){let c="";try{c=w.readFileAsText(k.errorLogPath)}catch{}throw i.phpLogs=c,i}finally{f(),P()}}async bootRequestHandler({siteUrl:e,allow:t,phpVersion:
|
|
13
|
+
`:"")):l.stderr(`${p}${u}Error:${o} ${r.message}
|
|
14
|
+
`);break}}}});if(e.verbosity==="debug"&&(d.stdout.pipeTo(new WritableStream({write(r){process.stdout.write(r)}})),d.stderr.pipeTo(new WritableStream({write(r){process.stderr.write(r)}}))),await d.finished,await d.exitCode!==0){const r=await a.PHPResponse.fromStreamedResponse(d);throw new a.PHPExecutionFailureError(`PHP.run() failed with exit code ${r.exitCode}. ${r.errors} ${r.text}`,r,"request")}}catch(i){let c="";try{c=w.readFileAsText(k.errorLogPath)}catch{}throw i.phpLogs=c,i}finally{f(),P()}}async bootRequestHandler({siteUrl:e,allow:t,phpVersion:n,processId:w,createFiles:f,constants:h,phpIniEntries:P,trace:i,nativeInternalDirPath:c,extensions:d,pathAliases:r,onPHPInstanceCreated:p,spawnHandler:u}){if(this.booted)throw new Error("Playground already booted");this.booted=!0;try{const o=await m.bootRequestHandler({siteUrl:e,createPhpRuntime:async()=>await y.loadNodeRuntime(n,{fileLockManager:this.fileLockManager,emscriptenOptions:{processId:w,trace:i?S:void 0,ENV:{DOCROOT:"/wordpress"},nativeInternalDirPath:c,bindUserSpace:M=>y.bindUserSpace({fileLockManager:this.fileLockManager},M)},followSymlinks:t?.includes("follow-symlinks"),extensions:d}),maxPhpInstances:1,onPHPInstanceCreated:p,sapiName:"cli",createFiles:f,constants:h,phpIniEntries:P,pathAliases:r,cookieStore:!1,spawnHandler:u});this.__internal_setRequestHandler(o);const H=await o.getPrimaryPhp();await this.setPrimaryPHP(H),B()}catch(o){throw C(o),o}}async mountAfterWordPressInstall(e){await W(this.__internal_getPHP(),e)}async applyPostInstallMountsToAllWorkers(e){const t=a.consumeAPI(e);await t(),t[a.releaseApiProxy]()}async dispose(){await this[Symbol.asyncDispose]()}}async function A({siteUrl:s,allow:e,phpVersion:t,createFiles:n,constants:w,phpIniEntries:f,trace:h,nativeInternalDirPath:P,extensions:i,pathAliases:c,mountsBeforeWpInstall:d,mountsAfterWpInstall:r},p){const u=await R.spawnWorkerThread("v2"),o=a.consumeAPI(u.phpPort);return o.useFileLockManager(p),await o.bootWorker({siteUrl:s,allow:e,phpVersion:t,createFiles:n,constants:w,phpIniEntries:f,processId:u.processId,trace:h,nativeInternalDirPath:P,extensions:i,pathAliases:c,mountsBeforeWpInstall:d,mountsAfterWpInstall:r}),{php:o,reap:()=>{try{o.dispose()}catch{}try{u.worker.terminate()}catch{}}}}process.on("unhandledRejection",s=>{k.logger.error("Unhandled rejection:",s)});const g=new x.MessageChannel,[B,C]=a.exposeAPI(new T(new q.EmscriptenDownloadMonitor),void 0,g.port1);x.parentPort?.postMessage({command:"worker-script-initialized",phpPort:g.port2},[g.port2]);exports.PlaygroundCliBlueprintV2Worker=T;
|
|
15
15
|
//# sourceMappingURL=worker-thread-v2.cjs.map
|
package/worker-thread-v2.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-thread-v2.cjs","sources":["../../../../packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts"],"sourcesContent":["import { errorLogPath, logger } from '@php-wasm/logger';\nimport type { FileLockManager } from '@php-wasm/universal';\nimport {\n\tbindUserSpace,\n\tcreateNodeFsMountHandler,\n\tloadNodeRuntime,\n\ttype PHPExtension,\n\ttype WasmUserSpaceContext,\n} from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type {\n\tPathAlias,\n\tPHP,\n\tFileTree,\n\tRemoteAPI,\n\tSupportedPHPVersion,\n\tSpawnHandler,\n} from '@php-wasm/universal';\nimport {\n\tPHPExecutionFailureError,\n\tPHPResponse,\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n\tsetPhpIniEntries,\n\twriteFiles,\n} from '@php-wasm/universal';\nimport { joinPaths, sprintf } from '@php-wasm/util';\nimport {\n\ttype BlueprintMessage,\n\trunBlueprintV2,\n\ttype BlueprintV1Declaration,\n} from '@wp-playground/blueprints';\nimport {\n\ttype ParsedBlueprintV2String,\n\ttype RawBlueprintV2Data,\n} from '@wp-playground/blueprints';\nimport {\n\tbootRequestHandler,\n\tpreloadPhpInfoRoute,\n\tsetupPlatformLevelMuPlugins,\n} from '@wp-playground/wordpress';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { type RunCLIArgs, spawnWorkerThread } from '../run-cli';\nimport type {\n\tPhpIniOptions,\n\tPHPInstanceCreatedHook,\n} from '@wp-playground/wordpress';\nimport { shouldRenderProgress } from '../utils/progress';\nimport type { Mount } from '@php-wasm/cli-util';\n\nasync function mountResources(php: PHP, mounts: Mount[]) {\n\tfor (const mount of mounts) {\n\t\ttry {\n\t\t\tphp.mkdir(mount.vfsPath);\n\t\t\tawait php.mount(\n\t\t\t\tmount.vfsPath,\n\t\t\t\tcreateNodeFsMountHandler(mount.hostPath)\n\t\t\t);\n\t\t} catch {\n\t\t\toutput.stderr(\n\t\t\t\t`\\x1b[31m\\x1b[1mError mounting path ${mount.hostPath} at ${mount.vfsPath}\\x1b[0m\\n`\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\n/**\n * Force TTY status to preserve ANSI control codes in the output\n * when the environment is interactive.\n *\n * This script is spawned as `new Worker()` and process.stdout and process.stderr are\n * WritableWorkerStdio objects. By default, they strip ANSI control codes from the output\n * causing every progress bar update to be printed in a new line instead of updating the\n * same line.\n */\nObject.defineProperty(process.stdout, 'isTTY', { value: true });\nObject.defineProperty(process.stderr, 'isTTY', { value: true });\n\n/**\n * Output writer that ensures that progress bars are not printed on the same line as other output.\n */\nconst output = {\n\tlastWriteWasProgress: false,\n\tprogress(data: string) {\n\t\tif (!shouldRenderProgress(process.stdout)) {\n\t\t\treturn;\n\t\t}\n\t\tif (!process.stdout.isTTY) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(data);\n\t\t} else {\n\t\t\tif (!output.lastWriteWasProgress) {\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\t\t\tprocess.stdout.write('\\r\\x1b[K' + data);\n\t\t\toutput.lastWriteWasProgress = true;\n\t\t}\n\t},\n\tstdout(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stdout.write(data);\n\t},\n\tstderr(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stderr.write(data);\n\t},\n};\n\nexport type WorkerWordPressBootArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n};\n\ntype WorkerRunBlueprintArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type SecondaryWorkerBootArgs = {\n\tsiteUrl: string;\n\tallow?: string;\n\tphpVersion: SupportedPHPVersion;\n\tphpIniEntries?: PhpIniOptions;\n\tconstants?: Record<string, string | number | boolean | null>;\n\tcreateFiles?: FileTree;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n\tmountsBeforeWpInstall?: Array<Mount>;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type WorkerBootRequestHandlerOptions = Omit<\n\tSecondaryWorkerBootArgs,\n\t'mountsBeforeWpInstall' | 'mountsAfterWpInstall'\n> & {\n\tonPHPInstanceCreated: PHPInstanceCreatedHook;\n\tspawnHandler: () => SpawnHandler;\n};\n\nexport class PlaygroundCliBlueprintV2Worker extends PHPWorker {\n\tbooted = false;\n\tblueprintTargetResolved = false;\n\tphpInstancesThatNeedMountsAfterTargetResolved = new Set<PHP>();\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\t/**\n\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t * until the result is available.\n\t\t *\n\t\t * @see comlink-sync.ts\n\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t */\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\targs: WorkerWordPressBootArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\t// TODO: Should we move a process like this back into the\n\t\t// `@wp-playground/wordpress` package?\n\t\tconst php = await this.__internal_getRequestHandler()!.getPrimaryPhp();\n\t\tphp.defineConstant('WP_DEBUG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_LOG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_DISPLAY', 'false');\n\t\tphp.defineConstant('WP_HOME', args.siteUrl);\n\t\tphp.defineConstant('WP_SITEURL', args.siteUrl);\n\n\t\tawait setPhpIniEntries(php, {\n\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\tallow_url_fopen: '1',\n\t\t\tdisable_functions: '',\n\t\t});\n\n\t\tawait setupPlatformLevelMuPlugins(php);\n\t\tawait writeFiles(php, '/', {\n\t\t\t'/internal/shared/ca-bundle.crt': rootCertificates.join('\\n'),\n\t\t});\n\t\tawait preloadPhpInfoRoute(\n\t\t\tphp,\n\t\t\tjoinPaths(new URL(args.siteUrl).pathname, 'phpinfo.php')\n\t\t);\n\n\t\tif (args.mode === 'mount-only') {\n\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\tworkerPostInstallMountsPort\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.runBlueprintV2(\n\t\t\t{\n\t\t\t\t// TODO: Do we really want to create a new object or can we pass args directly?\n\t\t\t\t...args,\n\t\t\t},\n\t\t\tworkerPostInstallMountsPort\n\t\t);\n\t}\n\n\tasync bootWorker(args: SecondaryWorkerBootArgs) {\n\t\tawait this.bootRequestHandler({\n\t\t\t...args,\n\t\t\tonPHPInstanceCreated: async (php: PHP) => {\n\t\t\t\tawait mountResources(php, args.mountsBeforeWpInstall || []);\n\t\t\t\tawait mountResources(php, args.mountsAfterWpInstall || []);\n\n\t\t\t\t// Temporary workaround for LOCK_EX in sqlite-database-integration.\n\t\t\t\t// Creation of these files results in this error:\n\t\t\t\t// PHP Warning: file_put_contents(): Exclusive locks are not supported for this stream\n\t\t\t\t// in\n\t\t\t\t// /wordpress/wp-content/plugins/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php\n\t\t\t\t// on line 670\n\t\t\t\tif (!php.isDir('/wordpress/wp-content')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content');\n\t\t\t\t}\n\t\t\t\tif (!php.isDir('/wordpress/wp-content/database')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content/database');\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/.htaccess')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/.htaccess',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/index.php')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/index.php',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tspawnHandler: () =>\n\t\t\t\tsandboxedSpawnHandlerFactory(() =>\n\t\t\t\t\tcreatePHPWorker(args, this.fileLockManager!)\n\t\t\t\t),\n\t\t});\n\t}\n\n\tasync runBlueprintV2(\n\t\targs: WorkerRunBlueprintArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tconst requestHandler = this.__internal_getRequestHandler()!;\n\t\tconst { php, reap } =\n\t\t\tawait requestHandler.instanceManager.acquirePHPInstance();\n\n\t\t// Mount the current working directory to the PHP runtime for the purposes of\n\t\t// Blueprint resolution.\n\t\tconst primaryPhp = this.__internal_getPHP()!;\n\t\tlet unmountCwd = () => {};\n\t\tif (typeof args.blueprint === 'string') {\n\t\t\tconst blueprintPath = path.resolve(process.cwd(), args.blueprint);\n\t\t\tif (existsSync(blueprintPath)) {\n\t\t\t\tprimaryPhp.mkdir('/internal/shared/cwd');\n\t\t\t\tunmountCwd = await primaryPhp.mount(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tcreateNodeFsMountHandler(path.dirname(blueprintPath))\n\t\t\t\t);\n\t\t\t\targs.blueprint = path.join(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tpath.basename(args.blueprint)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst cliArgsToPass: (keyof WorkerRunBlueprintArgs)[] = [\n\t\t\t\t'mode',\n\t\t\t\t'db-engine',\n\t\t\t\t'db-host',\n\t\t\t\t'db-user',\n\t\t\t\t'db-pass',\n\t\t\t\t'db-name',\n\t\t\t\t'db-path',\n\t\t\t\t'truncate-new-site-directory',\n\t\t\t\t'allow',\n\t\t\t];\n\t\t\tconst cliArgs = cliArgsToPass\n\t\t\t\t.filter((arg) => arg in args)\n\t\t\t\t.map((arg) => `--${arg}=${args[arg]}`);\n\t\t\tcliArgs.push(`--site-url=${args.siteUrl}`);\n\n\t\t\tconst streamedResponse = await runBlueprintV2({\n\t\t\t\tphp,\n\t\t\t\tblueprint: args.blueprint,\n\t\t\t\tblueprintOverrides: {\n\t\t\t\t\tadditionalSteps: args['additional-blueprint-steps'],\n\t\t\t\t\twordpressVersion: args.wp,\n\t\t\t\t},\n\t\t\t\tcliArgs,\n\t\t\t\tonMessage: async (message: BlueprintMessage) => {\n\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\tcase 'blueprint.target_resolved': {\n\t\t\t\t\t\t\tif (!this.blueprintTargetResolved) {\n\t\t\t\t\t\t\t\tthis.blueprintTargetResolved = true;\n\t\t\t\t\t\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\t\t\t\t\t\tworkerPostInstallMountsPort\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.progress': {\n\t\t\t\t\t\t\tconst progressMessage = `${message.caption.trim()} – ${message.progress.toFixed(\n\t\t\t\t\t\t\t\t2\n\t\t\t\t\t\t\t)}%`;\n\t\t\t\t\t\t\toutput.progress(progressMessage);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.error': {\n\t\t\t\t\t\t\tconst red = '\\x1b[31m';\n\t\t\t\t\t\t\tconst bold = '\\x1b[1m';\n\t\t\t\t\t\t\tconst reset = '\\x1b[0m';\n\t\t\t\t\t\t\tif (args.verbosity === 'debug' && message.details) {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Fatal error:${reset} Uncaught ${message.details.exception}: ${message.details.message}\\n` +\n\t\t\t\t\t\t\t\t\t\t` at ${message.details.file}:${message.details.line}\\n` +\n\t\t\t\t\t\t\t\t\t\t(message.details.trace\n\t\t\t\t\t\t\t\t\t\t\t? message.details.trace + '\\n'\n\t\t\t\t\t\t\t\t\t\t\t: '')\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Error:${reset} ${message.message}\\n`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\t/**\n\t\t\t * When we're debugging, every bit of information matters – let's immediately output\n\t\t\t * everything we get from the PHP output streams.\n\t\t\t */\n\t\t\tif (args.verbosity === 'debug') {\n\t\t\t\tstreamedResponse!.stdout.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stdout.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\tstreamedResponse!.stderr.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stderr.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait streamedResponse!.finished;\n\t\t\tif ((await streamedResponse!.exitCode) !== 0) {\n\t\t\t\t// exitCode != 1 means the blueprint execution failed. Let's throw an error.\n\t\t\t\t// and clean up.\n\t\t\t\tconst syncResponse =\n\t\t\t\t\tawait PHPResponse.fromStreamedResponse(streamedResponse);\n\t\t\t\tthrow new PHPExecutionFailureError(\n\t\t\t\t\t`PHP.run() failed with exit code ${syncResponse.exitCode}. ${syncResponse.errors} ${syncResponse.text}`,\n\t\t\t\t\tsyncResponse,\n\t\t\t\t\t'request'\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Capture the PHP error log details to provide more context for debugging.\n\t\t\tlet phpLogs = '';\n\t\t\ttry {\n\t\t\t\t// @TODO: Don't assume errorLogPath starts with /wordpress/\n\t\t\t\t// ...or maybe we can assume that in Playground CLI?\n\t\t\t\tphpLogs = php.readFileAsText(errorLogPath);\n\t\t\t} catch {\n\t\t\t\t// Ignore errors reading the PHP error log.\n\t\t\t}\n\t\t\t(error as any).phpLogs = phpLogs;\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\treap();\n\t\t\tunmountCwd();\n\t\t}\n\t}\n\n\tasync bootRequestHandler({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tprocessId,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tonPHPInstanceCreated,\n\t\tspawnHandler,\n\t}: WorkerBootRequestHandlerOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t\tENV: {\n\t\t\t\t\t\t\t\tDOCROOT: '/wordpress',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnativeInternalDirPath,\n\t\t\t\t\t\t\tbindUserSpace: (\n\t\t\t\t\t\t\t\tuserSpaceContext: WasmUserSpaceContext\n\t\t\t\t\t\t\t) => {\n\t\t\t\t\t\t\t\treturn bindUserSpace(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tuserSpaceContext\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks: allow?.includes('follow-symlinks'),\n\t\t\t\t\t\textensions,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tonPHPInstanceCreated,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles,\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries,\n\t\t\t\tpathAliases,\n\t\t\t\tcookieStore: false,\n\t\t\t\tspawnHandler,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\tasync applyPostInstallMountsToAllWorkers(\n\t\tpostInstallMountsPort: MessagePort\n\t): Promise<void> {\n\t\tconst applyPostInstallMountsToAllWorkers = consumeAPI<\n\t\t\t() => Promise<void>\n\t\t>(postInstallMountsPort);\n\t\tawait applyPostInstallMountsToAllWorkers();\n\t\tapplyPostInstallMountsToAllWorkers[releaseApiProxy]();\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t{\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t}: // NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\tOmit<SecondaryWorkerBootArgs, 'processId'>,\n\tfileLockManager: FileLockManager | RemoteAPI<FileLockManager>\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v2');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV2Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootWorker({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\tprocessId: spawnedWorker.processId,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV2Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["mountResources","php","mounts","mount","createNodeFsMountHandler","output","tracePhpWasm","processId","format","args","sprintf","data","shouldRenderProgress","PlaygroundCliBlueprintV2Worker","PHPWorker","monitor","port","consumeAPISync","workerPostInstallMountsPort","setPhpIniEntries","setupPlatformLevelMuPlugins","writeFiles","rootCertificates","preloadPhpInfoRoute","joinPaths","sandboxedSpawnHandlerFactory","createPHPWorker","requestHandler","reap","primaryPhp","unmountCwd","blueprintPath","path","existsSync","cliArgs","arg","streamedResponse","runBlueprintV2","message","progressMessage","red","bold","reset","chunk","syncResponse","PHPResponse","PHPExecutionFailureError","error","phpLogs","errorLogPath","siteUrl","allow","phpVersion","createFiles","constants","phpIniEntries","trace","nativeInternalDirPath","extensions","pathAliases","onPHPInstanceCreated","spawnHandler","bootRequestHandler","loadNodeRuntime","userSpaceContext","bindUserSpace","setApiReady","e","setAPIError","postInstallMountsPort","applyPostInstallMountsToAllWorkers","consumeAPI","releaseApiProxy","mountsBeforeWpInstall","mountsAfterWpInstall","fileLockManager","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker-thread-v2.cjs","sources":["../../../../packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts"],"sourcesContent":["import { errorLogPath, logger } from '@php-wasm/logger';\nimport type { FileLockManager } from '@php-wasm/universal';\nimport {\n\tbindUserSpace,\n\tcreateNodeFsMountHandler,\n\tloadNodeRuntime,\n\ttype PHPExtension,\n\ttype WasmUserSpaceContext,\n} from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type {\n\tPathAlias,\n\tPHP,\n\tFileTree,\n\tRemoteAPI,\n\tSupportedPHPVersion,\n\tSpawnHandler,\n} from '@php-wasm/universal';\nimport {\n\tPHPExecutionFailureError,\n\tPHPResponse,\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n\tsetPhpIniEntries,\n\twriteFiles,\n} from '@php-wasm/universal';\nimport { joinPaths, sprintf } from '@php-wasm/util';\nimport {\n\ttype BlueprintMessage,\n\trunBlueprintV2,\n\ttype BlueprintV1Declaration,\n} from '@wp-playground/blueprints';\nimport {\n\ttype ParsedBlueprintV2String,\n\ttype RawBlueprintV2Data,\n} from '@wp-playground/blueprints';\nimport {\n\tbootRequestHandler,\n\tpreloadPhpInfoRoute,\n\tsetupPlatformLevelMuPlugins,\n} from '@wp-playground/wordpress';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { type RunCLIArgs, spawnWorkerThread } from '../run-cli';\nimport type {\n\tPhpIniOptions,\n\tPHPInstanceCreatedHook,\n} from '@wp-playground/wordpress';\nimport { shouldRenderProgress } from '../utils/progress';\nimport type { Mount } from '@php-wasm/cli-util';\n\nasync function mountResources(php: PHP, mounts: Mount[]) {\n\tfor (const mount of mounts) {\n\t\ttry {\n\t\t\tphp.mkdir(mount.vfsPath);\n\t\t\tawait php.mount(\n\t\t\t\tmount.vfsPath,\n\t\t\t\tcreateNodeFsMountHandler(mount.hostPath)\n\t\t\t);\n\t\t} catch {\n\t\t\toutput.stderr(\n\t\t\t\t`\\x1b[31m\\x1b[1mError mounting path ${mount.hostPath} at ${mount.vfsPath}\\x1b[0m\\n`\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\n/**\n * Force TTY status to preserve ANSI control codes in the output\n * when the environment is interactive.\n *\n * This script is spawned as `new Worker()` and process.stdout and process.stderr are\n * WritableWorkerStdio objects. By default, they strip ANSI control codes from the output\n * causing every progress bar update to be printed in a new line instead of updating the\n * same line.\n */\nObject.defineProperty(process.stdout, 'isTTY', { value: true });\nObject.defineProperty(process.stderr, 'isTTY', { value: true });\n\n/**\n * Output writer that ensures that progress bars are not printed on the same line as other output.\n */\nconst output = {\n\tlastWriteWasProgress: false,\n\tprogress(data: string) {\n\t\tif (!shouldRenderProgress(process.stdout)) {\n\t\t\treturn;\n\t\t}\n\t\tif (!process.stdout.isTTY) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(data);\n\t\t} else {\n\t\t\tif (!output.lastWriteWasProgress) {\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\t\t\tprocess.stdout.write('\\r\\x1b[K' + data);\n\t\t\toutput.lastWriteWasProgress = true;\n\t\t}\n\t},\n\tstdout(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stdout.write(data);\n\t},\n\tstderr(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stderr.write(data);\n\t},\n};\n\nexport type WorkerWordPressBootArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n};\n\ntype WorkerRunBlueprintArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type SecondaryWorkerBootArgs = {\n\tsiteUrl: string;\n\tallow?: string;\n\tphpVersion: SupportedPHPVersion;\n\tphpIniEntries?: PhpIniOptions;\n\tconstants?: Record<string, string | number | boolean | null>;\n\tcreateFiles?: FileTree;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n\tmountsBeforeWpInstall?: Array<Mount>;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type WorkerBootRequestHandlerOptions = Omit<\n\tSecondaryWorkerBootArgs,\n\t'mountsBeforeWpInstall' | 'mountsAfterWpInstall'\n> & {\n\tonPHPInstanceCreated: PHPInstanceCreatedHook;\n\tspawnHandler: () => SpawnHandler;\n};\n\nexport class PlaygroundCliBlueprintV2Worker extends PHPWorker {\n\tbooted = false;\n\tblueprintTargetResolved = false;\n\tphpInstancesThatNeedMountsAfterTargetResolved = new Set<PHP>();\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\t/**\n\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t * until the result is available.\n\t\t *\n\t\t * @see comlink-sync.ts\n\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t */\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\targs: WorkerWordPressBootArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\t// TODO: Should we move a process like this back into the\n\t\t// `@wp-playground/wordpress` package?\n\t\tconst php = await this.__internal_getRequestHandler()!.getPrimaryPhp();\n\t\tphp.defineConstant('WP_DEBUG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_LOG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_DISPLAY', 'false');\n\t\tphp.defineConstant('WP_HOME', args.siteUrl);\n\t\tphp.defineConstant('WP_SITEURL', args.siteUrl);\n\n\t\tawait setPhpIniEntries(php, {\n\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t'curl.cainfo': '/internal/shared/ca-bundle.crt',\n\t\t\tallow_url_fopen: '1',\n\t\t\tdisable_functions: '',\n\t\t});\n\n\t\tawait setupPlatformLevelMuPlugins(php);\n\t\tawait writeFiles(php, '/', {\n\t\t\t'/internal/shared/ca-bundle.crt': rootCertificates.join('\\n'),\n\t\t});\n\t\tawait preloadPhpInfoRoute(\n\t\t\tphp,\n\t\t\tjoinPaths(new URL(args.siteUrl).pathname, 'phpinfo.php')\n\t\t);\n\n\t\tif (args.mode === 'mount-only') {\n\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\tworkerPostInstallMountsPort\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.runBlueprintV2(\n\t\t\t{\n\t\t\t\t// TODO: Do we really want to create a new object or can we pass args directly?\n\t\t\t\t...args,\n\t\t\t},\n\t\t\tworkerPostInstallMountsPort\n\t\t);\n\t}\n\n\tasync bootWorker(args: SecondaryWorkerBootArgs) {\n\t\tawait this.bootRequestHandler({\n\t\t\t...args,\n\t\t\tonPHPInstanceCreated: async (php: PHP) => {\n\t\t\t\tawait mountResources(php, args.mountsBeforeWpInstall || []);\n\t\t\t\tawait mountResources(php, args.mountsAfterWpInstall || []);\n\n\t\t\t\t// Temporary workaround for LOCK_EX in sqlite-database-integration.\n\t\t\t\t// Creation of these files results in this error:\n\t\t\t\t// PHP Warning: file_put_contents(): Exclusive locks are not supported for this stream\n\t\t\t\t// in\n\t\t\t\t// /wordpress/wp-content/plugins/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php\n\t\t\t\t// on line 670\n\t\t\t\tif (!php.isDir('/wordpress/wp-content')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content');\n\t\t\t\t}\n\t\t\t\tif (!php.isDir('/wordpress/wp-content/database')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content/database');\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/.htaccess')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/.htaccess',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/index.php')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/index.php',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tspawnHandler: () =>\n\t\t\t\tsandboxedSpawnHandlerFactory(() =>\n\t\t\t\t\tcreatePHPWorker(args, this.fileLockManager!)\n\t\t\t\t),\n\t\t});\n\t}\n\n\tasync runBlueprintV2(\n\t\targs: WorkerRunBlueprintArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tconst requestHandler = this.__internal_getRequestHandler()!;\n\t\tconst { php, reap } =\n\t\t\tawait requestHandler.instanceManager.acquirePHPInstance();\n\n\t\t// Mount the current working directory to the PHP runtime for the purposes of\n\t\t// Blueprint resolution.\n\t\tconst primaryPhp = this.__internal_getPHP()!;\n\t\tlet unmountCwd = () => {};\n\t\tif (typeof args.blueprint === 'string') {\n\t\t\tconst blueprintPath = path.resolve(process.cwd(), args.blueprint);\n\t\t\tif (existsSync(blueprintPath)) {\n\t\t\t\tprimaryPhp.mkdir('/internal/shared/cwd');\n\t\t\t\tunmountCwd = await primaryPhp.mount(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tcreateNodeFsMountHandler(path.dirname(blueprintPath))\n\t\t\t\t);\n\t\t\t\targs.blueprint = path.join(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tpath.basename(args.blueprint)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst cliArgsToPass: (keyof WorkerRunBlueprintArgs)[] = [\n\t\t\t\t'mode',\n\t\t\t\t'db-engine',\n\t\t\t\t'db-host',\n\t\t\t\t'db-user',\n\t\t\t\t'db-pass',\n\t\t\t\t'db-name',\n\t\t\t\t'db-path',\n\t\t\t\t'truncate-new-site-directory',\n\t\t\t\t'allow',\n\t\t\t];\n\t\t\tconst cliArgs = cliArgsToPass\n\t\t\t\t.filter((arg) => arg in args)\n\t\t\t\t.map((arg) => `--${arg}=${args[arg]}`);\n\t\t\tcliArgs.push(`--site-url=${args.siteUrl}`);\n\n\t\t\tconst streamedResponse = await runBlueprintV2({\n\t\t\t\tphp,\n\t\t\t\tblueprint: args.blueprint,\n\t\t\t\tblueprintOverrides: {\n\t\t\t\t\tadditionalSteps: args['additional-blueprint-steps'],\n\t\t\t\t\twordpressVersion: args.wp,\n\t\t\t\t},\n\t\t\t\tcliArgs,\n\t\t\t\tonMessage: async (message: BlueprintMessage) => {\n\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\tcase 'blueprint.target_resolved': {\n\t\t\t\t\t\t\tif (!this.blueprintTargetResolved) {\n\t\t\t\t\t\t\t\tthis.blueprintTargetResolved = true;\n\t\t\t\t\t\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\t\t\t\t\t\tworkerPostInstallMountsPort\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.progress': {\n\t\t\t\t\t\t\tconst progressMessage = `${message.caption.trim()} – ${message.progress.toFixed(\n\t\t\t\t\t\t\t\t2\n\t\t\t\t\t\t\t)}%`;\n\t\t\t\t\t\t\toutput.progress(progressMessage);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.error': {\n\t\t\t\t\t\t\tconst red = '\\x1b[31m';\n\t\t\t\t\t\t\tconst bold = '\\x1b[1m';\n\t\t\t\t\t\t\tconst reset = '\\x1b[0m';\n\t\t\t\t\t\t\tif (args.verbosity === 'debug' && message.details) {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Fatal error:${reset} Uncaught ${message.details.exception}: ${message.details.message}\\n` +\n\t\t\t\t\t\t\t\t\t\t` at ${message.details.file}:${message.details.line}\\n` +\n\t\t\t\t\t\t\t\t\t\t(message.details.trace\n\t\t\t\t\t\t\t\t\t\t\t? message.details.trace + '\\n'\n\t\t\t\t\t\t\t\t\t\t\t: '')\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Error:${reset} ${message.message}\\n`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\t/**\n\t\t\t * When we're debugging, every bit of information matters – let's immediately output\n\t\t\t * everything we get from the PHP output streams.\n\t\t\t */\n\t\t\tif (args.verbosity === 'debug') {\n\t\t\t\tstreamedResponse!.stdout.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stdout.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\tstreamedResponse!.stderr.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stderr.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait streamedResponse!.finished;\n\t\t\tif ((await streamedResponse!.exitCode) !== 0) {\n\t\t\t\t// exitCode != 1 means the blueprint execution failed. Let's throw an error.\n\t\t\t\t// and clean up.\n\t\t\t\tconst syncResponse =\n\t\t\t\t\tawait PHPResponse.fromStreamedResponse(streamedResponse);\n\t\t\t\tthrow new PHPExecutionFailureError(\n\t\t\t\t\t`PHP.run() failed with exit code ${syncResponse.exitCode}. ${syncResponse.errors} ${syncResponse.text}`,\n\t\t\t\t\tsyncResponse,\n\t\t\t\t\t'request'\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Capture the PHP error log details to provide more context for debugging.\n\t\t\tlet phpLogs = '';\n\t\t\ttry {\n\t\t\t\t// @TODO: Don't assume errorLogPath starts with /wordpress/\n\t\t\t\t// ...or maybe we can assume that in Playground CLI?\n\t\t\t\tphpLogs = php.readFileAsText(errorLogPath);\n\t\t\t} catch {\n\t\t\t\t// Ignore errors reading the PHP error log.\n\t\t\t}\n\t\t\t(error as any).phpLogs = phpLogs;\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\treap();\n\t\t\tunmountCwd();\n\t\t}\n\t}\n\n\tasync bootRequestHandler({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tprocessId,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tonPHPInstanceCreated,\n\t\tspawnHandler,\n\t}: WorkerBootRequestHandlerOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t\tENV: {\n\t\t\t\t\t\t\t\tDOCROOT: '/wordpress',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnativeInternalDirPath,\n\t\t\t\t\t\t\tbindUserSpace: (\n\t\t\t\t\t\t\t\tuserSpaceContext: WasmUserSpaceContext\n\t\t\t\t\t\t\t) => {\n\t\t\t\t\t\t\t\treturn bindUserSpace(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tuserSpaceContext\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks: allow?.includes('follow-symlinks'),\n\t\t\t\t\t\textensions,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tonPHPInstanceCreated,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles,\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries,\n\t\t\t\tpathAliases,\n\t\t\t\tcookieStore: false,\n\t\t\t\tspawnHandler,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\tasync applyPostInstallMountsToAllWorkers(\n\t\tpostInstallMountsPort: MessagePort\n\t): Promise<void> {\n\t\tconst applyPostInstallMountsToAllWorkers = consumeAPI<\n\t\t\t() => Promise<void>\n\t\t>(postInstallMountsPort);\n\t\tawait applyPostInstallMountsToAllWorkers();\n\t\tapplyPostInstallMountsToAllWorkers[releaseApiProxy]();\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t{\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t}: // NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\tOmit<SecondaryWorkerBootArgs, 'processId'>,\n\tfileLockManager: FileLockManager | RemoteAPI<FileLockManager>\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v2');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV2Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootWorker({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\tprocessId: spawnedWorker.processId,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV2Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["mountResources","php","mounts","mount","createNodeFsMountHandler","output","tracePhpWasm","processId","format","args","sprintf","data","shouldRenderProgress","PlaygroundCliBlueprintV2Worker","PHPWorker","monitor","port","consumeAPISync","workerPostInstallMountsPort","setPhpIniEntries","setupPlatformLevelMuPlugins","writeFiles","rootCertificates","preloadPhpInfoRoute","joinPaths","sandboxedSpawnHandlerFactory","createPHPWorker","requestHandler","reap","primaryPhp","unmountCwd","blueprintPath","path","existsSync","cliArgs","arg","streamedResponse","runBlueprintV2","message","progressMessage","red","bold","reset","chunk","syncResponse","PHPResponse","PHPExecutionFailureError","error","phpLogs","errorLogPath","siteUrl","allow","phpVersion","createFiles","constants","phpIniEntries","trace","nativeInternalDirPath","extensions","pathAliases","onPHPInstanceCreated","spawnHandler","bootRequestHandler","loadNodeRuntime","userSpaceContext","bindUserSpace","setApiReady","e","setAPIError","postInstallMountsPort","applyPostInstallMountsToAllWorkers","consumeAPI","releaseApiProxy","mountsBeforeWpInstall","mountsAfterWpInstall","fileLockManager","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
package/worker-thread-v2.js
CHANGED
|
@@ -72,6 +72,7 @@ class J extends $ {
|
|
|
72
72
|
const o = await this.__internal_getRequestHandler().getPrimaryPhp();
|
|
73
73
|
if (o.defineConstant("WP_DEBUG", "true"), o.defineConstant("WP_DEBUG_LOG", "true"), o.defineConstant("WP_DEBUG_DISPLAY", "false"), o.defineConstant("WP_HOME", e.siteUrl), o.defineConstant("WP_SITEURL", e.siteUrl), await v(o, {
|
|
74
74
|
"openssl.cafile": "/internal/shared/ca-bundle.crt",
|
|
75
|
+
"curl.cainfo": "/internal/shared/ca-bundle.crt",
|
|
75
76
|
allow_url_fopen: "1",
|
|
76
77
|
disable_functions: ""
|
|
77
78
|
}), await q(o), await A(o, "/", {
|
|
@@ -155,21 +156,21 @@ class J extends $ {
|
|
|
155
156
|
break;
|
|
156
157
|
}
|
|
157
158
|
case "blueprint.progress": {
|
|
158
|
-
const
|
|
159
|
+
const c = `${r.caption.trim()} – ${r.progress.toFixed(
|
|
159
160
|
2
|
|
160
161
|
)}%`;
|
|
161
|
-
i.progress(
|
|
162
|
+
i.progress(c);
|
|
162
163
|
break;
|
|
163
164
|
}
|
|
164
165
|
case "blueprint.error": {
|
|
165
|
-
const
|
|
166
|
+
const c = "\x1B[31m", p = "\x1B[1m", n = "\x1B[0m";
|
|
166
167
|
e.verbosity === "debug" && r.details ? i.stderr(
|
|
167
|
-
`${
|
|
168
|
+
`${c}${p}Fatal error:${n} Uncaught ${r.details.exception}: ${r.details.message}
|
|
168
169
|
at ${r.details.file}:${r.details.line}
|
|
169
170
|
` + (r.details.trace ? r.details.trace + `
|
|
170
171
|
` : "")
|
|
171
172
|
) : i.stderr(
|
|
172
|
-
`${
|
|
173
|
+
`${c}${p}Error:${n} ${r.message}
|
|
173
174
|
`
|
|
174
175
|
);
|
|
175
176
|
break;
|
|
@@ -220,8 +221,8 @@ class J extends $ {
|
|
|
220
221
|
nativeInternalDirPath: l,
|
|
221
222
|
extensions: d,
|
|
222
223
|
pathAliases: r,
|
|
223
|
-
onPHPInstanceCreated:
|
|
224
|
-
spawnHandler:
|
|
224
|
+
onPHPInstanceCreated: c,
|
|
225
|
+
spawnHandler: p
|
|
225
226
|
}) {
|
|
226
227
|
if (this.booted)
|
|
227
228
|
throw new Error("Playground already booted");
|
|
@@ -249,14 +250,14 @@ class J extends $ {
|
|
|
249
250
|
extensions: d
|
|
250
251
|
}),
|
|
251
252
|
maxPhpInstances: 1,
|
|
252
|
-
onPHPInstanceCreated:
|
|
253
|
+
onPHPInstanceCreated: c,
|
|
253
254
|
sapiName: "cli",
|
|
254
255
|
createFiles: h,
|
|
255
256
|
constants: w,
|
|
256
257
|
phpIniEntries: f,
|
|
257
258
|
pathAliases: r,
|
|
258
259
|
cookieStore: !1,
|
|
259
|
-
spawnHandler:
|
|
260
|
+
spawnHandler: p
|
|
260
261
|
});
|
|
261
262
|
this.__internal_setRequestHandler(n);
|
|
262
263
|
const g = await n.getPrimaryPhp();
|
|
@@ -290,18 +291,18 @@ async function Q({
|
|
|
290
291
|
pathAliases: l,
|
|
291
292
|
mountsBeforeWpInstall: d,
|
|
292
293
|
mountsAfterWpInstall: r
|
|
293
|
-
},
|
|
294
|
-
const
|
|
295
|
-
|
|
294
|
+
}, c) {
|
|
295
|
+
const p = await Y("v2"), n = W(
|
|
296
|
+
p.phpPort
|
|
296
297
|
);
|
|
297
|
-
return n.useFileLockManager(
|
|
298
|
+
return n.useFileLockManager(c), await n.bootWorker({
|
|
298
299
|
siteUrl: s,
|
|
299
300
|
allow: e,
|
|
300
301
|
phpVersion: t,
|
|
301
302
|
createFiles: o,
|
|
302
303
|
constants: u,
|
|
303
304
|
phpIniEntries: h,
|
|
304
|
-
processId:
|
|
305
|
+
processId: p.processId,
|
|
305
306
|
trace: w,
|
|
306
307
|
nativeInternalDirPath: f,
|
|
307
308
|
extensions: a,
|
|
@@ -316,7 +317,7 @@ async function Q({
|
|
|
316
317
|
} catch {
|
|
317
318
|
}
|
|
318
319
|
try {
|
|
319
|
-
|
|
320
|
+
p.worker.terminate();
|
|
320
321
|
} catch {
|
|
321
322
|
}
|
|
322
323
|
}
|
package/worker-thread-v2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-thread-v2.js","sources":["../../../../packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts"],"sourcesContent":["import { errorLogPath, logger } from '@php-wasm/logger';\nimport type { FileLockManager } from '@php-wasm/universal';\nimport {\n\tbindUserSpace,\n\tcreateNodeFsMountHandler,\n\tloadNodeRuntime,\n\ttype PHPExtension,\n\ttype WasmUserSpaceContext,\n} from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type {\n\tPathAlias,\n\tPHP,\n\tFileTree,\n\tRemoteAPI,\n\tSupportedPHPVersion,\n\tSpawnHandler,\n} from '@php-wasm/universal';\nimport {\n\tPHPExecutionFailureError,\n\tPHPResponse,\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n\tsetPhpIniEntries,\n\twriteFiles,\n} from '@php-wasm/universal';\nimport { joinPaths, sprintf } from '@php-wasm/util';\nimport {\n\ttype BlueprintMessage,\n\trunBlueprintV2,\n\ttype BlueprintV1Declaration,\n} from '@wp-playground/blueprints';\nimport {\n\ttype ParsedBlueprintV2String,\n\ttype RawBlueprintV2Data,\n} from '@wp-playground/blueprints';\nimport {\n\tbootRequestHandler,\n\tpreloadPhpInfoRoute,\n\tsetupPlatformLevelMuPlugins,\n} from '@wp-playground/wordpress';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { type RunCLIArgs, spawnWorkerThread } from '../run-cli';\nimport type {\n\tPhpIniOptions,\n\tPHPInstanceCreatedHook,\n} from '@wp-playground/wordpress';\nimport { shouldRenderProgress } from '../utils/progress';\nimport type { Mount } from '@php-wasm/cli-util';\n\nasync function mountResources(php: PHP, mounts: Mount[]) {\n\tfor (const mount of mounts) {\n\t\ttry {\n\t\t\tphp.mkdir(mount.vfsPath);\n\t\t\tawait php.mount(\n\t\t\t\tmount.vfsPath,\n\t\t\t\tcreateNodeFsMountHandler(mount.hostPath)\n\t\t\t);\n\t\t} catch {\n\t\t\toutput.stderr(\n\t\t\t\t`\\x1b[31m\\x1b[1mError mounting path ${mount.hostPath} at ${mount.vfsPath}\\x1b[0m\\n`\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\n/**\n * Force TTY status to preserve ANSI control codes in the output\n * when the environment is interactive.\n *\n * This script is spawned as `new Worker()` and process.stdout and process.stderr are\n * WritableWorkerStdio objects. By default, they strip ANSI control codes from the output\n * causing every progress bar update to be printed in a new line instead of updating the\n * same line.\n */\nObject.defineProperty(process.stdout, 'isTTY', { value: true });\nObject.defineProperty(process.stderr, 'isTTY', { value: true });\n\n/**\n * Output writer that ensures that progress bars are not printed on the same line as other output.\n */\nconst output = {\n\tlastWriteWasProgress: false,\n\tprogress(data: string) {\n\t\tif (!shouldRenderProgress(process.stdout)) {\n\t\t\treturn;\n\t\t}\n\t\tif (!process.stdout.isTTY) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(data);\n\t\t} else {\n\t\t\tif (!output.lastWriteWasProgress) {\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\t\t\tprocess.stdout.write('\\r\\x1b[K' + data);\n\t\t\toutput.lastWriteWasProgress = true;\n\t\t}\n\t},\n\tstdout(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stdout.write(data);\n\t},\n\tstderr(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stderr.write(data);\n\t},\n};\n\nexport type WorkerWordPressBootArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n};\n\ntype WorkerRunBlueprintArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type SecondaryWorkerBootArgs = {\n\tsiteUrl: string;\n\tallow?: string;\n\tphpVersion: SupportedPHPVersion;\n\tphpIniEntries?: PhpIniOptions;\n\tconstants?: Record<string, string | number | boolean | null>;\n\tcreateFiles?: FileTree;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n\tmountsBeforeWpInstall?: Array<Mount>;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type WorkerBootRequestHandlerOptions = Omit<\n\tSecondaryWorkerBootArgs,\n\t'mountsBeforeWpInstall' | 'mountsAfterWpInstall'\n> & {\n\tonPHPInstanceCreated: PHPInstanceCreatedHook;\n\tspawnHandler: () => SpawnHandler;\n};\n\nexport class PlaygroundCliBlueprintV2Worker extends PHPWorker {\n\tbooted = false;\n\tblueprintTargetResolved = false;\n\tphpInstancesThatNeedMountsAfterTargetResolved = new Set<PHP>();\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\t/**\n\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t * until the result is available.\n\t\t *\n\t\t * @see comlink-sync.ts\n\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t */\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\targs: WorkerWordPressBootArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\t// TODO: Should we move a process like this back into the\n\t\t// `@wp-playground/wordpress` package?\n\t\tconst php = await this.__internal_getRequestHandler()!.getPrimaryPhp();\n\t\tphp.defineConstant('WP_DEBUG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_LOG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_DISPLAY', 'false');\n\t\tphp.defineConstant('WP_HOME', args.siteUrl);\n\t\tphp.defineConstant('WP_SITEURL', args.siteUrl);\n\n\t\tawait setPhpIniEntries(php, {\n\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\tallow_url_fopen: '1',\n\t\t\tdisable_functions: '',\n\t\t});\n\n\t\tawait setupPlatformLevelMuPlugins(php);\n\t\tawait writeFiles(php, '/', {\n\t\t\t'/internal/shared/ca-bundle.crt': rootCertificates.join('\\n'),\n\t\t});\n\t\tawait preloadPhpInfoRoute(\n\t\t\tphp,\n\t\t\tjoinPaths(new URL(args.siteUrl).pathname, 'phpinfo.php')\n\t\t);\n\n\t\tif (args.mode === 'mount-only') {\n\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\tworkerPostInstallMountsPort\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.runBlueprintV2(\n\t\t\t{\n\t\t\t\t// TODO: Do we really want to create a new object or can we pass args directly?\n\t\t\t\t...args,\n\t\t\t},\n\t\t\tworkerPostInstallMountsPort\n\t\t);\n\t}\n\n\tasync bootWorker(args: SecondaryWorkerBootArgs) {\n\t\tawait this.bootRequestHandler({\n\t\t\t...args,\n\t\t\tonPHPInstanceCreated: async (php: PHP) => {\n\t\t\t\tawait mountResources(php, args.mountsBeforeWpInstall || []);\n\t\t\t\tawait mountResources(php, args.mountsAfterWpInstall || []);\n\n\t\t\t\t// Temporary workaround for LOCK_EX in sqlite-database-integration.\n\t\t\t\t// Creation of these files results in this error:\n\t\t\t\t// PHP Warning: file_put_contents(): Exclusive locks are not supported for this stream\n\t\t\t\t// in\n\t\t\t\t// /wordpress/wp-content/plugins/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php\n\t\t\t\t// on line 670\n\t\t\t\tif (!php.isDir('/wordpress/wp-content')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content');\n\t\t\t\t}\n\t\t\t\tif (!php.isDir('/wordpress/wp-content/database')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content/database');\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/.htaccess')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/.htaccess',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/index.php')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/index.php',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tspawnHandler: () =>\n\t\t\t\tsandboxedSpawnHandlerFactory(() =>\n\t\t\t\t\tcreatePHPWorker(args, this.fileLockManager!)\n\t\t\t\t),\n\t\t});\n\t}\n\n\tasync runBlueprintV2(\n\t\targs: WorkerRunBlueprintArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tconst requestHandler = this.__internal_getRequestHandler()!;\n\t\tconst { php, reap } =\n\t\t\tawait requestHandler.instanceManager.acquirePHPInstance();\n\n\t\t// Mount the current working directory to the PHP runtime for the purposes of\n\t\t// Blueprint resolution.\n\t\tconst primaryPhp = this.__internal_getPHP()!;\n\t\tlet unmountCwd = () => {};\n\t\tif (typeof args.blueprint === 'string') {\n\t\t\tconst blueprintPath = path.resolve(process.cwd(), args.blueprint);\n\t\t\tif (existsSync(blueprintPath)) {\n\t\t\t\tprimaryPhp.mkdir('/internal/shared/cwd');\n\t\t\t\tunmountCwd = await primaryPhp.mount(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tcreateNodeFsMountHandler(path.dirname(blueprintPath))\n\t\t\t\t);\n\t\t\t\targs.blueprint = path.join(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tpath.basename(args.blueprint)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst cliArgsToPass: (keyof WorkerRunBlueprintArgs)[] = [\n\t\t\t\t'mode',\n\t\t\t\t'db-engine',\n\t\t\t\t'db-host',\n\t\t\t\t'db-user',\n\t\t\t\t'db-pass',\n\t\t\t\t'db-name',\n\t\t\t\t'db-path',\n\t\t\t\t'truncate-new-site-directory',\n\t\t\t\t'allow',\n\t\t\t];\n\t\t\tconst cliArgs = cliArgsToPass\n\t\t\t\t.filter((arg) => arg in args)\n\t\t\t\t.map((arg) => `--${arg}=${args[arg]}`);\n\t\t\tcliArgs.push(`--site-url=${args.siteUrl}`);\n\n\t\t\tconst streamedResponse = await runBlueprintV2({\n\t\t\t\tphp,\n\t\t\t\tblueprint: args.blueprint,\n\t\t\t\tblueprintOverrides: {\n\t\t\t\t\tadditionalSteps: args['additional-blueprint-steps'],\n\t\t\t\t\twordpressVersion: args.wp,\n\t\t\t\t},\n\t\t\t\tcliArgs,\n\t\t\t\tonMessage: async (message: BlueprintMessage) => {\n\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\tcase 'blueprint.target_resolved': {\n\t\t\t\t\t\t\tif (!this.blueprintTargetResolved) {\n\t\t\t\t\t\t\t\tthis.blueprintTargetResolved = true;\n\t\t\t\t\t\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\t\t\t\t\t\tworkerPostInstallMountsPort\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.progress': {\n\t\t\t\t\t\t\tconst progressMessage = `${message.caption.trim()} – ${message.progress.toFixed(\n\t\t\t\t\t\t\t\t2\n\t\t\t\t\t\t\t)}%`;\n\t\t\t\t\t\t\toutput.progress(progressMessage);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.error': {\n\t\t\t\t\t\t\tconst red = '\\x1b[31m';\n\t\t\t\t\t\t\tconst bold = '\\x1b[1m';\n\t\t\t\t\t\t\tconst reset = '\\x1b[0m';\n\t\t\t\t\t\t\tif (args.verbosity === 'debug' && message.details) {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Fatal error:${reset} Uncaught ${message.details.exception}: ${message.details.message}\\n` +\n\t\t\t\t\t\t\t\t\t\t` at ${message.details.file}:${message.details.line}\\n` +\n\t\t\t\t\t\t\t\t\t\t(message.details.trace\n\t\t\t\t\t\t\t\t\t\t\t? message.details.trace + '\\n'\n\t\t\t\t\t\t\t\t\t\t\t: '')\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Error:${reset} ${message.message}\\n`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\t/**\n\t\t\t * When we're debugging, every bit of information matters – let's immediately output\n\t\t\t * everything we get from the PHP output streams.\n\t\t\t */\n\t\t\tif (args.verbosity === 'debug') {\n\t\t\t\tstreamedResponse!.stdout.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stdout.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\tstreamedResponse!.stderr.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stderr.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait streamedResponse!.finished;\n\t\t\tif ((await streamedResponse!.exitCode) !== 0) {\n\t\t\t\t// exitCode != 1 means the blueprint execution failed. Let's throw an error.\n\t\t\t\t// and clean up.\n\t\t\t\tconst syncResponse =\n\t\t\t\t\tawait PHPResponse.fromStreamedResponse(streamedResponse);\n\t\t\t\tthrow new PHPExecutionFailureError(\n\t\t\t\t\t`PHP.run() failed with exit code ${syncResponse.exitCode}. ${syncResponse.errors} ${syncResponse.text}`,\n\t\t\t\t\tsyncResponse,\n\t\t\t\t\t'request'\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Capture the PHP error log details to provide more context for debugging.\n\t\t\tlet phpLogs = '';\n\t\t\ttry {\n\t\t\t\t// @TODO: Don't assume errorLogPath starts with /wordpress/\n\t\t\t\t// ...or maybe we can assume that in Playground CLI?\n\t\t\t\tphpLogs = php.readFileAsText(errorLogPath);\n\t\t\t} catch {\n\t\t\t\t// Ignore errors reading the PHP error log.\n\t\t\t}\n\t\t\t(error as any).phpLogs = phpLogs;\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\treap();\n\t\t\tunmountCwd();\n\t\t}\n\t}\n\n\tasync bootRequestHandler({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tprocessId,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tonPHPInstanceCreated,\n\t\tspawnHandler,\n\t}: WorkerBootRequestHandlerOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t\tENV: {\n\t\t\t\t\t\t\t\tDOCROOT: '/wordpress',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnativeInternalDirPath,\n\t\t\t\t\t\t\tbindUserSpace: (\n\t\t\t\t\t\t\t\tuserSpaceContext: WasmUserSpaceContext\n\t\t\t\t\t\t\t) => {\n\t\t\t\t\t\t\t\treturn bindUserSpace(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tuserSpaceContext\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks: allow?.includes('follow-symlinks'),\n\t\t\t\t\t\textensions,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tonPHPInstanceCreated,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles,\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries,\n\t\t\t\tpathAliases,\n\t\t\t\tcookieStore: false,\n\t\t\t\tspawnHandler,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\tasync applyPostInstallMountsToAllWorkers(\n\t\tpostInstallMountsPort: MessagePort\n\t): Promise<void> {\n\t\tconst applyPostInstallMountsToAllWorkers = consumeAPI<\n\t\t\t() => Promise<void>\n\t\t>(postInstallMountsPort);\n\t\tawait applyPostInstallMountsToAllWorkers();\n\t\tapplyPostInstallMountsToAllWorkers[releaseApiProxy]();\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t{\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t}: // NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\tOmit<SecondaryWorkerBootArgs, 'processId'>,\n\tfileLockManager: FileLockManager | RemoteAPI<FileLockManager>\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v2');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV2Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootWorker({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\tprocessId: spawnedWorker.processId,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV2Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["mountResources","php","mounts","mount","createNodeFsMountHandler","output","tracePhpWasm","processId","format","args","sprintf","data","shouldRenderProgress","PlaygroundCliBlueprintV2Worker","PHPWorker","monitor","port","consumeAPISync","workerPostInstallMountsPort","setPhpIniEntries","setupPlatformLevelMuPlugins","writeFiles","rootCertificates","preloadPhpInfoRoute","joinPaths","sandboxedSpawnHandlerFactory","createPHPWorker","requestHandler","reap","primaryPhp","unmountCwd","blueprintPath","path","existsSync","cliArgs","arg","streamedResponse","runBlueprintV2","message","progressMessage","red","bold","reset","chunk","syncResponse","PHPResponse","PHPExecutionFailureError","error","phpLogs","errorLogPath","siteUrl","allow","phpVersion","createFiles","constants","phpIniEntries","trace","nativeInternalDirPath","extensions","pathAliases","onPHPInstanceCreated","spawnHandler","bootRequestHandler","loadNodeRuntime","userSpaceContext","bindUserSpace","setApiReady","e","setAPIError","postInstallMountsPort","applyPostInstallMountsToAllWorkers","consumeAPI","releaseApiProxy","mountsBeforeWpInstall","mountsAfterWpInstall","fileLockManager","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker-thread-v2.js","sources":["../../../../packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts"],"sourcesContent":["import { errorLogPath, logger } from '@php-wasm/logger';\nimport type { FileLockManager } from '@php-wasm/universal';\nimport {\n\tbindUserSpace,\n\tcreateNodeFsMountHandler,\n\tloadNodeRuntime,\n\ttype PHPExtension,\n\ttype WasmUserSpaceContext,\n} from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type {\n\tPathAlias,\n\tPHP,\n\tFileTree,\n\tRemoteAPI,\n\tSupportedPHPVersion,\n\tSpawnHandler,\n} from '@php-wasm/universal';\nimport {\n\tPHPExecutionFailureError,\n\tPHPResponse,\n\tPHPWorker,\n\treleaseApiProxy,\n\tconsumeAPI,\n\tconsumeAPISync,\n\texposeAPI,\n\tsandboxedSpawnHandlerFactory,\n\tsetPhpIniEntries,\n\twriteFiles,\n} from '@php-wasm/universal';\nimport { joinPaths, sprintf } from '@php-wasm/util';\nimport {\n\ttype BlueprintMessage,\n\trunBlueprintV2,\n\ttype BlueprintV1Declaration,\n} from '@wp-playground/blueprints';\nimport {\n\ttype ParsedBlueprintV2String,\n\ttype RawBlueprintV2Data,\n} from '@wp-playground/blueprints';\nimport {\n\tbootRequestHandler,\n\tpreloadPhpInfoRoute,\n\tsetupPlatformLevelMuPlugins,\n} from '@wp-playground/wordpress';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { rootCertificates } from 'tls';\nimport { MessageChannel, type MessagePort, parentPort } from 'worker_threads';\nimport { type RunCLIArgs, spawnWorkerThread } from '../run-cli';\nimport type {\n\tPhpIniOptions,\n\tPHPInstanceCreatedHook,\n} from '@wp-playground/wordpress';\nimport { shouldRenderProgress } from '../utils/progress';\nimport type { Mount } from '@php-wasm/cli-util';\n\nasync function mountResources(php: PHP, mounts: Mount[]) {\n\tfor (const mount of mounts) {\n\t\ttry {\n\t\t\tphp.mkdir(mount.vfsPath);\n\t\t\tawait php.mount(\n\t\t\t\tmount.vfsPath,\n\t\t\t\tcreateNodeFsMountHandler(mount.hostPath)\n\t\t\t);\n\t\t} catch {\n\t\t\toutput.stderr(\n\t\t\t\t`\\x1b[31m\\x1b[1mError mounting path ${mount.hostPath} at ${mount.vfsPath}\\x1b[0m\\n`\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n}\n\n/**\n * Print trace messages from PHP-WASM.\n *\n * @param {number} processId - The process ID.\n * @param {string} format - The format string.\n * @param {...any} args - The arguments.\n */\nfunction tracePhpWasm(processId: number, format: string, ...args: any[]) {\n\t// eslint-disable-next-line no-console\n\tconsole.log(\n\t\tperformance.now().toFixed(6).padStart(15, '0'),\n\t\tprocessId.toString().padStart(16, '0'),\n\t\tsprintf(format, ...args)\n\t);\n}\n\n/**\n * Force TTY status to preserve ANSI control codes in the output\n * when the environment is interactive.\n *\n * This script is spawned as `new Worker()` and process.stdout and process.stderr are\n * WritableWorkerStdio objects. By default, they strip ANSI control codes from the output\n * causing every progress bar update to be printed in a new line instead of updating the\n * same line.\n */\nObject.defineProperty(process.stdout, 'isTTY', { value: true });\nObject.defineProperty(process.stderr, 'isTTY', { value: true });\n\n/**\n * Output writer that ensures that progress bars are not printed on the same line as other output.\n */\nconst output = {\n\tlastWriteWasProgress: false,\n\tprogress(data: string) {\n\t\tif (!shouldRenderProgress(process.stdout)) {\n\t\t\treturn;\n\t\t}\n\t\tif (!process.stdout.isTTY) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(data);\n\t\t} else {\n\t\t\tif (!output.lastWriteWasProgress) {\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\t\t\tprocess.stdout.write('\\r\\x1b[K' + data);\n\t\t\toutput.lastWriteWasProgress = true;\n\t\t}\n\t},\n\tstdout(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stdout.write(data);\n\t},\n\tstderr(data: string) {\n\t\tprocess.stdout.write('\\n\\n\\n');\n\t\tif (output.lastWriteWasProgress) {\n\t\t\toutput.lastWriteWasProgress = false;\n\t\t}\n\t\tprocess.stderr.write(data);\n\t},\n};\n\nexport type WorkerWordPressBootArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n};\n\ntype WorkerRunBlueprintArgs = Omit<\n\tRunCLIArgs,\n\t'mount-before-install' | 'mount'\n> & {\n\tsiteUrl: string;\n\tblueprint:\n\t\t| RawBlueprintV2Data\n\t\t| ParsedBlueprintV2String\n\t\t| BlueprintV1Declaration;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type SecondaryWorkerBootArgs = {\n\tsiteUrl: string;\n\tallow?: string;\n\tphpVersion: SupportedPHPVersion;\n\tphpIniEntries?: PhpIniOptions;\n\tconstants?: Record<string, string | number | boolean | null>;\n\tcreateFiles?: FileTree;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\textensions?: PHPExtension[];\n\tpathAliases?: PathAlias[];\n\tmountsBeforeWpInstall?: Array<Mount>;\n\tmountsAfterWpInstall?: Array<Mount>;\n};\n\nexport type WorkerBootRequestHandlerOptions = Omit<\n\tSecondaryWorkerBootArgs,\n\t'mountsBeforeWpInstall' | 'mountsAfterWpInstall'\n> & {\n\tonPHPInstanceCreated: PHPInstanceCreatedHook;\n\tspawnHandler: () => SpawnHandler;\n};\n\nexport class PlaygroundCliBlueprintV2Worker extends PHPWorker {\n\tbooted = false;\n\tblueprintTargetResolved = false;\n\tphpInstancesThatNeedMountsAfterTargetResolved = new Set<PHP>();\n\tfileLockManager: FileLockManager | undefined;\n\n\tconstructor(monitor: EmscriptenDownloadMonitor) {\n\t\tsuper(undefined, monitor);\n\t}\n\n\t/**\n\t * Call this method before boot() to use file locking.\n\t *\n\t * This method is separate from boot() to simplify the related Comlink.transferHandlers\n\t * setup – if an argument is a MessagePort, we're transferring it, not copying it.\n\t *\n\t * @see comlink-sync.ts\n\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t */\n\tasync useFileLockManager(port: MessagePort) {\n\t\t/**\n\t\t * If JSPI is not available, php.js only supports synchronous locking syscalls.\n\t\t * Let's use the synchronous API. Every method call will block this thread\n\t\t * until the result is available.\n\t\t *\n\t\t * @see comlink-sync.ts\n\t\t * @see phpwasm-emscripten-library-file-locking-for-node.js\n\t\t */\n\t\tthis.fileLockManager = await consumeAPISync<FileLockManager>(port);\n\t}\n\n\tasync bootWordPress(\n\t\targs: WorkerWordPressBootArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\t// TODO: Should we move a process like this back into the\n\t\t// `@wp-playground/wordpress` package?\n\t\tconst php = await this.__internal_getRequestHandler()!.getPrimaryPhp();\n\t\tphp.defineConstant('WP_DEBUG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_LOG', 'true');\n\t\tphp.defineConstant('WP_DEBUG_DISPLAY', 'false');\n\t\tphp.defineConstant('WP_HOME', args.siteUrl);\n\t\tphp.defineConstant('WP_SITEURL', args.siteUrl);\n\n\t\tawait setPhpIniEntries(php, {\n\t\t\t'openssl.cafile': '/internal/shared/ca-bundle.crt',\n\t\t\t'curl.cainfo': '/internal/shared/ca-bundle.crt',\n\t\t\tallow_url_fopen: '1',\n\t\t\tdisable_functions: '',\n\t\t});\n\n\t\tawait setupPlatformLevelMuPlugins(php);\n\t\tawait writeFiles(php, '/', {\n\t\t\t'/internal/shared/ca-bundle.crt': rootCertificates.join('\\n'),\n\t\t});\n\t\tawait preloadPhpInfoRoute(\n\t\t\tphp,\n\t\t\tjoinPaths(new URL(args.siteUrl).pathname, 'phpinfo.php')\n\t\t);\n\n\t\tif (args.mode === 'mount-only') {\n\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\tworkerPostInstallMountsPort\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.runBlueprintV2(\n\t\t\t{\n\t\t\t\t// TODO: Do we really want to create a new object or can we pass args directly?\n\t\t\t\t...args,\n\t\t\t},\n\t\t\tworkerPostInstallMountsPort\n\t\t);\n\t}\n\n\tasync bootWorker(args: SecondaryWorkerBootArgs) {\n\t\tawait this.bootRequestHandler({\n\t\t\t...args,\n\t\t\tonPHPInstanceCreated: async (php: PHP) => {\n\t\t\t\tawait mountResources(php, args.mountsBeforeWpInstall || []);\n\t\t\t\tawait mountResources(php, args.mountsAfterWpInstall || []);\n\n\t\t\t\t// Temporary workaround for LOCK_EX in sqlite-database-integration.\n\t\t\t\t// Creation of these files results in this error:\n\t\t\t\t// PHP Warning: file_put_contents(): Exclusive locks are not supported for this stream\n\t\t\t\t// in\n\t\t\t\t// /wordpress/wp-content/plugins/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php\n\t\t\t\t// on line 670\n\t\t\t\tif (!php.isDir('/wordpress/wp-content')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content');\n\t\t\t\t}\n\t\t\t\tif (!php.isDir('/wordpress/wp-content/database')) {\n\t\t\t\t\tphp.mkdir('/wordpress/wp-content/database');\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/.htaccess')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/.htaccess',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!php.isFile('/wordpress/wp-content/database/index.php')) {\n\t\t\t\t\tphp.writeFile(\n\t\t\t\t\t\t'/wordpress/wp-content/database/index.php',\n\t\t\t\t\t\t'deny from all'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tspawnHandler: () =>\n\t\t\t\tsandboxedSpawnHandlerFactory(() =>\n\t\t\t\t\tcreatePHPWorker(args, this.fileLockManager!)\n\t\t\t\t),\n\t\t});\n\t}\n\n\tasync runBlueprintV2(\n\t\targs: WorkerRunBlueprintArgs,\n\t\tworkerPostInstallMountsPort: MessagePort\n\t) {\n\t\tconst requestHandler = this.__internal_getRequestHandler()!;\n\t\tconst { php, reap } =\n\t\t\tawait requestHandler.instanceManager.acquirePHPInstance();\n\n\t\t// Mount the current working directory to the PHP runtime for the purposes of\n\t\t// Blueprint resolution.\n\t\tconst primaryPhp = this.__internal_getPHP()!;\n\t\tlet unmountCwd = () => {};\n\t\tif (typeof args.blueprint === 'string') {\n\t\t\tconst blueprintPath = path.resolve(process.cwd(), args.blueprint);\n\t\t\tif (existsSync(blueprintPath)) {\n\t\t\t\tprimaryPhp.mkdir('/internal/shared/cwd');\n\t\t\t\tunmountCwd = await primaryPhp.mount(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tcreateNodeFsMountHandler(path.dirname(blueprintPath))\n\t\t\t\t);\n\t\t\t\targs.blueprint = path.join(\n\t\t\t\t\t'/internal/shared/cwd',\n\t\t\t\t\tpath.basename(args.blueprint)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst cliArgsToPass: (keyof WorkerRunBlueprintArgs)[] = [\n\t\t\t\t'mode',\n\t\t\t\t'db-engine',\n\t\t\t\t'db-host',\n\t\t\t\t'db-user',\n\t\t\t\t'db-pass',\n\t\t\t\t'db-name',\n\t\t\t\t'db-path',\n\t\t\t\t'truncate-new-site-directory',\n\t\t\t\t'allow',\n\t\t\t];\n\t\t\tconst cliArgs = cliArgsToPass\n\t\t\t\t.filter((arg) => arg in args)\n\t\t\t\t.map((arg) => `--${arg}=${args[arg]}`);\n\t\t\tcliArgs.push(`--site-url=${args.siteUrl}`);\n\n\t\t\tconst streamedResponse = await runBlueprintV2({\n\t\t\t\tphp,\n\t\t\t\tblueprint: args.blueprint,\n\t\t\t\tblueprintOverrides: {\n\t\t\t\t\tadditionalSteps: args['additional-blueprint-steps'],\n\t\t\t\t\twordpressVersion: args.wp,\n\t\t\t\t},\n\t\t\t\tcliArgs,\n\t\t\t\tonMessage: async (message: BlueprintMessage) => {\n\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\tcase 'blueprint.target_resolved': {\n\t\t\t\t\t\t\tif (!this.blueprintTargetResolved) {\n\t\t\t\t\t\t\t\tthis.blueprintTargetResolved = true;\n\t\t\t\t\t\t\t\tawait this.applyPostInstallMountsToAllWorkers(\n\t\t\t\t\t\t\t\t\tworkerPostInstallMountsPort\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.progress': {\n\t\t\t\t\t\t\tconst progressMessage = `${message.caption.trim()} – ${message.progress.toFixed(\n\t\t\t\t\t\t\t\t2\n\t\t\t\t\t\t\t)}%`;\n\t\t\t\t\t\t\toutput.progress(progressMessage);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'blueprint.error': {\n\t\t\t\t\t\t\tconst red = '\\x1b[31m';\n\t\t\t\t\t\t\tconst bold = '\\x1b[1m';\n\t\t\t\t\t\t\tconst reset = '\\x1b[0m';\n\t\t\t\t\t\t\tif (args.verbosity === 'debug' && message.details) {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Fatal error:${reset} Uncaught ${message.details.exception}: ${message.details.message}\\n` +\n\t\t\t\t\t\t\t\t\t\t` at ${message.details.file}:${message.details.line}\\n` +\n\t\t\t\t\t\t\t\t\t\t(message.details.trace\n\t\t\t\t\t\t\t\t\t\t\t? message.details.trace + '\\n'\n\t\t\t\t\t\t\t\t\t\t\t: '')\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutput.stderr(\n\t\t\t\t\t\t\t\t\t`${red}${bold}Error:${reset} ${message.message}\\n`\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\t/**\n\t\t\t * When we're debugging, every bit of information matters – let's immediately output\n\t\t\t * everything we get from the PHP output streams.\n\t\t\t */\n\t\t\tif (args.verbosity === 'debug') {\n\t\t\t\tstreamedResponse!.stdout.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stdout.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\tstreamedResponse!.stderr.pipeTo(\n\t\t\t\t\tnew WritableStream({\n\t\t\t\t\t\twrite(chunk) {\n\t\t\t\t\t\t\tprocess.stderr.write(chunk);\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait streamedResponse!.finished;\n\t\t\tif ((await streamedResponse!.exitCode) !== 0) {\n\t\t\t\t// exitCode != 1 means the blueprint execution failed. Let's throw an error.\n\t\t\t\t// and clean up.\n\t\t\t\tconst syncResponse =\n\t\t\t\t\tawait PHPResponse.fromStreamedResponse(streamedResponse);\n\t\t\t\tthrow new PHPExecutionFailureError(\n\t\t\t\t\t`PHP.run() failed with exit code ${syncResponse.exitCode}. ${syncResponse.errors} ${syncResponse.text}`,\n\t\t\t\t\tsyncResponse,\n\t\t\t\t\t'request'\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Capture the PHP error log details to provide more context for debugging.\n\t\t\tlet phpLogs = '';\n\t\t\ttry {\n\t\t\t\t// @TODO: Don't assume errorLogPath starts with /wordpress/\n\t\t\t\t// ...or maybe we can assume that in Playground CLI?\n\t\t\t\tphpLogs = php.readFileAsText(errorLogPath);\n\t\t\t} catch {\n\t\t\t\t// Ignore errors reading the PHP error log.\n\t\t\t}\n\t\t\t(error as any).phpLogs = phpLogs;\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\treap();\n\t\t\tunmountCwd();\n\t\t}\n\t}\n\n\tasync bootRequestHandler({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tprocessId,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tonPHPInstanceCreated,\n\t\tspawnHandler,\n\t}: WorkerBootRequestHandlerOptions) {\n\t\tif (this.booted) {\n\t\t\tthrow new Error('Playground already booted');\n\t\t}\n\t\tthis.booted = true;\n\n\t\ttry {\n\t\t\tconst requestHandler = await bootRequestHandler({\n\t\t\t\tsiteUrl,\n\t\t\t\tcreatePhpRuntime: async () => {\n\t\t\t\t\treturn await loadNodeRuntime(phpVersion, {\n\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\temscriptenOptions: {\n\t\t\t\t\t\t\tprocessId,\n\t\t\t\t\t\t\ttrace: trace ? tracePhpWasm : undefined,\n\t\t\t\t\t\t\tENV: {\n\t\t\t\t\t\t\t\tDOCROOT: '/wordpress',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnativeInternalDirPath,\n\t\t\t\t\t\t\tbindUserSpace: (\n\t\t\t\t\t\t\t\tuserSpaceContext: WasmUserSpaceContext\n\t\t\t\t\t\t\t) => {\n\t\t\t\t\t\t\t\treturn bindUserSpace(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfileLockManager: this.fileLockManager!,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tuserSpaceContext\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfollowSymlinks: allow?.includes('follow-symlinks'),\n\t\t\t\t\t\textensions,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmaxPhpInstances: 1,\n\t\t\t\tonPHPInstanceCreated,\n\t\t\t\tsapiName: 'cli',\n\t\t\t\tcreateFiles,\n\t\t\t\tconstants,\n\t\t\t\tphpIniEntries,\n\t\t\t\tpathAliases,\n\t\t\t\tcookieStore: false,\n\t\t\t\tspawnHandler,\n\t\t\t});\n\t\t\tthis.__internal_setRequestHandler(requestHandler);\n\n\t\t\tconst primaryPhp = await requestHandler.getPrimaryPhp();\n\t\t\tawait this.setPrimaryPHP(primaryPhp);\n\n\t\t\tsetApiReady();\n\t\t} catch (e) {\n\t\t\tsetAPIError(e as Error);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tasync mountAfterWordPressInstall(mounts: Array<Mount>) {\n\t\tawait mountResources(this.__internal_getPHP()!, mounts);\n\t}\n\n\tasync applyPostInstallMountsToAllWorkers(\n\t\tpostInstallMountsPort: MessagePort\n\t): Promise<void> {\n\t\tconst applyPostInstallMountsToAllWorkers = consumeAPI<\n\t\t\t() => Promise<void>\n\t\t>(postInstallMountsPort);\n\t\tawait applyPostInstallMountsToAllWorkers();\n\t\tapplyPostInstallMountsToAllWorkers[releaseApiProxy]();\n\t}\n\n\t// Provide a named disposal method that can be invoked via comlink.\n\tasync dispose() {\n\t\tawait this[Symbol.asyncDispose]();\n\t}\n}\n\n/**\n * Spawns a new PHP process to be used in the PHP spawn handler (in proc_open() etc. calls).\n * It boots from this worker-thread-v1.ts file, but is a separate process.\n *\n * We explicitly avoid using PHPProcessManager.acquirePHPInstance() here.\n *\n * Why?\n *\n * Because each PHP instance acquires actual OS-level file locks via fcntl() and LockFileEx()\n * syscalls. Running multiple PHP instances from the same OS process would allow them to\n * acquire overlapping locks. Running every PHP instance in a separate OS process ensures\n * any locks that overlap between PHP instances conflict with each other as expected.\n *\n * @param options - The options for the worker.\n * @param fileLockManager - The file lock manager to use.\n * @returns A promise that resolves to the PHP worker.\n */\nasync function createPHPWorker(\n\t{\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t}: // NOTE: We explicitly remove processId from the options\n\t// type so the type system will catch if we try to reuse\n\t// our parent's process ID.\n\tOmit<SecondaryWorkerBootArgs, 'processId'>,\n\tfileLockManager: FileLockManager | RemoteAPI<FileLockManager>\n) {\n\tconst spawnedWorker = await spawnWorkerThread('v2');\n\n\tconst handler = consumeAPI<PlaygroundCliBlueprintV2Worker>(\n\t\tspawnedWorker.phpPort\n\t);\n\thandler.useFileLockManager(fileLockManager as any);\n\tawait handler.bootWorker({\n\t\tsiteUrl,\n\t\tallow,\n\t\tphpVersion,\n\t\tcreateFiles,\n\t\tconstants,\n\t\tphpIniEntries,\n\t\tprocessId: spawnedWorker.processId,\n\t\ttrace,\n\t\tnativeInternalDirPath,\n\t\textensions,\n\t\tpathAliases,\n\t\tmountsBeforeWpInstall,\n\t\tmountsAfterWpInstall,\n\t});\n\n\treturn {\n\t\tphp: handler,\n\t\treap: () => {\n\t\t\ttry {\n\t\t\t\thandler.dispose();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tspawnedWorker.worker.terminate();\n\t\t\t} catch {\n\t\t\t\t/** */\n\t\t\t}\n\t\t},\n\t};\n}\n\nprocess.on('unhandledRejection', (e: any) => {\n\tlogger.error('Unhandled rejection:', e);\n});\n\nconst phpChannel = new MessageChannel();\n\nconst [setApiReady, setAPIError] = exposeAPI(\n\tnew PlaygroundCliBlueprintV2Worker(new EmscriptenDownloadMonitor()),\n\tundefined,\n\tphpChannel.port1\n);\n\nparentPort?.postMessage(\n\t{\n\t\tcommand: 'worker-script-initialized',\n\t\tphpPort: phpChannel.port2,\n\t},\n\t[phpChannel.port2 as any]\n);\n"],"names":["mountResources","php","mounts","mount","createNodeFsMountHandler","output","tracePhpWasm","processId","format","args","sprintf","data","shouldRenderProgress","PlaygroundCliBlueprintV2Worker","PHPWorker","monitor","port","consumeAPISync","workerPostInstallMountsPort","setPhpIniEntries","setupPlatformLevelMuPlugins","writeFiles","rootCertificates","preloadPhpInfoRoute","joinPaths","sandboxedSpawnHandlerFactory","createPHPWorker","requestHandler","reap","primaryPhp","unmountCwd","blueprintPath","path","existsSync","cliArgs","arg","streamedResponse","runBlueprintV2","message","progressMessage","red","bold","reset","chunk","syncResponse","PHPResponse","PHPExecutionFailureError","error","phpLogs","errorLogPath","siteUrl","allow","phpVersion","createFiles","constants","phpIniEntries","trace","nativeInternalDirPath","extensions","pathAliases","onPHPInstanceCreated","spawnHandler","bootRequestHandler","loadNodeRuntime","userSpaceContext","bindUserSpace","setApiReady","e","setAPIError","postInstallMountsPort","applyPostInstallMountsToAllWorkers","consumeAPI","releaseApiProxy","mountsBeforeWpInstall","mountsAfterWpInstall","fileLockManager","spawnedWorker","spawnWorkerThread","handler","logger","phpChannel","MessageChannel","exposeAPI","EmscriptenDownloadMonitor","parentPort"],"mappings":"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"}
|