@wp-playground/cli 3.1.20 → 3.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/blueprints-v1/download.d.ts +1 -1
- package/blueprints-v1/worker-thread-v1.d.ts +5 -6
- package/blueprints-v2/worker-thread-v2.d.ts +3 -5
- package/cli-output.d.ts +8 -33
- package/cli.cjs +1 -1
- package/cli.js +1 -1
- package/index.cjs +1 -1
- package/index.js +4 -3
- package/package.json +15 -14
- package/php-extensions.d.ts +21 -0
- package/{run-cli-B9kBDU12.js → run-cli-CC9V0J3D.js} +665 -639
- package/run-cli-CC9V0J3D.js.map +1 -0
- package/run-cli-b6r6MAhq.cjs +65 -0
- package/run-cli-b6r6MAhq.cjs.map +1 -0
- package/run-cli.d.ts +19 -4
- package/sqlite-database-integration.zip +0 -0
- package/worker-thread-v1.cjs +2 -2
- package/worker-thread-v1.cjs.map +1 -1
- package/worker-thread-v1.js +62 -62
- package/worker-thread-v1.js.map +1 -1
- package/worker-thread-v2.cjs +8 -8
- package/worker-thread-v2.cjs.map +1 -1
- package/worker-thread-v2.js +99 -105
- package/worker-thread-v2.js.map +1 -1
- package/run-cli-B6Dm34vQ.cjs +0 -66
- package/run-cli-B6Dm34vQ.cjs.map +0 -1
- package/run-cli-B9kBDU12.js.map +0 -1
package/run-cli.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
|
-
import { type Pooled, type PathAlias, type
|
|
3
|
+
import { type Pooled, type PathAlias, type AllPHPVersion } from '@php-wasm/universal';
|
|
4
4
|
import type { BlueprintBundle, BlueprintV1Declaration, BlueprintV2Declaration } from '@wp-playground/blueprints';
|
|
5
5
|
import type { Server } from 'http';
|
|
6
6
|
import { Worker } from 'worker_threads';
|
|
@@ -46,6 +46,14 @@ export declare function parseOptionsAndRunCLI(argsToParse: string[]): Promise<{
|
|
|
46
46
|
cliServer: RunCLIServer;
|
|
47
47
|
};
|
|
48
48
|
}>;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the --workers flag into a concrete worker count.
|
|
51
|
+
*
|
|
52
|
+
* The Math.max(1, ...) guard covers single-core hosts and restricted
|
|
53
|
+
* environments where `os.cpus()` can return an empty array — without it
|
|
54
|
+
* the default would drop to 0 and no workers would be spawned.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveWorkerCount(value: number | 'auto' | undefined): number;
|
|
49
57
|
export interface RunCLIArgs {
|
|
50
58
|
/**
|
|
51
59
|
* `_` holds positional tokens in the order they appeared.
|
|
@@ -59,13 +67,19 @@ export interface RunCLIArgs {
|
|
|
59
67
|
mount?: Mount[];
|
|
60
68
|
'mount-before-install'?: Mount[];
|
|
61
69
|
outfile?: string;
|
|
62
|
-
php?:
|
|
70
|
+
php?: AllPHPVersion;
|
|
63
71
|
port?: number;
|
|
64
72
|
'site-url'?: string;
|
|
65
73
|
quiet?: boolean;
|
|
66
74
|
verbosity?: LogVerbosity;
|
|
67
75
|
wp?: string;
|
|
68
|
-
|
|
76
|
+
/**
|
|
77
|
+
* For the `server` command (and other long-form commands), this is the
|
|
78
|
+
* host path to auto-detect and mount. For the `start` command, this is a
|
|
79
|
+
* boolean toggle: `true` (default) enables auto-detection on the
|
|
80
|
+
* `--path` directory; `false` (i.e. `--no-auto-mount`) disables it.
|
|
81
|
+
*/
|
|
82
|
+
autoMount?: string | boolean;
|
|
69
83
|
pathAliases?: PathAlias[];
|
|
70
84
|
experimentalTrace?: boolean;
|
|
71
85
|
internalCookieStore?: boolean;
|
|
@@ -78,6 +92,8 @@ export interface RunCLIArgs {
|
|
|
78
92
|
experimentalUnsafeIdeIntegration?: string[];
|
|
79
93
|
experimentalDevtools?: boolean;
|
|
80
94
|
'experimental-blueprints-v2-runner'?: boolean;
|
|
95
|
+
workers?: number | 'auto';
|
|
96
|
+
'experimental-multi-worker'?: number;
|
|
81
97
|
wordpressInstallMode?: WordPressInstallMode;
|
|
82
98
|
/**
|
|
83
99
|
* PHP string constants defined via --define flag.
|
|
@@ -108,7 +124,6 @@ export interface RunCLIArgs {
|
|
|
108
124
|
allow?: string;
|
|
109
125
|
path?: string;
|
|
110
126
|
skipBrowser?: boolean;
|
|
111
|
-
noAutoMount?: boolean;
|
|
112
127
|
reset?: boolean;
|
|
113
128
|
}
|
|
114
129
|
export type PlaygroundCliWorker = PlaygroundCliBlueprintV1Worker | PlaygroundCliBlueprintV2Worker;
|
|
Binary file
|
package/worker-thread-v1.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("@php-wasm/node"),
|
|
2
|
-
`)},phpIniEntries:{"openssl.cafile":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""},dataSqlPath:
|
|
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;
|
|
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 } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { PathAlias, SupportedPHPVersion } 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\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: SupportedPHPVersion;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\tfollowSymlinks: boolean;\n\twithIntl?: boolean;\n\twithRedis?: boolean;\n\twithMemcached?: boolean;\n\twithXdebug?: boolean;\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\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\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\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\twithIntl: options.withIntl,\n\t\t\t\twithRedis: options.withRedis,\n\t\t\t\twithMemcached: options.withMemcached,\n\t\t\t\twithXdebug: options.withXdebug,\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","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\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
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { loadNodeRuntime as
|
|
2
|
-
import { EmscriptenDownloadMonitor as
|
|
3
|
-
import { exposeAPI as
|
|
4
|
-
import { sprintf as
|
|
5
|
-
import { RecommendedPHPVersion as
|
|
6
|
-
import { bootWordPress as R, bootRequestHandler as
|
|
7
|
-
import { rootCertificates as
|
|
8
|
-
import { MessageChannel as
|
|
9
|
-
import {
|
|
10
|
-
import { logger as
|
|
11
|
-
function
|
|
1
|
+
import { loadNodeRuntime as w } from "@php-wasm/node";
|
|
2
|
+
import { EmscriptenDownloadMonitor as f } from "@php-wasm/progress";
|
|
3
|
+
import { exposeAPI as y, PHPWorker as b, consumeAPISync as g, consumeAPI as h, releaseApiProxy as I, sandboxedSpawnHandlerFactory as W } from "@php-wasm/universal";
|
|
4
|
+
import { sprintf as H } from "@php-wasm/util";
|
|
5
|
+
import { RecommendedPHPVersion as k } from "@wp-playground/common";
|
|
6
|
+
import { bootWordPress as R, bootRequestHandler as q } from "@wp-playground/wordpress";
|
|
7
|
+
import { rootCertificates as A } from "tls";
|
|
8
|
+
import { MessageChannel as _, parentPort as v } from "worker_threads";
|
|
9
|
+
import { b as n, s as M } from "./run-cli-CC9V0J3D.js";
|
|
10
|
+
import { logger as S } from "@php-wasm/logger";
|
|
11
|
+
function x(r, e, ...t) {
|
|
12
12
|
console.log(
|
|
13
13
|
performance.now().toFixed(6).padStart(15, "0"),
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
r.toString().padStart(16, "0"),
|
|
15
|
+
H(e, ...t)
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
|
-
class F extends
|
|
18
|
+
class F extends b {
|
|
19
19
|
constructor(e) {
|
|
20
20
|
super(void 0, e), this.bootedRequestHandler = !1, this.bootedWordPress = !1;
|
|
21
21
|
}
|
|
@@ -31,13 +31,14 @@ class F extends y {
|
|
|
31
31
|
async useFileLockManager(e) {
|
|
32
32
|
this.fileLockManager = await g(e);
|
|
33
33
|
}
|
|
34
|
-
async bootWordPress(e,
|
|
34
|
+
async bootWordPress(e, t) {
|
|
35
35
|
if (this.bootedWordPress)
|
|
36
36
|
throw new Error("WordPress already booted");
|
|
37
37
|
this.bootedWordPress = !0;
|
|
38
38
|
const {
|
|
39
|
-
siteUrl:
|
|
40
|
-
|
|
39
|
+
siteUrl: o,
|
|
40
|
+
phpVersion: s,
|
|
41
|
+
wordpressInstallMode: P,
|
|
41
42
|
wordPressZip: l,
|
|
42
43
|
sqliteIntegrationPluginZip: d,
|
|
43
44
|
dataSqlPath: m,
|
|
@@ -45,8 +46,9 @@ class F extends y {
|
|
|
45
46
|
} = e;
|
|
46
47
|
try {
|
|
47
48
|
await R(this.__internal_getRequestHandler(), {
|
|
48
|
-
siteUrl:
|
|
49
|
-
|
|
49
|
+
siteUrl: o,
|
|
50
|
+
phpVersion: s,
|
|
51
|
+
wordpressInstallMode: P,
|
|
50
52
|
wordPressZip: l !== void 0 ? new File([l], "wordpress.zip") : void 0,
|
|
51
53
|
sqliteIntegrationPluginZip: d !== void 0 ? new File(
|
|
52
54
|
[d],
|
|
@@ -54,7 +56,7 @@ class F extends y {
|
|
|
54
56
|
) : void 0,
|
|
55
57
|
// TODO: Are these redundant creations?
|
|
56
58
|
createFiles: {
|
|
57
|
-
"/internal/shared/ca-bundle.crt":
|
|
59
|
+
"/internal/shared/ca-bundle.crt": A.join(`
|
|
58
60
|
`)
|
|
59
61
|
},
|
|
60
62
|
phpIniEntries: {
|
|
@@ -65,10 +67,10 @@ class F extends y {
|
|
|
65
67
|
dataSqlPath: m,
|
|
66
68
|
constants: u
|
|
67
69
|
});
|
|
68
|
-
const
|
|
69
|
-
await
|
|
70
|
-
} catch (
|
|
71
|
-
throw p(
|
|
70
|
+
const a = h(t);
|
|
71
|
+
await a.applyPostInstallMountsToAllWorkers(), a[I](), c();
|
|
72
|
+
} catch (a) {
|
|
73
|
+
throw p(a), a;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
async bootRequestHandler(e) {
|
|
@@ -76,35 +78,36 @@ class F extends y {
|
|
|
76
78
|
throw new Error("Playground already booted");
|
|
77
79
|
this.bootedRequestHandler = !0;
|
|
78
80
|
try {
|
|
79
|
-
const
|
|
81
|
+
const t = await q({
|
|
80
82
|
siteUrl: e.siteUrl,
|
|
83
|
+
phpVersion: e.phpVersion,
|
|
81
84
|
maxPhpInstances: 1,
|
|
82
|
-
createPhpRuntime:
|
|
85
|
+
createPhpRuntime: V(
|
|
83
86
|
e,
|
|
84
87
|
this.fileLockManager
|
|
85
88
|
),
|
|
86
|
-
onPHPInstanceCreated: async (
|
|
87
|
-
await n(
|
|
89
|
+
onPHPInstanceCreated: async (s) => {
|
|
90
|
+
await n(s, e.mountsBeforeWpInstall), this.bootedWordPress && await n(s, e.mountsAfterWpInstall);
|
|
88
91
|
},
|
|
89
92
|
sapiName: "cli",
|
|
90
93
|
cookieStore: !1,
|
|
91
94
|
pathAliases: e.pathAliases,
|
|
92
|
-
spawnHandler: () =>
|
|
93
|
-
let
|
|
94
|
-
return this.bootedWordPress || (
|
|
95
|
+
spawnHandler: () => W(() => {
|
|
96
|
+
let s = e;
|
|
97
|
+
return this.bootedWordPress || (s = {
|
|
95
98
|
...e,
|
|
96
99
|
mountsAfterWpInstall: []
|
|
97
100
|
}), C(
|
|
98
|
-
|
|
101
|
+
s,
|
|
99
102
|
this.fileLockManager
|
|
100
103
|
);
|
|
101
104
|
})
|
|
102
105
|
});
|
|
103
|
-
this.__internal_setRequestHandler(
|
|
104
|
-
const
|
|
105
|
-
await this.setPrimaryPHP(
|
|
106
|
-
} catch (
|
|
107
|
-
throw p(
|
|
106
|
+
this.__internal_setRequestHandler(t);
|
|
107
|
+
const o = await t.getPrimaryPhp();
|
|
108
|
+
await this.setPrimaryPHP(o), c();
|
|
109
|
+
} catch (t) {
|
|
110
|
+
throw p(t), t;
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
async mountAfterWordPressInstall(e) {
|
|
@@ -115,54 +118,51 @@ class F extends y {
|
|
|
115
118
|
await this[Symbol.asyncDispose]();
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
|
-
function
|
|
119
|
-
return async () => await
|
|
120
|
-
|
|
121
|
+
function V(r, e) {
|
|
122
|
+
return async () => await w(
|
|
123
|
+
r.phpVersion || k,
|
|
121
124
|
{
|
|
122
125
|
fileLockManager: e,
|
|
123
126
|
emscriptenOptions: {
|
|
124
|
-
processId:
|
|
125
|
-
trace:
|
|
126
|
-
nativeInternalDirPath:
|
|
127
|
+
processId: r.processId,
|
|
128
|
+
trace: r.trace ? x : void 0,
|
|
129
|
+
nativeInternalDirPath: r.nativeInternalDirPath
|
|
127
130
|
},
|
|
128
|
-
followSymlinks:
|
|
129
|
-
|
|
130
|
-
withRedis: t.withRedis,
|
|
131
|
-
withMemcached: t.withMemcached,
|
|
132
|
-
withXdebug: t.withXdebug
|
|
131
|
+
followSymlinks: r.followSymlinks,
|
|
132
|
+
extensions: r.extensions
|
|
133
133
|
}
|
|
134
134
|
);
|
|
135
135
|
}
|
|
136
|
-
async function C(
|
|
137
|
-
const
|
|
138
|
-
|
|
136
|
+
async function C(r, e) {
|
|
137
|
+
const t = await M("v1"), o = h(
|
|
138
|
+
t.phpPort
|
|
139
139
|
);
|
|
140
|
-
return
|
|
141
|
-
...
|
|
142
|
-
processId:
|
|
140
|
+
return o.useFileLockManager(e), await o.bootRequestHandler({
|
|
141
|
+
...r,
|
|
142
|
+
processId: t.processId
|
|
143
143
|
}), {
|
|
144
|
-
php:
|
|
144
|
+
php: o,
|
|
145
145
|
reap: () => {
|
|
146
146
|
try {
|
|
147
|
-
|
|
147
|
+
o.dispose();
|
|
148
148
|
} catch {
|
|
149
149
|
}
|
|
150
150
|
try {
|
|
151
|
-
|
|
151
|
+
t.worker.terminate();
|
|
152
152
|
} catch {
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
|
-
process.on("unhandledRejection", (
|
|
158
|
-
|
|
157
|
+
process.on("unhandledRejection", (r) => {
|
|
158
|
+
S.error("Unhandled rejection:", r);
|
|
159
159
|
});
|
|
160
|
-
const i = new
|
|
161
|
-
new F(new
|
|
160
|
+
const i = new _(), [c, p] = y(
|
|
161
|
+
new F(new f()),
|
|
162
162
|
void 0,
|
|
163
163
|
i.port1
|
|
164
164
|
);
|
|
165
|
-
|
|
165
|
+
v?.postMessage(
|
|
166
166
|
{
|
|
167
167
|
command: "worker-script-initialized",
|
|
168
168
|
phpPort: i.port2
|
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 } from '@php-wasm/node';\nimport { EmscriptenDownloadMonitor } from '@php-wasm/progress';\nimport type { PathAlias, SupportedPHPVersion } 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\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: SupportedPHPVersion;\n\tprocessId: number;\n\ttrace: boolean;\n\tnativeInternalDirPath: string;\n\tmountsBeforeWpInstall: Array<Mount>;\n\tmountsAfterWpInstall: Array<Mount>;\n\tfollowSymlinks: boolean;\n\twithIntl?: boolean;\n\twithRedis?: boolean;\n\twithMemcached?: boolean;\n\twithXdebug?: boolean;\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\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\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\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\twithIntl: options.withIntl,\n\t\t\t\twithRedis: options.withRedis,\n\t\t\t\twithMemcached: options.withMemcached,\n\t\t\t\twithXdebug: options.withXdebug,\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","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\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
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
`),process.exit(1)}}function
|
|
3
|
-
`),process.stdout.write("\r\x1B[K"+s),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const k=require("@php-wasm/logger"),y=require("@php-wasm/node"),q=require("@php-wasm/progress"),a=require("@php-wasm/universal"),_=require("@php-wasm/util"),v=require("@wp-playground/blueprints"),m=require("@wp-playground/wordpress"),I=require("fs"),b=require("path"),$=require("tls"),x=require("worker_threads"),R=require("./run-cli-b6r6MAhq.cjs");async function W(s,e){for(const t of e)try{s.mkdir(t.vfsPath),await s.mount(t.vfsPath,y.createNodeFsMountHandler(t.hostPath))}catch{l.stderr(`\x1B[31m\x1B[1mError mounting path ${t.hostPath} at ${t.vfsPath}\x1B[0m
|
|
2
|
+
`),process.exit(1)}}function S(s,e,...t){console.log(performance.now().toFixed(6).padStart(15,"0"),s.toString().padStart(16,"0"),_.sprintf(e,...t))}Object.defineProperty(process.stdout,"isTTY",{value:!0});Object.defineProperty(process.stderr,"isTTY",{value:!0});const l={lastWriteWasProgress:!1,progress(s){R.shouldRenderProgress(process.stdout)&&(process.stdout.isTTY?(l.lastWriteWasProgress||process.stdout.write(`
|
|
3
|
+
`),process.stdout.write("\r\x1B[K"+s),l.lastWriteWasProgress=!0):console.log(s))},stdout(s){process.stdout.write(`
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
`),
|
|
6
|
+
`),l.lastWriteWasProgress&&(l.lastWriteWasProgress=!1),process.stdout.write(s)},stderr(s){process.stdout.write(`
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
`),
|
|
10
|
-
`)}),await
|
|
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 o=await this.__internal_getRequestHandler().getPrimaryPhp();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 a.setPhpIniEntries(o,{"openssl.cafile":"/internal/shared/ca-bundle.crt",allow_url_fopen:"1",disable_functions:""}),await m.setupPlatformLevelMuPlugins(o),await a.writeFiles(o,"/",{"/internal/shared/ca-bundle.crt":$.rootCertificates.join(`
|
|
10
|
+
`)}),await m.preloadPhpInfoRoute(o,_.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 o=this.__internal_getRequestHandler(),{php:w,reap:f}=await o.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",n="\x1B[0m";e.verbosity==="debug"&&r.details?l.stderr(`${p}${u}Fatal error:${n} 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
|
-
`:"")):
|
|
14
|
-
`);break}}}});if(e.verbosity==="debug"&&(
|
|
13
|
+
`:"")):l.stderr(`${p}${u}Error:${n} ${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:o,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 n=await m.bootRequestHandler({siteUrl:e,createPhpRuntime:async()=>await y.loadNodeRuntime(o,{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(n);const H=await n.getPrimaryPhp();await this.setPrimaryPHP(H),B()}catch(n){throw C(n),n}}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:o,constants:w,phpIniEntries:f,trace:h,nativeInternalDirPath:P,extensions:i,pathAliases:c,mountsBeforeWpInstall:d,mountsAfterWpInstall:r},p){const u=await R.spawnWorkerThread("v2"),n=a.consumeAPI(u.phpPort);return n.useFileLockManager(p),await n.bootWorker({siteUrl:s,allow:e,phpVersion:t,createFiles:o,constants:w,phpIniEntries:f,processId:u.processId,trace:h,nativeInternalDirPath:P,extensions:i,pathAliases:c,mountsBeforeWpInstall:d,mountsAfterWpInstall:r}),{php:n,reap:()=>{try{n.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
|