@wp-playground/cli 1.2.2 → 2.0.0
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 +3 -0
- package/blueprints-BQYAZ9gc.js +9 -0
- package/blueprints-BQYAZ9gc.js.map +1 -0
- package/blueprints-DLntJtVr.js +9 -0
- package/blueprints-DLntJtVr.js.map +1 -0
- package/blueprints-ZBrY1bvK.cjs +2 -0
- package/blueprints-ZBrY1bvK.cjs.map +1 -0
- package/blueprints-v1/blueprints-v1-handler.d.ts +31 -0
- package/{worker-thread.d.ts → blueprints-v1/worker-thread-v1.d.ts} +5 -3
- package/blueprints-v2/blueprint-v2-declaration.d.ts +10 -0
- package/blueprints-v2/blueprints-v2-handler.d.ts +31 -0
- package/blueprints-v2/get-v2-runner.d.ts +1 -0
- package/blueprints-v2/run-blueprint-v2.d.ts +35 -0
- package/blueprints-v2/worker-thread-v2.d.ts +49 -0
- package/blueprints.phar +0 -0
- package/cli.cjs +1 -1
- package/cli.cjs.map +1 -1
- package/cli.js +2 -4
- package/cli.js.map +1 -1
- package/index.cjs +1 -1
- package/index.js +1 -1
- package/load-balancer.d.ts +3 -1
- package/mounts-B-Qdcyyt.js +124 -0
- package/mounts-B-Qdcyyt.js.map +1 -0
- package/mounts-D7bhhGq3.cjs +15 -0
- package/mounts-D7bhhGq3.cjs.map +1 -0
- package/{mount.d.ts → mounts.d.ts} +10 -1
- package/package.json +11 -11
- package/run-cli-CY1lYIJH.js +674 -0
- package/run-cli-CY1lYIJH.js.map +1 -0
- package/run-cli-opqDVXKw.cjs +27 -0
- package/run-cli-opqDVXKw.cjs.map +1 -0
- package/run-cli.d.ts +30 -6
- package/worker-thread-v1-BTJIbQLy.js +133 -0
- package/worker-thread-v1-BTJIbQLy.js.map +1 -0
- package/worker-thread-v1.cjs +3 -0
- package/worker-thread-v1.cjs.map +1 -0
- package/worker-thread-v1.js +128 -0
- package/worker-thread-v1.js.map +1 -0
- package/worker-thread-v2-Pfv6UYF4.js +429 -0
- package/worker-thread-v2-Pfv6UYF4.js.map +1 -0
- package/worker-thread-v2.cjs +92 -0
- package/worker-thread-v2.cjs.map +1 -0
- package/worker-thread-v2.js +432 -0
- package/worker-thread-v2.js.map +1 -0
- package/cli-auto-mount.d.ts +0 -19
- package/index-CddYZc1x.js +0 -5
- package/index-CddYZc1x.js.map +0 -1
- package/index-CyPmrjJv.cjs +0 -2
- package/index-CyPmrjJv.cjs.map +0 -1
- package/reportable-error.d.ts +0 -5
- package/run-cli-Bon3Rz_F.cjs +0 -43
- package/run-cli-Bon3Rz_F.cjs.map +0 -1
- package/run-cli-DyJ6_Vj8.js +0 -8449
- package/run-cli-DyJ6_Vj8.js.map +0 -1
- package/worker-thread-CQBM_bGk.js +0 -171
- package/worker-thread-CQBM_bGk.js.map +0 -1
- package/worker-thread.cjs +0 -3
- package/worker-thread.cjs.map +0 -1
- package/worker-thread.js +0 -124
- package/worker-thread.js.map +0 -1
- /package/{download.d.ts → blueprints-v1/download.d.ts} +0 -0
- /package/{server.d.ts → start-server.d.ts} +0 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
import { logger as p, errorLogPath as z } from "@php-wasm/logger";
|
|
2
|
+
import { consumeAPI as L, SupportedPHPVersions as T, PHPResponse as F, exposeAPI as V, exposeSyncAPI as q } from "@php-wasm/universal";
|
|
3
|
+
import { resolveRemoteBlueprint as j, isBlueprintBundle as O, compileBlueprint as D, runBlueprintSteps as Z } from "@wp-playground/blueprints";
|
|
4
|
+
import { zipDirectory as M, RecommendedPHPVersion as B, unzipFile as _ } from "@wp-playground/common";
|
|
5
|
+
import m from "fs";
|
|
6
|
+
import { Worker as N, MessageChannel as J } from "worker_threads";
|
|
7
|
+
import { p as R, a as Y, e as Q } from "./mounts-B-Qdcyyt.js";
|
|
8
|
+
import X from "express";
|
|
9
|
+
import { FileLockManagerForNode as G } from "@php-wasm/node";
|
|
10
|
+
import K, { cpus as ee } from "os";
|
|
11
|
+
import { jspi as te } from "wasm-feature-detect";
|
|
12
|
+
import re from "yargs";
|
|
13
|
+
import y, { basename as oe } from "path";
|
|
14
|
+
import { NodeJsFilesystem as se, OverlayFilesystem as ie, InMemoryFilesystem as ne, ZipFilesystem as ae } from "@wp-playground/storage";
|
|
15
|
+
import { EmscriptenDownloadMonitor as le, ProgressTracker as pe } from "@php-wasm/progress";
|
|
16
|
+
import { resolveWordPressRelease as de } from "@wp-playground/wordpress";
|
|
17
|
+
import P from "fs-extra";
|
|
18
|
+
async function ce(t) {
|
|
19
|
+
const r = X(), e = await new Promise((i, n) => {
|
|
20
|
+
const l = r.listen(t.port, () => {
|
|
21
|
+
const a = l.address();
|
|
22
|
+
a === null || typeof a == "string" ? n(new Error("Server address is not available")) : i(l);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
r.use("/", async (i, n) => {
|
|
26
|
+
const l = await t.handleRequest({
|
|
27
|
+
url: i.url,
|
|
28
|
+
headers: he(i),
|
|
29
|
+
method: i.method,
|
|
30
|
+
body: await ue(i)
|
|
31
|
+
});
|
|
32
|
+
n.statusCode = l.httpStatusCode;
|
|
33
|
+
for (const a in l.headers)
|
|
34
|
+
n.setHeader(a, l.headers[a]);
|
|
35
|
+
n.end(l.bytes);
|
|
36
|
+
});
|
|
37
|
+
const s = e.address().port;
|
|
38
|
+
return await t.onBind(e, s);
|
|
39
|
+
}
|
|
40
|
+
const ue = async (t) => await new Promise((r) => {
|
|
41
|
+
const e = [];
|
|
42
|
+
t.on("data", (o) => {
|
|
43
|
+
e.push(o);
|
|
44
|
+
}), t.on("end", () => {
|
|
45
|
+
r(new Uint8Array(Buffer.concat(e)));
|
|
46
|
+
});
|
|
47
|
+
}), he = (t) => {
|
|
48
|
+
const r = {};
|
|
49
|
+
if (t.rawHeaders && t.rawHeaders.length)
|
|
50
|
+
for (let e = 0; e < t.rawHeaders.length; e += 2)
|
|
51
|
+
r[t.rawHeaders[e].toLowerCase()] = t.rawHeaders[e + 1];
|
|
52
|
+
return r;
|
|
53
|
+
};
|
|
54
|
+
class fe {
|
|
55
|
+
constructor(r) {
|
|
56
|
+
this.workerLoads = [], this.addWorker(r);
|
|
57
|
+
}
|
|
58
|
+
addWorker(r) {
|
|
59
|
+
this.workerLoads.push({
|
|
60
|
+
worker: r,
|
|
61
|
+
activeRequests: /* @__PURE__ */ new Set()
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async handleRequest(r) {
|
|
65
|
+
let e = this.workerLoads[0];
|
|
66
|
+
for (let s = 1; s < this.workerLoads.length; s++) {
|
|
67
|
+
const i = this.workerLoads[s];
|
|
68
|
+
i.activeRequests.size < e.activeRequests.size && (e = i);
|
|
69
|
+
}
|
|
70
|
+
const o = e.worker.request(r);
|
|
71
|
+
return e.activeRequests.add(o), o.url = r.url, o.finally(() => {
|
|
72
|
+
e.activeRequests.delete(o);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function me(t) {
|
|
77
|
+
return /^latest$|^trunk$|^nightly$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/.test(t);
|
|
78
|
+
}
|
|
79
|
+
async function we({
|
|
80
|
+
sourceString: t,
|
|
81
|
+
blueprintMayReadAdjacentFiles: r
|
|
82
|
+
}) {
|
|
83
|
+
if (!t)
|
|
84
|
+
return;
|
|
85
|
+
if (t.startsWith("http://") || t.startsWith("https://"))
|
|
86
|
+
return await j(t);
|
|
87
|
+
let e = y.resolve(process.cwd(), t);
|
|
88
|
+
if (!m.existsSync(e))
|
|
89
|
+
throw new Error(`Blueprint file does not exist: ${e}`);
|
|
90
|
+
const o = m.statSync(e);
|
|
91
|
+
if (o.isDirectory() && (e = y.join(e, "blueprint.json")), !o.isFile() && o.isSymbolicLink())
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Blueprint path is neither a file nor a directory: ${e}`
|
|
94
|
+
);
|
|
95
|
+
const s = y.extname(e);
|
|
96
|
+
switch (s) {
|
|
97
|
+
case ".zip":
|
|
98
|
+
return ae.fromArrayBuffer(
|
|
99
|
+
m.readFileSync(e).buffer
|
|
100
|
+
);
|
|
101
|
+
case ".json": {
|
|
102
|
+
const i = m.readFileSync(e, "utf-8");
|
|
103
|
+
try {
|
|
104
|
+
JSON.parse(i);
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Blueprint file at ${e} is not a valid JSON file`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const n = y.dirname(e), l = new se(n);
|
|
111
|
+
return new ie([
|
|
112
|
+
new ne({
|
|
113
|
+
"blueprint.json": i
|
|
114
|
+
}),
|
|
115
|
+
/**
|
|
116
|
+
* Wrap the NodeJS filesystem to prevent access to local files
|
|
117
|
+
* unless the user explicitly allowed it.
|
|
118
|
+
*/
|
|
119
|
+
{
|
|
120
|
+
read(a) {
|
|
121
|
+
if (!r)
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Error: Blueprint contained tried to read a local file at path "${a}" (via a resource of type "bundled"). Playground restricts access to local resources by default as a security measure.
|
|
124
|
+
|
|
125
|
+
You can allow this Blueprint to read files from the same parent directory by explicitly adding the --blueprint-may-read-adjacent-files option to your command.`
|
|
126
|
+
);
|
|
127
|
+
return l.read(a);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Unsupported blueprint file extension: ${s}. Only .zip and .json files are supported.`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const ge = "" + new URL("worker-thread-v2-Pfv6UYF4.js", import.meta.url).href;
|
|
139
|
+
class ye {
|
|
140
|
+
constructor(r, e) {
|
|
141
|
+
this.lastProgressMessage = "", this.args = r, this.siteUrl = e.siteUrl, this.processIdSpaceLength = e.processIdSpaceLength, this.phpVersion = r.php;
|
|
142
|
+
}
|
|
143
|
+
getWorkerUrl() {
|
|
144
|
+
return ge;
|
|
145
|
+
}
|
|
146
|
+
async bootPrimaryWorker(r, e) {
|
|
147
|
+
const o = L(r);
|
|
148
|
+
await o.useFileLockManager(e);
|
|
149
|
+
const s = {
|
|
150
|
+
...this.args,
|
|
151
|
+
php: this.phpVersion,
|
|
152
|
+
siteUrl: this.siteUrl,
|
|
153
|
+
firstProcessId: 1,
|
|
154
|
+
processIdSpaceLength: this.processIdSpaceLength,
|
|
155
|
+
trace: this.args.debug || !1,
|
|
156
|
+
blueprint: this.args.blueprint
|
|
157
|
+
};
|
|
158
|
+
return await o.bootAsPrimaryWorker(s), o;
|
|
159
|
+
}
|
|
160
|
+
async bootSecondaryWorker({
|
|
161
|
+
worker: r,
|
|
162
|
+
fileLockManagerPort: e,
|
|
163
|
+
firstProcessId: o
|
|
164
|
+
}) {
|
|
165
|
+
const s = L(r.phpPort);
|
|
166
|
+
await s.useFileLockManager(e);
|
|
167
|
+
const i = {
|
|
168
|
+
...this.args,
|
|
169
|
+
php: this.phpVersion,
|
|
170
|
+
siteUrl: this.siteUrl,
|
|
171
|
+
firstProcessId: o,
|
|
172
|
+
processIdSpaceLength: this.processIdSpaceLength,
|
|
173
|
+
trace: this.args.debug || !1,
|
|
174
|
+
blueprint: this.args.blueprint
|
|
175
|
+
};
|
|
176
|
+
return await s.bootAsSecondaryWorker(i), s;
|
|
177
|
+
}
|
|
178
|
+
writeProgressUpdate(r, e, o) {
|
|
179
|
+
e !== this.lastProgressMessage && (this.lastProgressMessage = e, r.isTTY ? (r.cursorTo(0), r.write(e), r.clearLine(1), o && r.write(`
|
|
180
|
+
`)) : r.write(`${e}
|
|
181
|
+
`));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const I = y.join(K.homedir(), ".wordpress-playground");
|
|
185
|
+
async function be(t) {
|
|
186
|
+
return await $(
|
|
187
|
+
"https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip",
|
|
188
|
+
"sqlite.zip",
|
|
189
|
+
t
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
async function $(t, r, e) {
|
|
193
|
+
const o = y.join(I, r);
|
|
194
|
+
return P.existsSync(o) || (P.ensureDirSync(I), await Pe(t, o, e)), A(o);
|
|
195
|
+
}
|
|
196
|
+
async function Pe(t, r, e) {
|
|
197
|
+
const s = (await e.monitorFetch(fetch(t))).body.getReader(), i = `${r}.partial`, n = P.createWriteStream(i);
|
|
198
|
+
for (; ; ) {
|
|
199
|
+
const { done: l, value: a } = await s.read();
|
|
200
|
+
if (a && n.write(a), l)
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
n.close(), n.closed || await new Promise((l, a) => {
|
|
204
|
+
n.on("finish", () => {
|
|
205
|
+
P.renameSync(i, r), l(null);
|
|
206
|
+
}), n.on("error", (c) => {
|
|
207
|
+
P.removeSync(i), a(c);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function A(t, r) {
|
|
212
|
+
return new File([P.readFileSync(t)], oe(t));
|
|
213
|
+
}
|
|
214
|
+
const ke = "" + new URL("worker-thread-v1-BTJIbQLy.js", import.meta.url).href;
|
|
215
|
+
class ve {
|
|
216
|
+
constructor(r, e) {
|
|
217
|
+
this.lastProgressMessage = "", this.args = r, this.siteUrl = e.siteUrl, this.processIdSpaceLength = e.processIdSpaceLength;
|
|
218
|
+
}
|
|
219
|
+
getWorkerUrl() {
|
|
220
|
+
return ke;
|
|
221
|
+
}
|
|
222
|
+
async bootPrimaryWorker(r, e) {
|
|
223
|
+
const o = await this.compileInputBlueprint(
|
|
224
|
+
this.args["additional-blueprint-steps"] || []
|
|
225
|
+
);
|
|
226
|
+
this.phpVersion = o.versions.php;
|
|
227
|
+
let s;
|
|
228
|
+
const i = new le();
|
|
229
|
+
if (!this.args.skipWordPressSetup) {
|
|
230
|
+
let k = !1;
|
|
231
|
+
i.addEventListener("progress", (h) => {
|
|
232
|
+
if (k)
|
|
233
|
+
return;
|
|
234
|
+
const { loaded: g, total: v } = h.detail, f = Math.floor(
|
|
235
|
+
Math.min(100, 100 * g / v)
|
|
236
|
+
);
|
|
237
|
+
k = f === 100, this.args.quiet || this.writeProgressUpdate(
|
|
238
|
+
process.stdout,
|
|
239
|
+
`Downloading WordPress ${f}%...`,
|
|
240
|
+
k
|
|
241
|
+
);
|
|
242
|
+
}), s = await de(this.args.wp), p.log(
|
|
243
|
+
`Resolved WordPress release URL: ${s?.releaseUrl}`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
const n = s && y.join(
|
|
247
|
+
I,
|
|
248
|
+
`prebuilt-wp-content-for-wp-${s.version}.zip`
|
|
249
|
+
), l = s ? m.existsSync(n) ? A(n) : await $(
|
|
250
|
+
s.releaseUrl,
|
|
251
|
+
`${s.version}.zip`,
|
|
252
|
+
i
|
|
253
|
+
) : void 0;
|
|
254
|
+
p.log("Fetching SQLite integration plugin...");
|
|
255
|
+
const a = this.args.skipSqliteSetup ? void 0 : await be(i), c = this.args.followSymlinks === !0, d = this.args.experimentalTrace === !0, w = this.args["mount-before-install"] || [], b = this.args.mount || [], u = L(r);
|
|
256
|
+
return await u.isConnected(), p.log("Booting WordPress..."), await u.useFileLockManager(e), await u.bootAsPrimaryWorker({
|
|
257
|
+
phpVersion: this.phpVersion,
|
|
258
|
+
wpVersion: o.versions.wp,
|
|
259
|
+
absoluteUrl: this.siteUrl,
|
|
260
|
+
mountsBeforeWpInstall: w,
|
|
261
|
+
mountsAfterWpInstall: b,
|
|
262
|
+
wordPressZip: l && await l.arrayBuffer(),
|
|
263
|
+
sqliteIntegrationPluginZip: await a.arrayBuffer(),
|
|
264
|
+
firstProcessId: 0,
|
|
265
|
+
processIdSpaceLength: this.processIdSpaceLength,
|
|
266
|
+
followSymlinks: c,
|
|
267
|
+
trace: d,
|
|
268
|
+
internalCookieStore: this.args.internalCookieStore,
|
|
269
|
+
withXdebug: this.args.xdebug
|
|
270
|
+
}), s && !this.args["mount-before-install"] && !m.existsSync(n) && (p.log("Caching preinstalled WordPress for the next boot..."), m.writeFileSync(
|
|
271
|
+
n,
|
|
272
|
+
await M(u, "/wordpress")
|
|
273
|
+
), p.log("Cached!")), u;
|
|
274
|
+
}
|
|
275
|
+
async bootSecondaryWorker({
|
|
276
|
+
worker: r,
|
|
277
|
+
fileLockManagerPort: e,
|
|
278
|
+
firstProcessId: o
|
|
279
|
+
}) {
|
|
280
|
+
const s = L(
|
|
281
|
+
r.phpPort
|
|
282
|
+
);
|
|
283
|
+
return await s.isConnected(), await s.useFileLockManager(e), await s.bootAsSecondaryWorker({
|
|
284
|
+
phpVersion: this.phpVersion,
|
|
285
|
+
absoluteUrl: this.siteUrl,
|
|
286
|
+
mountsBeforeWpInstall: this.args["mount-before-install"] || [],
|
|
287
|
+
mountsAfterWpInstall: this.args.mount || [],
|
|
288
|
+
// Skip WordPress zip because we share the /wordpress directory
|
|
289
|
+
// populated by the initial worker.
|
|
290
|
+
wordPressZip: void 0,
|
|
291
|
+
// Skip SQLite integration plugin for now because we
|
|
292
|
+
// will copy it from primary's `/internal` directory.
|
|
293
|
+
sqliteIntegrationPluginZip: void 0,
|
|
294
|
+
dataSqlPath: "/wordpress/wp-content/database/.ht.sqlite",
|
|
295
|
+
firstProcessId: o,
|
|
296
|
+
processIdSpaceLength: this.processIdSpaceLength,
|
|
297
|
+
followSymlinks: this.args.followSymlinks === !0,
|
|
298
|
+
trace: this.args.experimentalTrace === !0,
|
|
299
|
+
// @TODO: Move this to the request handler or else every worker
|
|
300
|
+
// will have a separate cookie store.
|
|
301
|
+
internalCookieStore: this.args.internalCookieStore,
|
|
302
|
+
withXdebug: this.args.xdebug
|
|
303
|
+
}), await s.isReady(), s;
|
|
304
|
+
}
|
|
305
|
+
async compileInputBlueprint(r) {
|
|
306
|
+
const e = this.args, o = e.blueprint, s = O(o) ? o : {
|
|
307
|
+
login: e.login,
|
|
308
|
+
...o || {},
|
|
309
|
+
preferredVersions: {
|
|
310
|
+
php: e.php ?? o?.preferredVersions?.php ?? B,
|
|
311
|
+
wp: e.wp ?? o?.preferredVersions?.wp ?? "latest",
|
|
312
|
+
...o?.preferredVersions || {}
|
|
313
|
+
}
|
|
314
|
+
}, i = new pe();
|
|
315
|
+
let n = "", l = !1;
|
|
316
|
+
return i.addEventListener("progress", (a) => {
|
|
317
|
+
if (l)
|
|
318
|
+
return;
|
|
319
|
+
l = a.detail.progress === 100;
|
|
320
|
+
const c = Math.floor(a.detail.progress);
|
|
321
|
+
n = a.detail.caption || n || "Running the Blueprint";
|
|
322
|
+
const d = `${n.trim()} – ${c}%`;
|
|
323
|
+
e.quiet || this.writeProgressUpdate(
|
|
324
|
+
process.stdout,
|
|
325
|
+
d,
|
|
326
|
+
l
|
|
327
|
+
);
|
|
328
|
+
}), await D(s, {
|
|
329
|
+
progress: i,
|
|
330
|
+
additionalSteps: r
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
writeProgressUpdate(r, e, o) {
|
|
334
|
+
e !== this.lastProgressMessage && (this.lastProgressMessage = e, r.isTTY ? (r.cursorTo(0), r.write(e), r.clearLine(1), o && r.write(`
|
|
335
|
+
`)) : r.write(`${e}
|
|
336
|
+
`));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function Oe() {
|
|
340
|
+
try {
|
|
341
|
+
const t = re(process.argv.slice(2)).usage("Usage: wp-playground <command> [options]").positional("command", {
|
|
342
|
+
describe: "Command to run",
|
|
343
|
+
choices: ["server", "run-blueprint", "build-snapshot"],
|
|
344
|
+
demandOption: !0
|
|
345
|
+
}).option("outfile", {
|
|
346
|
+
describe: "When building, write to this output file.",
|
|
347
|
+
type: "string",
|
|
348
|
+
default: "wordpress.zip"
|
|
349
|
+
}).option("port", {
|
|
350
|
+
describe: "Port to listen on when serving.",
|
|
351
|
+
type: "number",
|
|
352
|
+
default: 9400
|
|
353
|
+
}).option("php", {
|
|
354
|
+
describe: "PHP version to use.",
|
|
355
|
+
type: "string",
|
|
356
|
+
default: B,
|
|
357
|
+
choices: T
|
|
358
|
+
}).option("wp", {
|
|
359
|
+
describe: "WordPress version to use.",
|
|
360
|
+
type: "string",
|
|
361
|
+
default: "latest"
|
|
362
|
+
}).option("mount", {
|
|
363
|
+
describe: "Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path",
|
|
364
|
+
type: "array",
|
|
365
|
+
string: !0,
|
|
366
|
+
coerce: R
|
|
367
|
+
}).option("mount-before-install", {
|
|
368
|
+
describe: "Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: /host/path:/vfs/path",
|
|
369
|
+
type: "array",
|
|
370
|
+
string: !0,
|
|
371
|
+
coerce: R
|
|
372
|
+
}).option("mount-dir", {
|
|
373
|
+
describe: 'Mount a directory to the PHP runtime (can be used multiple times). Format: "/host/path" "/vfs/path"',
|
|
374
|
+
type: "array",
|
|
375
|
+
nargs: 2,
|
|
376
|
+
array: !0
|
|
377
|
+
// coerce: parseMountDirArguments,
|
|
378
|
+
}).option("mount-dir-before-install", {
|
|
379
|
+
describe: 'Mount a directory before WordPress installation (can be used multiple times). Format: "/host/path" "/vfs/path"',
|
|
380
|
+
type: "string",
|
|
381
|
+
nargs: 2,
|
|
382
|
+
array: !0,
|
|
383
|
+
coerce: Y
|
|
384
|
+
}).option("login", {
|
|
385
|
+
describe: "Should log the user in",
|
|
386
|
+
type: "boolean",
|
|
387
|
+
default: !1
|
|
388
|
+
}).option("blueprint", {
|
|
389
|
+
describe: "Blueprint to execute.",
|
|
390
|
+
type: "string"
|
|
391
|
+
}).option("blueprint-may-read-adjacent-files", {
|
|
392
|
+
describe: 'Consent flag: Allow "bundled" resources in a local blueprint to read files in the same directory as the blueprint file.',
|
|
393
|
+
type: "boolean",
|
|
394
|
+
default: !1
|
|
395
|
+
}).option("skip-wordpress-setup", {
|
|
396
|
+
describe: "Do not download, unzip, and install WordPress. Useful for mounting a pre-configured WordPress directory at /wordpress.",
|
|
397
|
+
type: "boolean",
|
|
398
|
+
default: !1
|
|
399
|
+
}).option("skip-sqlite-setup", {
|
|
400
|
+
describe: "Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.",
|
|
401
|
+
type: "boolean",
|
|
402
|
+
default: !1
|
|
403
|
+
}).option("quiet", {
|
|
404
|
+
describe: "Do not output logs and progress messages.",
|
|
405
|
+
type: "boolean",
|
|
406
|
+
default: !1
|
|
407
|
+
}).option("debug", {
|
|
408
|
+
describe: "Print PHP error log content if an error occurs during Playground boot.",
|
|
409
|
+
type: "boolean",
|
|
410
|
+
default: !1
|
|
411
|
+
}).option("auto-mount", {
|
|
412
|
+
describe: "Automatically mount the current working directory. You can mount a WordPress directory, a plugin directory, a theme directory, a wp-content directory, or any directory containing PHP and HTML files.",
|
|
413
|
+
type: "boolean",
|
|
414
|
+
default: !1
|
|
415
|
+
}).option("follow-symlinks", {
|
|
416
|
+
describe: `Allow Playground to follow symlinks by automatically mounting symlinked directories and files encountered in mounted directories.
|
|
417
|
+
Warning: Following symlinks will expose files outside mounted directories to Playground and could be a security risk.`,
|
|
418
|
+
type: "boolean",
|
|
419
|
+
default: !1
|
|
420
|
+
}).option("experimental-trace", {
|
|
421
|
+
describe: "Print detailed messages about system behavior to the console. Useful for troubleshooting.",
|
|
422
|
+
type: "boolean",
|
|
423
|
+
default: !1,
|
|
424
|
+
// Hide this option because we want to replace with a more general log-level flag.
|
|
425
|
+
hidden: !0
|
|
426
|
+
}).option("internal-cookie-store", {
|
|
427
|
+
describe: "Enable internal cookie handling. When enabled, Playground will manage cookies internally using an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled externally (e.g., by a browser in Node.js environments).",
|
|
428
|
+
type: "boolean",
|
|
429
|
+
default: !1
|
|
430
|
+
}).option("xdebug", {
|
|
431
|
+
describe: "Enable Xdebug.",
|
|
432
|
+
type: "boolean",
|
|
433
|
+
default: !1
|
|
434
|
+
}).option("experimental-multi-worker", {
|
|
435
|
+
describe: "Enable experimental multi-worker support which requires JSPI and a /wordpress directory backed by a real filesystem. Pass a positive number to specify the number of workers to use. Otherwise, default to the number of CPUs minus 1.",
|
|
436
|
+
type: "number",
|
|
437
|
+
coerce: (s) => s ?? ee().length - 1
|
|
438
|
+
}).option("experimental-blueprints-v2-runner", {
|
|
439
|
+
describe: "Use the experimental Blueprint V2 runner.",
|
|
440
|
+
type: "boolean",
|
|
441
|
+
default: !1,
|
|
442
|
+
// Remove the "hidden" flag once Blueprint V2 is fully supported
|
|
443
|
+
hidden: !0
|
|
444
|
+
}).showHelpOnFail(!1).strictOptions().check(async (s) => {
|
|
445
|
+
if (s.wp !== void 0 && !me(s.wp))
|
|
446
|
+
try {
|
|
447
|
+
new URL(s.wp);
|
|
448
|
+
} catch {
|
|
449
|
+
throw new Error(
|
|
450
|
+
'Unrecognized WordPress version. Please use "latest", a URL, or a numeric version such as "6.2", "6.0.1", "6.2-beta1", or "6.2-RC1"'
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (s["experimental-multi-worker"] !== void 0) {
|
|
454
|
+
if (s["experimental-multi-worker"] <= 1)
|
|
455
|
+
throw new Error(
|
|
456
|
+
"The --experimentalMultiWorker flag must be a positive integer greater than 1."
|
|
457
|
+
);
|
|
458
|
+
const i = (n) => n.vfsPath === "/wordpress";
|
|
459
|
+
if (!s.mount?.some(i) && !s["mount-before-install"]?.some(
|
|
460
|
+
i
|
|
461
|
+
))
|
|
462
|
+
throw new Error(
|
|
463
|
+
"Please mount a real filesystem directory as the /wordpress directory before using the --experimentalMultiWorker flag. For example: --mount-dir-before-install ./empty-dir /wordpress"
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return !0;
|
|
467
|
+
});
|
|
468
|
+
t.wrap(t.terminalWidth());
|
|
469
|
+
const r = await t.argv, e = r._[0];
|
|
470
|
+
["run-blueprint", "server", "build-snapshot"].includes(e) || (t.showHelp(), process.exit(1));
|
|
471
|
+
const o = {
|
|
472
|
+
...r,
|
|
473
|
+
command: e,
|
|
474
|
+
mount: [...r.mount || [], ...r["mount-dir"] || []],
|
|
475
|
+
"mount-before-install": [
|
|
476
|
+
...r["mount-before-install"] || [],
|
|
477
|
+
...r["mount-dir-before-install"] || []
|
|
478
|
+
]
|
|
479
|
+
};
|
|
480
|
+
await We(o);
|
|
481
|
+
} catch (t) {
|
|
482
|
+
if (!(t instanceof Error))
|
|
483
|
+
throw t;
|
|
484
|
+
const r = process.argv.includes("--debug");
|
|
485
|
+
console.error(r ? t : t.message), process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async function We(t) {
|
|
489
|
+
let r, e;
|
|
490
|
+
const o = [];
|
|
491
|
+
t.autoMount && (t = Q(t)), t.quiet && (p.handlers = []);
|
|
492
|
+
const s = await import("fs-ext").then((a) => a.flockSync).catch(() => {
|
|
493
|
+
p.warn(
|
|
494
|
+
"The fs-ext package is not installed. Internal file locking will not be integrated with host OS file locking."
|
|
495
|
+
);
|
|
496
|
+
}), i = new G(s);
|
|
497
|
+
let n = !1, l = !0;
|
|
498
|
+
return p.log("Starting a PHP server..."), ce({
|
|
499
|
+
port: t.port,
|
|
500
|
+
onBind: async (a, c) => {
|
|
501
|
+
const d = `http://127.0.0.1:${c}`, w = t.experimentalMultiWorker ?? 1, b = Math.floor(
|
|
502
|
+
Number.MAX_SAFE_INTEGER / w
|
|
503
|
+
);
|
|
504
|
+
let u;
|
|
505
|
+
t["experimental-blueprints-v2-runner"] ? u = new ye(t, {
|
|
506
|
+
siteUrl: d,
|
|
507
|
+
processIdSpaceLength: b
|
|
508
|
+
}) : (u = new ve(t, {
|
|
509
|
+
siteUrl: d,
|
|
510
|
+
processIdSpaceLength: b
|
|
511
|
+
}), typeof t.blueprint == "string" && (t.blueprint = await we({
|
|
512
|
+
sourceString: t.blueprint,
|
|
513
|
+
blueprintMayReadAdjacentFiles: t["blueprint-may-read-adjacent-files"] === !0
|
|
514
|
+
})));
|
|
515
|
+
const k = Se(
|
|
516
|
+
u.getWorkerUrl(),
|
|
517
|
+
w,
|
|
518
|
+
({ exitCode: h, isMain: g, workerIndex: v }) => {
|
|
519
|
+
h !== 0 && (p.error(
|
|
520
|
+
`Worker ${v} exited with code ${h}
|
|
521
|
+
`
|
|
522
|
+
), g && t.exitOnPrimaryWorkerCrash && process.exit(1));
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
p.log(`Setting up WordPress ${t.wp}`);
|
|
526
|
+
try {
|
|
527
|
+
const [h, ...g] = await k, v = await U(
|
|
528
|
+
i
|
|
529
|
+
);
|
|
530
|
+
if (e = await u.bootPrimaryWorker(
|
|
531
|
+
h.phpPort,
|
|
532
|
+
v
|
|
533
|
+
), o.push({
|
|
534
|
+
playground: e,
|
|
535
|
+
worker: h.worker
|
|
536
|
+
}), await e.isReady(), n = !0, p.log("Booted!"), r = new fe(e), !t["experimental-blueprints-v2-runner"]) {
|
|
537
|
+
const f = await u.compileInputBlueprint(
|
|
538
|
+
t["additional-blueprint-steps"] || []
|
|
539
|
+
);
|
|
540
|
+
f && (p.log("Running the Blueprint..."), await Z(f, e), p.log("Finished running the blueprint"));
|
|
541
|
+
}
|
|
542
|
+
if (t.command === "build-snapshot" ? (await xe(e, t.outfile), p.log(`WordPress exported to ${t.outfile}`), process.exit(0)) : t.command === "run-blueprint" && (p.log("Blueprint executed"), process.exit(0)), t.experimentalMultiWorker && t.experimentalMultiWorker > 1) {
|
|
543
|
+
p.log("Preparing additional workers...");
|
|
544
|
+
const f = await M(
|
|
545
|
+
e,
|
|
546
|
+
"/internal"
|
|
547
|
+
), W = b;
|
|
548
|
+
await Promise.all(
|
|
549
|
+
g.map(async (x, C) => {
|
|
550
|
+
const E = W + C * b, H = await U(i), S = await u.bootSecondaryWorker({
|
|
551
|
+
worker: x,
|
|
552
|
+
fileLockManagerPort: H,
|
|
553
|
+
firstProcessId: E
|
|
554
|
+
});
|
|
555
|
+
o.push({
|
|
556
|
+
playground: S,
|
|
557
|
+
worker: x.worker
|
|
558
|
+
}), await S.writeFile(
|
|
559
|
+
"/tmp/internal.zip",
|
|
560
|
+
f
|
|
561
|
+
), await _(
|
|
562
|
+
S,
|
|
563
|
+
"/tmp/internal.zip",
|
|
564
|
+
"/internal"
|
|
565
|
+
), await S.unlink(
|
|
566
|
+
"/tmp/internal.zip"
|
|
567
|
+
), r.addWorker(S);
|
|
568
|
+
})
|
|
569
|
+
), p.log("Ready!");
|
|
570
|
+
}
|
|
571
|
+
return p.log(`WordPress is running on ${d}`), {
|
|
572
|
+
playground: e,
|
|
573
|
+
server: a,
|
|
574
|
+
[Symbol.asyncDispose]: async function() {
|
|
575
|
+
await Promise.all(
|
|
576
|
+
o.map(
|
|
577
|
+
async ({ playground: W, worker: x }) => {
|
|
578
|
+
await W.dispose(), await x.terminate();
|
|
579
|
+
}
|
|
580
|
+
)
|
|
581
|
+
), await new Promise((W) => a.close(W));
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
} catch (h) {
|
|
585
|
+
if (!t.debug)
|
|
586
|
+
throw h;
|
|
587
|
+
const g = await e.readFileAsText(z);
|
|
588
|
+
throw new Error(g, { cause: h });
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
async handleRequest(a) {
|
|
592
|
+
if (!n)
|
|
593
|
+
return F.forHttpCode(
|
|
594
|
+
502,
|
|
595
|
+
"WordPress is not ready yet"
|
|
596
|
+
);
|
|
597
|
+
if (l) {
|
|
598
|
+
l = !1;
|
|
599
|
+
const c = {
|
|
600
|
+
"Content-Type": ["text/plain"],
|
|
601
|
+
"Content-Length": ["0"],
|
|
602
|
+
Location: ["/"]
|
|
603
|
+
};
|
|
604
|
+
return a.headers?.cookie?.includes(
|
|
605
|
+
"playground_auto_login_already_happened"
|
|
606
|
+
) && (c["Set-Cookie"] = [
|
|
607
|
+
"playground_auto_login_already_happened=1; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/"
|
|
608
|
+
]), new F(302, c, new Uint8Array());
|
|
609
|
+
}
|
|
610
|
+
return await r.handleRequest(a);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
function Se(t, r, e) {
|
|
615
|
+
const o = new URL(t, import.meta.url), s = [];
|
|
616
|
+
for (let i = 0; i < r; i++) {
|
|
617
|
+
const n = new N(o), l = (a) => {
|
|
618
|
+
e({
|
|
619
|
+
exitCode: a,
|
|
620
|
+
isMain: i === 0,
|
|
621
|
+
workerIndex: i
|
|
622
|
+
});
|
|
623
|
+
};
|
|
624
|
+
s.push(
|
|
625
|
+
new Promise(
|
|
626
|
+
(a, c) => {
|
|
627
|
+
n.once("message", function(d) {
|
|
628
|
+
d.command === "worker-script-initialized" && a({ worker: n, phpPort: d.phpPort });
|
|
629
|
+
}), n.once("error", function(d) {
|
|
630
|
+
console.error(d);
|
|
631
|
+
const w = new Error(
|
|
632
|
+
`Worker failed to load at ${o}. ${d.message ? `Original error: ${d.message}` : ""}`
|
|
633
|
+
);
|
|
634
|
+
w.filename = o, c(w);
|
|
635
|
+
}), n.once("exit", l);
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
return Promise.all(s);
|
|
641
|
+
}
|
|
642
|
+
async function U(t) {
|
|
643
|
+
const { port1: r, port2: e } = new J();
|
|
644
|
+
return await te() ? V(t, null, r) : await q(t, r), e;
|
|
645
|
+
}
|
|
646
|
+
async function xe(t, r) {
|
|
647
|
+
await t.run({
|
|
648
|
+
code: `<?php
|
|
649
|
+
$zip = new ZipArchive();
|
|
650
|
+
if(false === $zip->open('/tmp/build.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
|
|
651
|
+
throw new Exception('Failed to create ZIP');
|
|
652
|
+
}
|
|
653
|
+
$files = new RecursiveIteratorIterator(
|
|
654
|
+
new RecursiveDirectoryIterator('/wordpress')
|
|
655
|
+
);
|
|
656
|
+
foreach ($files as $file) {
|
|
657
|
+
echo $file . PHP_EOL;
|
|
658
|
+
if (!$file->isFile()) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
$zip->addFile($file->getPathname(), $file->getPathname());
|
|
662
|
+
}
|
|
663
|
+
$zip->close();
|
|
664
|
+
|
|
665
|
+
`
|
|
666
|
+
});
|
|
667
|
+
const e = await t.readFileAsBuffer("/tmp/build.zip");
|
|
668
|
+
m.writeFileSync(r, e);
|
|
669
|
+
}
|
|
670
|
+
export {
|
|
671
|
+
Oe as p,
|
|
672
|
+
We as r
|
|
673
|
+
};
|
|
674
|
+
//# sourceMappingURL=run-cli-CY1lYIJH.js.map
|