wp-studio 1.7.7-alpha1
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/LICENSE.md +257 -0
- package/README.md +87 -0
- package/dist/cli/_events-BeOo0LuG.js +116 -0
- package/dist/cli/appdata-07CF2rhg.js +21090 -0
- package/dist/cli/archive-xDmkN4wb.js +15942 -0
- package/dist/cli/browser-CgWK-yoe.js +44 -0
- package/dist/cli/certificate-manager-DdBumKZp.js +250 -0
- package/dist/cli/create-BHVhkvTx.js +80 -0
- package/dist/cli/create-ZS29BDDi.js +40999 -0
- package/dist/cli/delete-BgQn-elT.js +56 -0
- package/dist/cli/delete-g8pgaLna.js +132 -0
- package/dist/cli/get-wordpress-version-BwSCJujO.js +18 -0
- package/dist/cli/index-7pbG_s_U.js +434 -0
- package/dist/cli/index-BXRYeCYG.js +1393 -0
- package/dist/cli/index-T3F1GwxX.js +2668 -0
- package/dist/cli/is-errno-exception-t38xF2pB.js +6 -0
- package/dist/cli/list-BE_UBjL5.js +105 -0
- package/dist/cli/list-DKz0XxM7.js +1032 -0
- package/dist/cli/logger-actions-OaIvl-ai.js +45 -0
- package/dist/cli/login-B4PkfKOu.js +82 -0
- package/dist/cli/logout-BC9gKlTj.js +48 -0
- package/dist/cli/main.js +5 -0
- package/dist/cli/mu-plugins-GEfKsl5U.js +530 -0
- package/dist/cli/passwords-DyzWd9Xi.js +80 -0
- package/dist/cli/process-manager-daemon.js +327 -0
- package/dist/cli/process-manager-ipc-AUZeYYDT.js +454 -0
- package/dist/cli/proxy-daemon.js +197 -0
- package/dist/cli/run-wp-cli-command-BctnMDWG.js +88 -0
- package/dist/cli/sequential-BQFuixXz.js +46 -0
- package/dist/cli/server-files-C_oy-mnI.js +26 -0
- package/dist/cli/set-DknhAZpw.js +327 -0
- package/dist/cli/site-utils-CfsabjUn.js +243 -0
- package/dist/cli/snapshots-6XE53y_F.js +874 -0
- package/dist/cli/sqlite-integration-H4OwSlwR.js +83 -0
- package/dist/cli/start-CRJqm09_.js +90 -0
- package/dist/cli/status-CWNHIOaY.js +44 -0
- package/dist/cli/status-CWWx9jYF.js +110 -0
- package/dist/cli/stop-CQosmjqA.js +117 -0
- package/dist/cli/update-BgL2HKHW.js +101 -0
- package/dist/cli/validation-error-DqLxqQuA.js +40 -0
- package/dist/cli/wordpress-server-child.js +514 -0
- package/dist/cli/wordpress-server-ipc-Dwsg9jSb.js +140 -0
- package/dist/cli/wordpress-server-manager-CtiuJqEb.js +566 -0
- package/dist/cli/wordpress-version-utils-B6UVeTh_.js +51 -0
- package/dist/cli/wp-UGSnlkN0.js +103 -0
- package/package.json +73 -0
- package/patches/@wp-playground+wordpress+3.1.12.patch +28 -0
- package/scripts/postinstall-npm.mjs +38 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { dirname } from "path";
|
|
2
|
+
import { D as DEFAULT_PHP_VERSION, i as isWordPressDirectory } from "./appdata-07CF2rhg.js";
|
|
3
|
+
import { c as cleanupLegacyMuPlugins, g as getMuPlugins } from "./mu-plugins-GEfKsl5U.js";
|
|
4
|
+
import { d as decodePassword } from "./passwords-DyzWd9Xi.js";
|
|
5
|
+
import { __ } from "@wordpress/i18n";
|
|
6
|
+
import { s as sequential } from "./sequential-BQFuixXz.js";
|
|
7
|
+
import { i as isWordPressDevVersion } from "./wordpress-version-utils-B6UVeTh_.js";
|
|
8
|
+
import { runCLI } from "@wp-playground/cli";
|
|
9
|
+
import { InMemoryFilesystem, OverlayFilesystem, FetchFilesystem, NodeJsFilesystem } from "@wp-playground/storage";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { isStepDefinition } from "@wp-playground/blueprints";
|
|
12
|
+
import { g as getWpCliPharPath, a as getSqliteCommandPath } from "./server-files-C_oy-mnI.js";
|
|
13
|
+
import { i as isSqliteIntegrationInstalled } from "./sqlite-integration-H4OwSlwR.js";
|
|
14
|
+
import { m as managerMessageSchema } from "./wordpress-server-ipc-Dwsg9jSb.js";
|
|
15
|
+
function formatPlaygroundCliMessage(message) {
|
|
16
|
+
if (message.includes("WordPress is running")) {
|
|
17
|
+
return __("WordPress is running");
|
|
18
|
+
}
|
|
19
|
+
if (message.includes("Resolved WordPress release URL")) {
|
|
20
|
+
return __("Downloading WordPress…");
|
|
21
|
+
}
|
|
22
|
+
if (message.includes("Downloading WordPress")) {
|
|
23
|
+
return __("Downloading WordPress…");
|
|
24
|
+
}
|
|
25
|
+
if (message.includes("Starting up workers")) {
|
|
26
|
+
return __("Starting up workers…");
|
|
27
|
+
}
|
|
28
|
+
if (message.includes("Booting WordPress")) {
|
|
29
|
+
return __("Booting WordPress…");
|
|
30
|
+
}
|
|
31
|
+
if (message.includes("Running the Blueprint")) {
|
|
32
|
+
return __("Running the Blueprint…");
|
|
33
|
+
}
|
|
34
|
+
if (message.includes("Finished running the blueprint")) {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
if (message.includes("Preparing workers")) {
|
|
38
|
+
return __("Preparing workers…");
|
|
39
|
+
}
|
|
40
|
+
return message;
|
|
41
|
+
}
|
|
42
|
+
function sanitizeBlueprintStep(step) {
|
|
43
|
+
const baseStep = { step: step.step };
|
|
44
|
+
switch (step.step) {
|
|
45
|
+
case "login":
|
|
46
|
+
return { ...baseStep, hasLogin: true };
|
|
47
|
+
case "runPHP":
|
|
48
|
+
case "runPHPWithOptions":
|
|
49
|
+
return { ...baseStep, hasCode: "code" in step };
|
|
50
|
+
case "defineWpConfigConsts":
|
|
51
|
+
return {
|
|
52
|
+
...baseStep,
|
|
53
|
+
constCount: "consts" in step ? Object.keys(step.consts).length : void 0
|
|
54
|
+
};
|
|
55
|
+
case "runSql":
|
|
56
|
+
return { ...baseStep, hasSql: "sql" in step };
|
|
57
|
+
case "writeFile":
|
|
58
|
+
return {
|
|
59
|
+
...baseStep,
|
|
60
|
+
path: "path" in step ? step.path : void 0,
|
|
61
|
+
hasData: "data" in step
|
|
62
|
+
};
|
|
63
|
+
case "request":
|
|
64
|
+
return {
|
|
65
|
+
...baseStep,
|
|
66
|
+
url: "request" in step ? step.request.url : void 0,
|
|
67
|
+
method: "request" in step ? step.request.method : void 0
|
|
68
|
+
};
|
|
69
|
+
case "setSiteOptions":
|
|
70
|
+
case "updateUserMeta": {
|
|
71
|
+
let keys = [];
|
|
72
|
+
if ("options" in step && step.options) {
|
|
73
|
+
keys = Object.keys(step.options);
|
|
74
|
+
} else if ("meta" in step) {
|
|
75
|
+
keys = Object.keys(step.meta);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
...baseStep,
|
|
79
|
+
keys
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
case "installPlugin":
|
|
83
|
+
case "installTheme":
|
|
84
|
+
return step;
|
|
85
|
+
default:
|
|
86
|
+
return step;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function sanitizeBlueprint(blueprint) {
|
|
90
|
+
if (!blueprint) {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
// Keep metadata but omit author (triggers Sentry's "auth" filter)
|
|
95
|
+
meta: blueprint.meta ? {
|
|
96
|
+
title: blueprint.meta.title,
|
|
97
|
+
description: blueprint.meta.description
|
|
98
|
+
} : void 0,
|
|
99
|
+
// Sanitize each step individually
|
|
100
|
+
steps: blueprint.steps?.filter(isStepDefinition).map(sanitizeBlueprintStep),
|
|
101
|
+
// Safe configuration
|
|
102
|
+
features: blueprint.features,
|
|
103
|
+
preferredVersions: blueprint.preferredVersions,
|
|
104
|
+
landingPage: blueprint.landingPage,
|
|
105
|
+
login: typeof blueprint.login === "boolean" ? blueprint.login : "<credentials>"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function sanitizeRunCLIArgs(args) {
|
|
109
|
+
return {
|
|
110
|
+
command: args.command,
|
|
111
|
+
php: args.php,
|
|
112
|
+
wp: args.wp,
|
|
113
|
+
port: args.port,
|
|
114
|
+
debug: args.debug,
|
|
115
|
+
verbosity: args.verbosity,
|
|
116
|
+
wordpressInstallMode: args.wordpressInstallMode,
|
|
117
|
+
skipSqliteSetup: args.skipSqliteSetup,
|
|
118
|
+
followSymlinks: args.followSymlinks,
|
|
119
|
+
internalCookieStore: args.internalCookieStore,
|
|
120
|
+
xdebug: args.xdebug,
|
|
121
|
+
experimentalDevtools: args.experimentalDevtools,
|
|
122
|
+
"site-url": args["site-url"],
|
|
123
|
+
outfile: args.outfile,
|
|
124
|
+
blueprintJson: args.blueprint ? (
|
|
125
|
+
// This isn't a fully safe type assertion, but by the time we execute this, the blueprint
|
|
126
|
+
// content will already have been validated to conform to the V1 spec, so it's fine.
|
|
127
|
+
JSON.stringify(sanitizeBlueprint(args.blueprint))
|
|
128
|
+
) : void 0
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
let server = null;
|
|
132
|
+
let lastCliArgs = null;
|
|
133
|
+
const originalConsoleLog = console.log;
|
|
134
|
+
const originalConsoleError = console.error;
|
|
135
|
+
const originalConsoleWarn = console.warn;
|
|
136
|
+
console.log = (...args) => {
|
|
137
|
+
originalConsoleLog("[playground-cli]", ...args);
|
|
138
|
+
const message = args.join(" ");
|
|
139
|
+
process.send({ topic: "activity" });
|
|
140
|
+
const formattedMessage = formatPlaygroundCliMessage(message);
|
|
141
|
+
if (formattedMessage !== message) {
|
|
142
|
+
process.send({ topic: "console-message", message: formattedMessage });
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
console.error = (...args) => {
|
|
146
|
+
originalConsoleError("[playground-cli]", ...args);
|
|
147
|
+
process.send({ topic: "activity" });
|
|
148
|
+
};
|
|
149
|
+
console.warn = (...args) => {
|
|
150
|
+
originalConsoleWarn("[playground-cli]", ...args);
|
|
151
|
+
process.send({ topic: "activity" });
|
|
152
|
+
};
|
|
153
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
154
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
155
|
+
process.stdout.write = function(...args) {
|
|
156
|
+
process.send({ topic: "activity" });
|
|
157
|
+
return originalStdoutWrite(...args);
|
|
158
|
+
};
|
|
159
|
+
process.stderr.write = function(...args) {
|
|
160
|
+
process.send({ topic: "activity" });
|
|
161
|
+
return originalStderrWrite(...args);
|
|
162
|
+
};
|
|
163
|
+
function logToConsole(...args) {
|
|
164
|
+
originalConsoleLog(`[WordPress Server Child]`, ...args);
|
|
165
|
+
}
|
|
166
|
+
function errorToConsole(...args) {
|
|
167
|
+
originalConsoleError(`[WordPress Server Child]`, ...args);
|
|
168
|
+
}
|
|
169
|
+
function escapePhpString(str) {
|
|
170
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
171
|
+
}
|
|
172
|
+
async function setAdminCredentials(server2, adminPassword, adminUsername, adminEmail) {
|
|
173
|
+
await server2.playground.request({
|
|
174
|
+
url: "/?studio-admin-api",
|
|
175
|
+
method: "POST",
|
|
176
|
+
body: {
|
|
177
|
+
action: "set_admin_password",
|
|
178
|
+
...adminPassword && {
|
|
179
|
+
password: escapePhpString(decodePassword(adminPassword))
|
|
180
|
+
},
|
|
181
|
+
...adminUsername && { username: escapePhpString(adminUsername) },
|
|
182
|
+
...adminEmail && { email: escapePhpString(adminEmail) }
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function getWordPressInstallMode(sitePath) {
|
|
187
|
+
const hasWordPress = isWordPressDirectory(sitePath);
|
|
188
|
+
const hasSqlite = await isSqliteIntegrationInstalled(sitePath);
|
|
189
|
+
if (!hasWordPress) {
|
|
190
|
+
return "download-and-install";
|
|
191
|
+
}
|
|
192
|
+
if (hasSqlite) {
|
|
193
|
+
return "install-from-existing-files-if-needed";
|
|
194
|
+
}
|
|
195
|
+
return "do-not-attempt-installing";
|
|
196
|
+
}
|
|
197
|
+
async function getBaseRunCLIArgs(command, config) {
|
|
198
|
+
const wordpressInstallMode = await getWordPressInstallMode(config.sitePath);
|
|
199
|
+
await cleanupLegacyMuPlugins(config.sitePath);
|
|
200
|
+
const [studioMuPluginsHostPath, loaderMuPluginHostPath] = await getMuPlugins({
|
|
201
|
+
isWpAutoUpdating: config.isWpAutoUpdating
|
|
202
|
+
});
|
|
203
|
+
const mounts = [
|
|
204
|
+
{
|
|
205
|
+
hostPath: config.sitePath,
|
|
206
|
+
vfsPath: "/wordpress"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
hostPath: studioMuPluginsHostPath,
|
|
210
|
+
vfsPath: "/internal/studio/mu-plugins"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
hostPath: loaderMuPluginHostPath,
|
|
214
|
+
vfsPath: "/internal/shared/mu-plugins/99-studio-loader.php"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
hostPath: getWpCliPharPath(),
|
|
218
|
+
vfsPath: "/tmp/wp-cli.phar"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
hostPath: getSqliteCommandPath(),
|
|
222
|
+
vfsPath: "/tmp/sqlite-command"
|
|
223
|
+
}
|
|
224
|
+
];
|
|
225
|
+
const enableDebugLog = config.enableDebugLog ?? false;
|
|
226
|
+
const enableDebugDisplay = config.enableDebugDisplay ?? false;
|
|
227
|
+
const defaultConstants = {
|
|
228
|
+
WP_SQLITE_AST_DRIVER: true,
|
|
229
|
+
WP_DEBUG: enableDebugLog || enableDebugDisplay,
|
|
230
|
+
WP_DEBUG_LOG: enableDebugLog,
|
|
231
|
+
WP_DEBUG_DISPLAY: enableDebugDisplay
|
|
232
|
+
};
|
|
233
|
+
let blueprintBundle;
|
|
234
|
+
const preferredVersions = {
|
|
235
|
+
php: config.phpVersion || config.blueprint?.contents.preferredVersions?.php || DEFAULT_PHP_VERSION,
|
|
236
|
+
wp: config.wpVersion || config.blueprint?.contents.preferredVersions?.wp || "latest"
|
|
237
|
+
};
|
|
238
|
+
if (config.blueprint) {
|
|
239
|
+
config.blueprint.contents.constants = {
|
|
240
|
+
...config.blueprint.contents.constants,
|
|
241
|
+
...defaultConstants
|
|
242
|
+
};
|
|
243
|
+
config.blueprint.contents.preferredVersions = preferredVersions;
|
|
244
|
+
const blueprintFs = new InMemoryFilesystem({
|
|
245
|
+
"blueprint.json": JSON.stringify(config.blueprint.contents)
|
|
246
|
+
});
|
|
247
|
+
if (config.blueprint.uri.startsWith("http://") || config.blueprint.uri.startsWith("https://")) {
|
|
248
|
+
blueprintBundle = new OverlayFilesystem([
|
|
249
|
+
blueprintFs,
|
|
250
|
+
new FetchFilesystem({ baseUrl: config.blueprint.uri })
|
|
251
|
+
]);
|
|
252
|
+
} else {
|
|
253
|
+
blueprintBundle = new OverlayFilesystem([
|
|
254
|
+
blueprintFs,
|
|
255
|
+
new NodeJsFilesystem(dirname(config.blueprint.uri))
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
blueprintBundle = new InMemoryFilesystem({
|
|
260
|
+
"blueprint.json": JSON.stringify({
|
|
261
|
+
constants: defaultConstants,
|
|
262
|
+
preferredVersions
|
|
263
|
+
})
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
const args = {
|
|
267
|
+
command,
|
|
268
|
+
internalCookieStore: false,
|
|
269
|
+
login: false,
|
|
270
|
+
followSymlinks: true,
|
|
271
|
+
skipSqliteSetup: true,
|
|
272
|
+
port: config.port,
|
|
273
|
+
"mount-before-install": mounts,
|
|
274
|
+
"site-url": config.absoluteUrl || `http://localhost:${config.port}`,
|
|
275
|
+
blueprint: blueprintBundle,
|
|
276
|
+
wordpressInstallMode,
|
|
277
|
+
redis: true,
|
|
278
|
+
memcached: true
|
|
279
|
+
};
|
|
280
|
+
if (config.wpVersion) {
|
|
281
|
+
if (isWordPressDevVersion(config.wpVersion)) {
|
|
282
|
+
args.wp = "nightly";
|
|
283
|
+
} else {
|
|
284
|
+
args.wp = config.wpVersion;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (config.enableXdebug) {
|
|
288
|
+
logToConsole("Enabling Xdebug support");
|
|
289
|
+
args.xdebug = true;
|
|
290
|
+
}
|
|
291
|
+
lastCliArgs = sanitizeRunCLIArgs(args);
|
|
292
|
+
return args;
|
|
293
|
+
}
|
|
294
|
+
let startupAbortController = null;
|
|
295
|
+
let startingPromise = null;
|
|
296
|
+
function wrapWithStartingPromise(callback) {
|
|
297
|
+
return async (...args) => {
|
|
298
|
+
if (startingPromise) {
|
|
299
|
+
return startingPromise;
|
|
300
|
+
}
|
|
301
|
+
startingPromise = callback(...args);
|
|
302
|
+
return startingPromise;
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const startServer = wrapWithStartingPromise(
|
|
306
|
+
async (config, signal) => {
|
|
307
|
+
if (server) {
|
|
308
|
+
logToConsole(`Server already running for site ${config.siteId}`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
startupAbortController = new AbortController();
|
|
312
|
+
const stopSignal = AbortSignal.any([signal, startupAbortController.signal]);
|
|
313
|
+
try {
|
|
314
|
+
stopSignal.throwIfAborted();
|
|
315
|
+
const args = await getBaseRunCLIArgs("server", config);
|
|
316
|
+
server = await runCLI(args);
|
|
317
|
+
stopSignal.throwIfAborted();
|
|
318
|
+
if (config.adminPassword || config.adminUsername || config.adminEmail) {
|
|
319
|
+
await setAdminCredentials(
|
|
320
|
+
server,
|
|
321
|
+
config.adminPassword,
|
|
322
|
+
config.adminUsername,
|
|
323
|
+
config.adminEmail
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
stopSignal.throwIfAborted();
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (server) {
|
|
329
|
+
await server[Symbol.asyncDispose]();
|
|
330
|
+
server = null;
|
|
331
|
+
}
|
|
332
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
333
|
+
logToConsole(`Aborted start server operation:`, error);
|
|
334
|
+
} else {
|
|
335
|
+
errorToConsole(`Failed to start server:`, error);
|
|
336
|
+
}
|
|
337
|
+
throw error;
|
|
338
|
+
} finally {
|
|
339
|
+
startupAbortController = null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
const STOP_SERVER_TIMEOUT = 5e3;
|
|
344
|
+
async function stopServer() {
|
|
345
|
+
if (startupAbortController) {
|
|
346
|
+
logToConsole("Startup operation in progress. Aborting it to stop the server…");
|
|
347
|
+
startupAbortController.abort();
|
|
348
|
+
return "ABORTED_STARTUP";
|
|
349
|
+
}
|
|
350
|
+
if (!server) {
|
|
351
|
+
logToConsole("No server running, nothing to stop");
|
|
352
|
+
return "OK";
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
const disposalTimeout = new Promise(
|
|
356
|
+
(_, reject) => setTimeout(() => reject(new Error("Server disposal timeout")), STOP_SERVER_TIMEOUT)
|
|
357
|
+
);
|
|
358
|
+
await Promise.race([server[Symbol.asyncDispose](), disposalTimeout]);
|
|
359
|
+
logToConsole("Server stopped gracefully");
|
|
360
|
+
return "OK";
|
|
361
|
+
} catch (error) {
|
|
362
|
+
errorToConsole("Error during server disposal:", error);
|
|
363
|
+
throw error;
|
|
364
|
+
} finally {
|
|
365
|
+
server = null;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function runBlueprint(config, signal) {
|
|
369
|
+
try {
|
|
370
|
+
signal.throwIfAborted();
|
|
371
|
+
const args = await getBaseRunCLIArgs("run-blueprint", config);
|
|
372
|
+
await runCLI(args);
|
|
373
|
+
signal.throwIfAborted();
|
|
374
|
+
logToConsole(`Blueprint applied successfully for site ${config.siteId}`);
|
|
375
|
+
} catch (error) {
|
|
376
|
+
errorToConsole(`Failed to run Blueprint:`, error);
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const runWpCliCommand = sequential(
|
|
381
|
+
async (args, signal) => {
|
|
382
|
+
await Promise.allSettled([startingPromise]);
|
|
383
|
+
if (!server) {
|
|
384
|
+
throw new Error(`Failed to run WP CLI command because server is not running`);
|
|
385
|
+
}
|
|
386
|
+
logToConsole(`Running WP-CLI command:`, args);
|
|
387
|
+
signal.addEventListener(
|
|
388
|
+
"abort",
|
|
389
|
+
() => {
|
|
390
|
+
throw new Error("Operation aborted");
|
|
391
|
+
},
|
|
392
|
+
{ once: true }
|
|
393
|
+
);
|
|
394
|
+
const response = await server.playground.cli([
|
|
395
|
+
"php",
|
|
396
|
+
"/tmp/wp-cli.phar",
|
|
397
|
+
`--path=${await server.playground.documentRoot}`,
|
|
398
|
+
...args
|
|
399
|
+
]);
|
|
400
|
+
return {
|
|
401
|
+
stdout: await response.stdoutText,
|
|
402
|
+
stderr: await response.stderrText,
|
|
403
|
+
exitCode: await response.exitCode
|
|
404
|
+
};
|
|
405
|
+
},
|
|
406
|
+
{ concurrent: 3, max: 100, deduplicateKey: (args) => args.join(" ") }
|
|
407
|
+
);
|
|
408
|
+
function parsePhpError(error) {
|
|
409
|
+
if (!(error instanceof Error)) {
|
|
410
|
+
return String(error);
|
|
411
|
+
}
|
|
412
|
+
const message = error.message;
|
|
413
|
+
const wpDieMatch = message.match(/<div class="wp-die-message"[^>]*>([\s\S]*?)<\/div>/);
|
|
414
|
+
if (wpDieMatch) {
|
|
415
|
+
const htmlContent = wpDieMatch[1];
|
|
416
|
+
const textContent = htmlContent.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
417
|
+
if (textContent) {
|
|
418
|
+
return `WordPress error: ${textContent}`;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const fatalMatch = message.match(/PHP Fatal error:\s*(.+?)(?:\sin\s|$)/i);
|
|
422
|
+
if (fatalMatch) {
|
|
423
|
+
return `PHP Fatal error: ${fatalMatch[1].trim()}`;
|
|
424
|
+
}
|
|
425
|
+
if (message.includes("PHP.run() failed with exit code")) {
|
|
426
|
+
const exitCodeMatch = message.match(/exit code (\d+)/);
|
|
427
|
+
const exitCode = exitCodeMatch ? exitCodeMatch[1] : "unknown";
|
|
428
|
+
return `WordPress failed to start (PHP exit code ${exitCode}). Check the site's debug.log for details.`;
|
|
429
|
+
}
|
|
430
|
+
return message;
|
|
431
|
+
}
|
|
432
|
+
function sendErrorMessage(messageId, error) {
|
|
433
|
+
return new Promise((resolve) => {
|
|
434
|
+
const errorResponse = {
|
|
435
|
+
originalMessageId: messageId,
|
|
436
|
+
topic: "error",
|
|
437
|
+
errorMessage: parsePhpError(error),
|
|
438
|
+
errorStack: error instanceof Error ? error.stack : void 0,
|
|
439
|
+
cliArgs: lastCliArgs ?? void 0
|
|
440
|
+
};
|
|
441
|
+
process.send(errorResponse, () => {
|
|
442
|
+
resolve();
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
const abortControllers = {};
|
|
447
|
+
async function ipcMessageHandler(packet) {
|
|
448
|
+
const messageResult = managerMessageSchema.safeParse(packet);
|
|
449
|
+
if (!messageResult.success) {
|
|
450
|
+
errorToConsole("Invalid message received:", messageResult.error);
|
|
451
|
+
const minimalMessageSchema = z.object({ id: z.string() });
|
|
452
|
+
const minimalMessage = minimalMessageSchema.safeParse(packet);
|
|
453
|
+
if (minimalMessage.success) {
|
|
454
|
+
await sendErrorMessage(minimalMessage.data.id, messageResult.error);
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const validMessage = messageResult.data;
|
|
459
|
+
if (validMessage.topic !== "abort") {
|
|
460
|
+
abortControllers[validMessage.messageId] = new AbortController();
|
|
461
|
+
}
|
|
462
|
+
const abortController = abortControllers[validMessage.messageId];
|
|
463
|
+
logToConsole(`Received ${validMessage.topic} message`);
|
|
464
|
+
try {
|
|
465
|
+
let result;
|
|
466
|
+
switch (validMessage.topic) {
|
|
467
|
+
case "abort":
|
|
468
|
+
abortController?.abort();
|
|
469
|
+
return;
|
|
470
|
+
case "start-server":
|
|
471
|
+
result = await startServer(validMessage.data.config, abortController.signal);
|
|
472
|
+
break;
|
|
473
|
+
case "run-blueprint":
|
|
474
|
+
result = await runBlueprint(validMessage.data.config, abortController.signal);
|
|
475
|
+
break;
|
|
476
|
+
case "stop-server":
|
|
477
|
+
result = await stopServer();
|
|
478
|
+
break;
|
|
479
|
+
case "wp-cli-command":
|
|
480
|
+
try {
|
|
481
|
+
result = await runWpCliCommand(validMessage.data.args, abortController.signal);
|
|
482
|
+
} catch (wpCliError) {
|
|
483
|
+
errorToConsole(`WP-CLI error:`, wpCliError);
|
|
484
|
+
await sendErrorMessage(validMessage.messageId, wpCliError);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
default:
|
|
489
|
+
throw new Error(`Unknown message.`);
|
|
490
|
+
}
|
|
491
|
+
const response = {
|
|
492
|
+
originalMessageId: validMessage.messageId,
|
|
493
|
+
topic: "result",
|
|
494
|
+
result
|
|
495
|
+
};
|
|
496
|
+
process.send(response);
|
|
497
|
+
if (validMessage.topic === "stop-server" && result === "OK") {
|
|
498
|
+
process.disconnect();
|
|
499
|
+
}
|
|
500
|
+
} catch (error) {
|
|
501
|
+
errorToConsole(`Error handling message ${validMessage.topic}:`, error);
|
|
502
|
+
await sendErrorMessage(validMessage.messageId, error);
|
|
503
|
+
originalConsoleLog("Killing process because of", error);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
} finally {
|
|
506
|
+
delete abortControllers[validMessage.messageId];
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (process.send) {
|
|
510
|
+
process.on("message", ipcMessageHandler);
|
|
511
|
+
process.send({ topic: "ready" });
|
|
512
|
+
} else {
|
|
513
|
+
throw new Error("process.send is not available");
|
|
514
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const serverConfig = z.object({
|
|
3
|
+
siteId: z.string(),
|
|
4
|
+
sitePath: z.string(),
|
|
5
|
+
port: z.number(),
|
|
6
|
+
phpVersion: z.string().optional(),
|
|
7
|
+
wpVersion: z.string().optional(),
|
|
8
|
+
absoluteUrl: z.string().optional(),
|
|
9
|
+
adminUsername: z.string().optional(),
|
|
10
|
+
adminPassword: z.string().optional(),
|
|
11
|
+
adminEmail: z.string().optional(),
|
|
12
|
+
siteTitle: z.string().optional(),
|
|
13
|
+
siteLanguage: z.string().optional(),
|
|
14
|
+
isWpAutoUpdating: z.boolean().optional(),
|
|
15
|
+
enableXdebug: z.boolean().optional(),
|
|
16
|
+
enableDebugLog: z.boolean().optional(),
|
|
17
|
+
enableDebugDisplay: z.boolean().optional(),
|
|
18
|
+
blueprint: z.object({
|
|
19
|
+
contents: z.any(),
|
|
20
|
+
// Blueprint type is complex, allow any for now
|
|
21
|
+
uri: z.string()
|
|
22
|
+
}).optional()
|
|
23
|
+
});
|
|
24
|
+
const managerMessageAbort = z.object({
|
|
25
|
+
topic: z.literal("abort"),
|
|
26
|
+
data: z.object({})
|
|
27
|
+
});
|
|
28
|
+
const managerMessageStartServer = z.object({
|
|
29
|
+
topic: z.literal("start-server"),
|
|
30
|
+
data: z.object({
|
|
31
|
+
config: serverConfig
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
const managerMessageRunBlueprint = z.object({
|
|
35
|
+
topic: z.literal("run-blueprint"),
|
|
36
|
+
data: z.object({
|
|
37
|
+
config: serverConfig
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
const managerMessageStopServer = z.object({
|
|
41
|
+
topic: z.literal("stop-server"),
|
|
42
|
+
data: z.object({})
|
|
43
|
+
});
|
|
44
|
+
const managerMessageWpCliCommand = z.object({
|
|
45
|
+
topic: z.literal("wp-cli-command"),
|
|
46
|
+
data: z.object({
|
|
47
|
+
args: z.array(z.string())
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
z.discriminatedUnion("topic", [
|
|
51
|
+
managerMessageAbort,
|
|
52
|
+
managerMessageStartServer,
|
|
53
|
+
managerMessageRunBlueprint,
|
|
54
|
+
managerMessageStopServer,
|
|
55
|
+
managerMessageWpCliCommand
|
|
56
|
+
]);
|
|
57
|
+
const managerMessageBase = z.object({ messageId: z.string() });
|
|
58
|
+
const managerMessageSchema = z.discriminatedUnion("topic", [
|
|
59
|
+
managerMessageBase.extend(managerMessageAbort.shape),
|
|
60
|
+
managerMessageBase.extend(managerMessageStartServer.shape),
|
|
61
|
+
managerMessageBase.extend(managerMessageRunBlueprint.shape),
|
|
62
|
+
managerMessageBase.extend(managerMessageStopServer.shape),
|
|
63
|
+
managerMessageBase.extend(managerMessageWpCliCommand.shape)
|
|
64
|
+
]);
|
|
65
|
+
const childMessageReady = z.object({
|
|
66
|
+
topic: z.literal("ready")
|
|
67
|
+
});
|
|
68
|
+
const childMessageActivity = z.object({
|
|
69
|
+
topic: z.literal("activity")
|
|
70
|
+
});
|
|
71
|
+
const childMessageResult = z.object({
|
|
72
|
+
originalMessageId: z.string(),
|
|
73
|
+
topic: z.literal("result"),
|
|
74
|
+
result: z.unknown()
|
|
75
|
+
});
|
|
76
|
+
const childMessageError = z.object({
|
|
77
|
+
originalMessageId: z.string(),
|
|
78
|
+
topic: z.literal("error"),
|
|
79
|
+
errorMessage: z.string(),
|
|
80
|
+
errorStack: z.string().optional(),
|
|
81
|
+
cliArgs: z.record(z.string(), z.unknown()).optional()
|
|
82
|
+
});
|
|
83
|
+
const childMessageConsole = z.object({
|
|
84
|
+
topic: z.literal("console-message"),
|
|
85
|
+
message: z.string()
|
|
86
|
+
});
|
|
87
|
+
const childMessageSiteCreated = z.object({
|
|
88
|
+
topic: z.literal("site-created"),
|
|
89
|
+
data: z.object({
|
|
90
|
+
siteId: z.string()
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
const childMessageSiteUpdated = z.object({
|
|
94
|
+
topic: z.literal("site-updated"),
|
|
95
|
+
data: z.object({
|
|
96
|
+
siteId: z.string()
|
|
97
|
+
})
|
|
98
|
+
});
|
|
99
|
+
const childMessageSiteDeleted = z.object({
|
|
100
|
+
topic: z.literal("site-deleted"),
|
|
101
|
+
data: z.object({
|
|
102
|
+
siteId: z.string()
|
|
103
|
+
})
|
|
104
|
+
});
|
|
105
|
+
const childMessageSiteStarted = z.object({
|
|
106
|
+
topic: z.literal("site-started"),
|
|
107
|
+
data: z.object({
|
|
108
|
+
siteId: z.string(),
|
|
109
|
+
url: z.string()
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
const childMessageSiteStopped = z.object({
|
|
113
|
+
topic: z.literal("site-stopped"),
|
|
114
|
+
data: z.object({
|
|
115
|
+
siteId: z.string()
|
|
116
|
+
})
|
|
117
|
+
});
|
|
118
|
+
const childMessageRaw = z.discriminatedUnion("topic", [
|
|
119
|
+
childMessageReady,
|
|
120
|
+
childMessageActivity,
|
|
121
|
+
childMessageResult,
|
|
122
|
+
childMessageError,
|
|
123
|
+
childMessageConsole,
|
|
124
|
+
childMessageSiteCreated,
|
|
125
|
+
childMessageSiteUpdated,
|
|
126
|
+
childMessageSiteDeleted,
|
|
127
|
+
childMessageSiteStarted,
|
|
128
|
+
childMessageSiteStopped
|
|
129
|
+
]);
|
|
130
|
+
const childMessageFromProcessManagerSchema = z.object({
|
|
131
|
+
process: z.object({
|
|
132
|
+
name: z.string(),
|
|
133
|
+
pm_id: z.number()
|
|
134
|
+
}),
|
|
135
|
+
raw: childMessageRaw
|
|
136
|
+
});
|
|
137
|
+
export {
|
|
138
|
+
childMessageFromProcessManagerSchema as c,
|
|
139
|
+
managerMessageSchema as m
|
|
140
|
+
};
|