@php-wasm/universal 0.7.1 → 0.7.3

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/index.js CHANGED
@@ -2,11 +2,11 @@ var Q = (e, t, r) => {
2
2
  if (!t.has(e))
3
3
  throw TypeError("Cannot " + r);
4
4
  };
5
- var c = (e, t, r) => (Q(e, t, "read from private field"), r ? r.call(e) : t.get(e)), d = (e, t, r) => {
5
+ var u = (e, t, r) => (Q(e, t, "read from private field"), r ? r.call(e) : t.get(e)), h = (e, t, r) => {
6
6
  if (t.has(e))
7
7
  throw TypeError("Cannot add the same private member more than once");
8
8
  t instanceof WeakSet ? t.add(e) : t.set(e, r);
9
- }, h = (e, t, r, s) => (Q(e, t, "write to private field"), s ? s.call(e, r) : t.set(e, r), r);
9
+ }, d = (e, t, r, s) => (Q(e, t, "write to private field"), s ? s.call(e, r) : t.set(e, r), r);
10
10
  var p = (e, t, r) => (Q(e, t, "access private method"), r);
11
11
  const currentJsRuntime$1 = function() {
12
12
  var e;
@@ -19,8 +19,8 @@ const currentJsRuntime$1 = function() {
19
19
  if (currentJsRuntime$1 === "NODE") {
20
20
  let e = function(r) {
21
21
  return new Promise(function(s, n) {
22
- r.onload = r.onerror = function(o) {
23
- r.onload = r.onerror = null, o.type === "load" ? s(r.result) : n(new Error("Failed to read the blob/file"));
22
+ r.onload = r.onerror = function(i) {
23
+ r.onload = r.onerror = null, i.type === "load" ? s(r.result) : n(new Error("Failed to read the blob/file"));
24
24
  };
25
25
  });
26
26
  }, t = function() {
@@ -33,10 +33,10 @@ if (currentJsRuntime$1 === "NODE") {
33
33
  };
34
34
  if (typeof File > "u") {
35
35
  class r extends Blob {
36
- constructor(n, o, i) {
36
+ constructor(n, i, o) {
37
37
  super(n);
38
38
  let a;
39
- i != null && i.lastModified && (a = /* @__PURE__ */ new Date()), (!a || isNaN(a.getFullYear())) && (a = /* @__PURE__ */ new Date()), this.lastModifiedDate = a, this.lastModified = a.getMilliseconds(), this.name = o || "";
39
+ o != null && o.lastModified && (a = /* @__PURE__ */ new Date()), (!a || isNaN(a.getFullYear())) && (a = /* @__PURE__ */ new Date()), this.lastModifiedDate = a, this.lastModified = a.getMilliseconds(), this.name = i || "";
40
40
  }
41
41
  }
42
42
  global.File = r;
@@ -56,13 +56,13 @@ if (currentJsRuntime$1 === "NODE") {
56
56
  // this if needed.
57
57
  autoAllocateChunkSize: 512 * 1024,
58
58
  async pull(n) {
59
- const o = n.byobRequest.view, a = await s.slice(
59
+ const i = n.byobRequest.view, a = await s.slice(
60
60
  r,
61
- r + o.byteLength
62
- ).arrayBuffer(), u = new Uint8Array(a);
63
- new Uint8Array(o.buffer).set(u);
64
- const l = u.byteLength;
65
- n.byobRequest.respond(l), r += l, r >= s.size && n.close();
61
+ r + i.byteLength
62
+ ).arrayBuffer(), l = new Uint8Array(a);
63
+ new Uint8Array(i.buffer).set(l);
64
+ const c = l.byteLength;
65
+ n.byobRequest.respond(c), r += c, r >= s.size && n.close();
66
66
  }
67
67
  });
68
68
  });
@@ -102,6 +102,171 @@ const ErrorEvent = typeof globalThis.ErrorEvent == "function" ? globalThis.Error
102
102
  function isExitCodeZero(e) {
103
103
  return e instanceof Error ? "exitCode" in e && (e == null ? void 0 : e.exitCode) === 0 || (e == null ? void 0 : e.name) === "ExitStatus" && "status" in e && e.status === 0 : !1;
104
104
  }
105
+ const logToConsole = (e, ...t) => {
106
+ switch (e.severity) {
107
+ case "Debug":
108
+ console.debug(e.message, ...t);
109
+ break;
110
+ case "Info":
111
+ console.info(e.message, ...t);
112
+ break;
113
+ case "Warn":
114
+ console.warn(e.message, ...t);
115
+ break;
116
+ case "Error":
117
+ console.error(e.message, ...t);
118
+ break;
119
+ case "Fatal":
120
+ console.error(e.message, ...t);
121
+ break;
122
+ default:
123
+ console.log(e.message, ...t);
124
+ }
125
+ }, prepareLogMessage = (e, ...t) => [
126
+ typeof e == "object" ? JSON.stringify(e) : e,
127
+ ...t.map((r) => JSON.stringify(r))
128
+ ].join(" "), logs = [], addToLogArray = (e) => {
129
+ logs.push(e);
130
+ }, logToMemory = (e) => {
131
+ if (e.raw === !0)
132
+ addToLogArray(e.message);
133
+ else {
134
+ const t = formatLogEntry(
135
+ typeof e.message == "object" ? prepareLogMessage(e.message) : e.message,
136
+ e.severity ?? "Info",
137
+ e.prefix ?? "JavaScript"
138
+ );
139
+ addToLogArray(t);
140
+ }
141
+ };
142
+ class Logger extends EventTarget {
143
+ // constructor
144
+ constructor(t = []) {
145
+ super(), this.handlers = t, this.fatalErrorEvent = "playground-fatal-error";
146
+ }
147
+ /**
148
+ * Get all logs.
149
+ * @returns string[]
150
+ */
151
+ getLogs() {
152
+ return this.handlers.includes(logToMemory) ? [...logs] : (this.error(`Logs aren't stored because the logToMemory handler isn't registered.
153
+ If you're using a custom logger instance, make sure to register logToMemory handler.
154
+ `), []);
155
+ }
156
+ /**
157
+ * Log message with severity.
158
+ *
159
+ * @param message any
160
+ * @param severity LogSeverity
161
+ * @param raw boolean
162
+ * @param args any
163
+ */
164
+ logMessage(t, ...r) {
165
+ for (const s of this.handlers)
166
+ s(t, ...r);
167
+ }
168
+ /**
169
+ * Log message
170
+ *
171
+ * @param message any
172
+ * @param args any
173
+ */
174
+ log(t, ...r) {
175
+ this.logMessage(
176
+ {
177
+ message: t,
178
+ severity: void 0,
179
+ prefix: "JavaScript",
180
+ raw: !1
181
+ },
182
+ ...r
183
+ );
184
+ }
185
+ /**
186
+ * Log debug message
187
+ *
188
+ * @param message any
189
+ * @param args any
190
+ */
191
+ debug(t, ...r) {
192
+ this.logMessage(
193
+ {
194
+ message: t,
195
+ severity: "Debug",
196
+ prefix: "JavaScript",
197
+ raw: !1
198
+ },
199
+ ...r
200
+ );
201
+ }
202
+ /**
203
+ * Log info message
204
+ *
205
+ * @param message any
206
+ * @param args any
207
+ */
208
+ info(t, ...r) {
209
+ this.logMessage(
210
+ {
211
+ message: t,
212
+ severity: "Info",
213
+ prefix: "JavaScript",
214
+ raw: !1
215
+ },
216
+ ...r
217
+ );
218
+ }
219
+ /**
220
+ * Log warning message
221
+ *
222
+ * @param message any
223
+ * @param args any
224
+ */
225
+ warn(t, ...r) {
226
+ this.logMessage(
227
+ {
228
+ message: t,
229
+ severity: "Warn",
230
+ prefix: "JavaScript",
231
+ raw: !1
232
+ },
233
+ ...r
234
+ );
235
+ }
236
+ /**
237
+ * Log error message
238
+ *
239
+ * @param message any
240
+ * @param args any
241
+ */
242
+ error(t, ...r) {
243
+ this.logMessage(
244
+ {
245
+ message: t,
246
+ severity: "Error",
247
+ prefix: "JavaScript",
248
+ raw: !1
249
+ },
250
+ ...r
251
+ );
252
+ }
253
+ }
254
+ const logger = new Logger([logToMemory, logToConsole]), formatLogEntry = (e, t, r) => {
255
+ const s = /* @__PURE__ */ new Date(), n = new Intl.DateTimeFormat("en-GB", {
256
+ year: "numeric",
257
+ month: "short",
258
+ day: "2-digit",
259
+ timeZone: "UTC"
260
+ }).format(s).replace(/ /g, "-"), i = new Intl.DateTimeFormat("en-GB", {
261
+ hour: "2-digit",
262
+ minute: "2-digit",
263
+ second: "2-digit",
264
+ hour12: !1,
265
+ timeZone: "UTC",
266
+ timeZoneName: "short"
267
+ }).format(s);
268
+ return `[${n + " " + i}] ${r} ${t}: ${e}`;
269
+ };
105
270
  class UnhandledRejectionsTarget extends EventTarget {
106
271
  constructor() {
107
272
  super(...arguments), this.listenersCount = 0;
@@ -125,26 +290,26 @@ function improveWASMErrorReporting(e) {
125
290
  if (typeof e.asm[r] == "function") {
126
291
  const s = e.asm[r];
127
292
  e.asm[r] = function(...n) {
128
- var o;
293
+ var i;
129
294
  try {
130
295
  return s(...n);
131
- } catch (i) {
132
- if (!(i instanceof Error))
133
- throw i;
296
+ } catch (o) {
297
+ if (!(o instanceof Error))
298
+ throw o;
134
299
  const a = clarifyErrorMessage(
135
- i,
136
- (o = e.lastAsyncifyStackSource) == null ? void 0 : o.stack
300
+ o,
301
+ (i = e.lastAsyncifyStackSource) == null ? void 0 : i.stack
137
302
  );
138
- if (e.lastAsyncifyStackSource && (i.cause = e.lastAsyncifyStackSource), t.hasListeners()) {
303
+ if (e.lastAsyncifyStackSource && (o.cause = e.lastAsyncifyStackSource), t.hasListeners()) {
139
304
  t.dispatchEvent(
140
305
  new ErrorEvent("error", {
141
- error: i,
306
+ error: o,
142
307
  message: a
143
308
  })
144
309
  );
145
310
  return;
146
311
  }
147
- throw isExitCodeZero(i) || showCriticalErrorBox(a), i;
312
+ throw isExitCodeZero(o) || showCriticalErrorBox(a), o;
148
313
  }
149
314
  };
150
315
  }
@@ -200,13 +365,13 @@ CLI option:
200
365
  let logged = !1;
201
366
  function showCriticalErrorBox(e) {
202
367
  if (!logged && (logged = !0, !(e != null && e.trim().startsWith("Program terminated with exit")))) {
203
- console.log(`${redBg}
368
+ logger.log(`${redBg}
204
369
  ${eol}
205
370
  ${bold} WASM ERROR${reset}${redBg}`);
206
371
  for (const t of e.split(`
207
372
  `))
208
- console.log(`${eol} ${t} `);
209
- console.log(`${reset}`);
373
+ logger.log(`${eol} ${t} `);
374
+ logger.log(`${reset}`);
210
375
  }
211
376
  }
212
377
  function extractPHPFunctionsFromStack(e) {
@@ -226,6 +391,29 @@ function extractPHPFunctionsFromStack(e) {
226
391
  return [];
227
392
  }
228
393
  }
394
+ class HttpCookieStore {
395
+ constructor() {
396
+ this.cookies = {};
397
+ }
398
+ rememberCookiesFromResponseHeaders(t) {
399
+ if (t != null && t["set-cookie"])
400
+ for (const r of t["set-cookie"])
401
+ try {
402
+ if (!r.includes("="))
403
+ continue;
404
+ const s = r.indexOf("="), n = r.substring(0, s), i = r.substring(s + 1).split(";")[0];
405
+ this.cookies[n] = i;
406
+ } catch (s) {
407
+ logger.error(s);
408
+ }
409
+ }
410
+ getCookieRequestHeader() {
411
+ const t = [];
412
+ for (const r in this.cookies)
413
+ t.push(`${r}=${this.cookies[r]}`);
414
+ return t.join("; ");
415
+ }
416
+ }
229
417
  const SleepFinished = Symbol("SleepFinished");
230
418
  function sleep(e) {
231
419
  return new Promise((t) => {
@@ -308,28 +496,33 @@ function normalizePathsArray(e, t) {
308
496
  }
309
497
  function splitShellCommand(e) {
310
498
  let s = 0, n = "";
311
- const o = [];
312
- let i = "";
499
+ const i = [];
500
+ let o = "";
313
501
  for (let a = 0; a < e.length; a++) {
314
- const u = e[a];
315
- u === "\\" ? ((e[a + 1] === '"' || e[a + 1] === "'") && a++, i += e[a]) : s === 0 ? u === '"' || u === "'" ? (s = 1, n = u) : u.match(/\s/) ? (i.trim().length && o.push(i.trim()), i = u) : o.length && !i ? i = o.pop() + u : i += u : s === 1 && (u === n ? (s = 0, n = "") : i += u);
502
+ const l = e[a];
503
+ l === "\\" ? ((e[a + 1] === '"' || e[a + 1] === "'") && a++, o += e[a]) : s === 0 ? l === '"' || l === "'" ? (s = 1, n = l) : l.match(/\s/) ? (o.trim().length && i.push(o.trim()), o = l) : i.length && !o ? o = i.pop() + l : o += l : s === 1 && (l === n ? (s = 0, n = "") : o += l);
316
504
  }
317
- return i && o.push(i.trim()), o;
505
+ return o && i.push(o.trim()), i;
318
506
  }
319
507
  function createSpawnHandler(e) {
320
508
  return function(t, r = [], s = {}) {
321
- const n = new ChildProcess(), o = new ProcessApi(n);
509
+ const n = new ChildProcess(), i = new ProcessApi(n);
322
510
  return setTimeout(async () => {
323
- let i = [];
511
+ let o = [];
324
512
  if (r.length)
325
- i = [t, ...r];
513
+ o = [t, ...r];
326
514
  else if (typeof t == "string")
327
- i = splitShellCommand(t);
515
+ o = splitShellCommand(t);
328
516
  else if (Array.isArray(t))
329
- i = t;
517
+ o = t;
330
518
  else
331
519
  throw new Error("Invalid command ", t);
332
- await e(i, o, s), n.emit("spawn", !0);
520
+ try {
521
+ await e(o, i, s);
522
+ } catch (a) {
523
+ n.emit("error", a), typeof a == "object" && a !== null && "message" in a && typeof a.message == "string" && i.stderr(a.message), i.exit(1);
524
+ }
525
+ n.emit("spawn", !0);
333
526
  }), n;
334
527
  };
335
528
  }
@@ -430,14 +623,14 @@ function limitBytes(e, t) {
430
623
  let s = 0;
431
624
  return new ReadableStream({
432
625
  async pull(n) {
433
- const { value: o, done: i } = await r.read(
626
+ const { value: i, done: o } = await r.read(
434
627
  new Uint8Array(t - s)
435
628
  );
436
- if (i) {
629
+ if (o) {
437
630
  r.releaseLock(), n.close();
438
631
  return;
439
632
  }
440
- s += o.length, n.enqueue(o), s >= t && (r.releaseLock(), n.close());
633
+ s += i.length, n.enqueue(i), s >= t && (r.releaseLock(), n.close());
441
634
  },
442
635
  cancel() {
443
636
  r.cancel();
@@ -519,22 +712,22 @@ async function* iteratePhpFiles(e, t, {
519
712
  exceptPaths: n = []
520
713
  } = {}) {
521
714
  t = normalizePath(t);
522
- const o = [t];
523
- for (; o.length; ) {
524
- const i = o.pop();
525
- if (!i)
715
+ const i = [t];
716
+ for (; i.length; ) {
717
+ const o = i.pop();
718
+ if (!o)
526
719
  return;
527
- const a = await e.listFiles(i);
528
- for (const u of a) {
529
- const l = `${i}/${u}`;
530
- if (n.includes(l.substring(t.length + 1)))
720
+ const a = await e.listFiles(o);
721
+ for (const l of a) {
722
+ const c = `${o}/${l}`;
723
+ if (n.includes(c.substring(t.length + 1)))
531
724
  continue;
532
- await e.isDir(l) ? o.push(l) : yield new StreamedFile(
533
- streamReadFileFromPHP(e, l),
725
+ await e.isDir(c) ? i.push(c) : yield new StreamedFile(
726
+ streamReadFileFromPHP(e, c),
534
727
  r ? joinPaths(
535
728
  s || "",
536
- l.substring(t.length + 1)
537
- ) : l
729
+ c.substring(t.length + 1)
730
+ ) : c
538
731
  );
539
732
  }
540
733
  }
@@ -550,9 +743,145 @@ function writeFilesStreamToPhp(e, t) {
550
743
  }
551
744
  });
552
745
  }
746
+ class MaxPhpInstancesError extends Error {
747
+ constructor(t) {
748
+ super(
749
+ `Requested more concurrent PHP instances than the limit (${t}).`
750
+ ), this.name = this.constructor.name;
751
+ }
752
+ }
753
+ class PHPProcessManager {
754
+ constructor(t) {
755
+ this.primaryIdle = !0, this.nextInstance = null, this.allInstances = [], this.maxPhpInstances = (t == null ? void 0 : t.maxPhpInstances) ?? 5, this.phpFactory = t == null ? void 0 : t.phpFactory, this.primaryPhp = t == null ? void 0 : t.primaryPhp, this.semaphore = new Semaphore({
756
+ concurrency: this.maxPhpInstances,
757
+ /**
758
+ * Wait up to 5 seconds for resources to become available
759
+ * before assuming that all the PHP instances are deadlocked.
760
+ */
761
+ timeout: (t == null ? void 0 : t.timeout) || 5e3
762
+ });
763
+ }
764
+ /**
765
+ * Get the primary PHP instance.
766
+ *
767
+ * If the primary PHP instance is not set, it will be spawned
768
+ * using the provided phpFactory.
769
+ *
770
+ * @throws {Error} when called twice before the first call is resolved.
771
+ */
772
+ async getPrimaryPhp() {
773
+ if (!this.phpFactory && !this.primaryPhp)
774
+ throw new Error(
775
+ "phpFactory or primaryPhp must be set before calling getPrimaryPhp()."
776
+ );
777
+ if (!this.primaryPhp) {
778
+ const t = await this.spawn({ isPrimary: !0 });
779
+ this.primaryPhp = t.php;
780
+ }
781
+ return this.primaryPhp;
782
+ }
783
+ /**
784
+ * Get a PHP instance.
785
+ *
786
+ * It could be either the primary PHP instance, an idle disposable PHP instance,
787
+ * or a newly spawned PHP instance – depending on the resource availability.
788
+ *
789
+ * @throws {MaxPhpInstancesError} when the maximum number of PHP instances is reached
790
+ * and the waiting timeout is exceeded.
791
+ */
792
+ async acquirePHPInstance() {
793
+ if (this.primaryIdle)
794
+ return this.primaryIdle = !1, {
795
+ php: await this.getPrimaryPhp(),
796
+ reap: () => this.primaryIdle = !0
797
+ };
798
+ const t = this.nextInstance || this.spawn({ isPrimary: !1 });
799
+ return this.semaphore.remaining > 0 ? this.nextInstance = this.spawn({ isPrimary: !1 }) : this.nextInstance = null, await t;
800
+ }
801
+ /**
802
+ * Initiated spawning of a new PHP instance.
803
+ * This function is synchronous on purpose – it needs to synchronously
804
+ * add the spawn promise to the allInstances array without waiting
805
+ * for PHP to spawn.
806
+ */
807
+ spawn(t) {
808
+ if (t.isPrimary && this.allInstances.length > 0)
809
+ throw new Error(
810
+ "Requested spawning a primary PHP instance when another primary instance already started spawning."
811
+ );
812
+ const r = this.doSpawn(t);
813
+ this.allInstances.push(r);
814
+ const s = () => {
815
+ this.allInstances = this.allInstances.filter(
816
+ (n) => n !== r
817
+ );
818
+ };
819
+ return r.catch((n) => {
820
+ throw s(), n;
821
+ }).then((n) => ({
822
+ ...n,
823
+ reap: () => {
824
+ s(), n.reap();
825
+ }
826
+ }));
827
+ }
828
+ /**
829
+ * Actually acquires the lock and spawns a new PHP instance.
830
+ */
831
+ async doSpawn(t) {
832
+ let r;
833
+ try {
834
+ r = await this.semaphore.acquire();
835
+ } catch (s) {
836
+ throw s instanceof AcquireTimeoutError ? new MaxPhpInstancesError(this.maxPhpInstances) : s;
837
+ }
838
+ try {
839
+ const s = await this.phpFactory(t);
840
+ return {
841
+ php: s,
842
+ reap() {
843
+ s.exit(), r();
844
+ }
845
+ };
846
+ } catch (s) {
847
+ throw r(), s;
848
+ }
849
+ }
850
+ async [Symbol.asyncDispose]() {
851
+ this.primaryPhp && this.primaryPhp.exit(), await Promise.all(
852
+ this.allInstances.map(
853
+ (t) => t.then(({ reap: r }) => r())
854
+ )
855
+ );
856
+ }
857
+ }
858
+ const responseTexts = {
859
+ 500: "Internal Server Error",
860
+ 502: "Bad Gateway",
861
+ 404: "Not Found",
862
+ 403: "Forbidden",
863
+ 401: "Unauthorized",
864
+ 400: "Bad Request",
865
+ 301: "Moved Permanently",
866
+ 302: "Found",
867
+ 307: "Temporary Redirect",
868
+ 308: "Permanent Redirect",
869
+ 204: "No Content",
870
+ 201: "Created",
871
+ 200: "OK"
872
+ };
553
873
  class PHPResponse {
554
- constructor(t, r, s, n = "", o = 0) {
555
- this.httpStatusCode = t, this.headers = r, this.bytes = s, this.exitCode = o, this.errors = n;
874
+ constructor(t, r, s, n = "", i = 0) {
875
+ this.httpStatusCode = t, this.headers = r, this.bytes = s, this.exitCode = i, this.errors = n;
876
+ }
877
+ static forHttpCode(t, r = "") {
878
+ return new PHPResponse(
879
+ t,
880
+ {},
881
+ new TextEncoder().encode(
882
+ r || responseTexts[t] || ""
883
+ )
884
+ );
556
885
  }
557
886
  static fromRawData(t) {
558
887
  return new PHPResponse(
@@ -603,726 +932,327 @@ const SupportedPHPVersions = [
603
932
  ], SupportedPHPExtensionBundles = {
604
933
  "kitchen-sink": SupportedPHPExtensionsList,
605
934
  light: []
606
- }, DEFAULT_BASE_URL = "http://example.com";
607
- function toRelativeUrl(e) {
608
- return e.toString().substring(e.origin.length);
609
- }
610
- function removePathPrefix(e, t) {
611
- return !t || !e.startsWith(t) ? e : e.substring(t.length);
612
- }
613
- function ensurePathPrefix(e, t) {
614
- return !t || e.startsWith(t) ? e : t + e;
935
+ }, FileErrorCodes = {
936
+ 0: "No error occurred. System call completed successfully.",
937
+ 1: "Argument list too long.",
938
+ 2: "Permission denied.",
939
+ 3: "Address in use.",
940
+ 4: "Address not available.",
941
+ 5: "Address family not supported.",
942
+ 6: "Resource unavailable, or operation would block.",
943
+ 7: "Connection already in progress.",
944
+ 8: "Bad file descriptor.",
945
+ 9: "Bad message.",
946
+ 10: "Device or resource busy.",
947
+ 11: "Operation canceled.",
948
+ 12: "No child processes.",
949
+ 13: "Connection aborted.",
950
+ 14: "Connection refused.",
951
+ 15: "Connection reset.",
952
+ 16: "Resource deadlock would occur.",
953
+ 17: "Destination address required.",
954
+ 18: "Mathematics argument out of domain of function.",
955
+ 19: "Reserved.",
956
+ 20: "File exists.",
957
+ 21: "Bad address.",
958
+ 22: "File too large.",
959
+ 23: "Host is unreachable.",
960
+ 24: "Identifier removed.",
961
+ 25: "Illegal byte sequence.",
962
+ 26: "Operation in progress.",
963
+ 27: "Interrupted function.",
964
+ 28: "Invalid argument.",
965
+ 29: "I/O error.",
966
+ 30: "Socket is connected.",
967
+ 31: "There is a directory under that path.",
968
+ 32: "Too many levels of symbolic links.",
969
+ 33: "File descriptor value too large.",
970
+ 34: "Too many links.",
971
+ 35: "Message too large.",
972
+ 36: "Reserved.",
973
+ 37: "Filename too long.",
974
+ 38: "Network is down.",
975
+ 39: "Connection aborted by network.",
976
+ 40: "Network unreachable.",
977
+ 41: "Too many files open in system.",
978
+ 42: "No buffer space available.",
979
+ 43: "No such device.",
980
+ 44: "There is no such file or directory OR the parent directory does not exist.",
981
+ 45: "Executable file format error.",
982
+ 46: "No locks available.",
983
+ 47: "Reserved.",
984
+ 48: "Not enough space.",
985
+ 49: "No message of the desired type.",
986
+ 50: "Protocol not available.",
987
+ 51: "No space left on device.",
988
+ 52: "Function not supported.",
989
+ 53: "The socket is not connected.",
990
+ 54: "Not a directory or a symbolic link to a directory.",
991
+ 55: "Directory not empty.",
992
+ 56: "State not recoverable.",
993
+ 57: "Not a socket.",
994
+ 58: "Not supported, or operation not supported on socket.",
995
+ 59: "Inappropriate I/O control operation.",
996
+ 60: "No such device or address.",
997
+ 61: "Value too large to be stored in data type.",
998
+ 62: "Previous owner died.",
999
+ 63: "Operation not permitted.",
1000
+ 64: "Broken pipe.",
1001
+ 65: "Protocol error.",
1002
+ 66: "Protocol not supported.",
1003
+ 67: "Protocol wrong type for socket.",
1004
+ 68: "Result too large.",
1005
+ 69: "Read-only file system.",
1006
+ 70: "Invalid seek.",
1007
+ 71: "No such process.",
1008
+ 72: "Reserved.",
1009
+ 73: "Connection timed out.",
1010
+ 74: "Text file busy.",
1011
+ 75: "Cross-device link.",
1012
+ 76: "Extension: Capabilities insufficient."
1013
+ };
1014
+ function getEmscriptenFsError(e) {
1015
+ const t = typeof e == "object" ? e == null ? void 0 : e.errno : null;
1016
+ if (t in FileErrorCodes)
1017
+ return FileErrorCodes[t];
615
1018
  }
616
- async function encodeAsMultipart(e) {
617
- const t = `----${Math.random().toString(36).slice(2)}`, r = `multipart/form-data; boundary=${t}`, s = new TextEncoder(), n = [];
618
- for (const [u, l] of Object.entries(e))
619
- n.push(`--${t}\r
620
- `), n.push(`Content-Disposition: form-data; name="${u}"`), l instanceof File && n.push(`; filename="${l.name}"`), n.push(`\r
621
- `), l instanceof File && (n.push("Content-Type: application/octet-stream"), n.push(`\r
622
- `)), n.push(`\r
623
- `), l instanceof File ? n.push(await fileToUint8Array(l)) : n.push(l), n.push(`\r
624
- `);
625
- n.push(`--${t}--\r
626
- `);
627
- const o = n.reduce((u, l) => u + l.length, 0), i = new Uint8Array(o);
628
- let a = 0;
629
- for (const u of n)
630
- i.set(
631
- typeof u == "string" ? s.encode(u) : u,
632
- a
633
- ), a += u.length;
634
- return { bytes: i, contentType: r };
1019
+ function rethrowFileSystemError(e = "") {
1020
+ return function(r, s, n) {
1021
+ const i = n.value;
1022
+ n.value = function(...o) {
1023
+ try {
1024
+ return i.apply(this, o);
1025
+ } catch (a) {
1026
+ const l = typeof a == "object" ? a == null ? void 0 : a.errno : null;
1027
+ if (l in FileErrorCodes) {
1028
+ const c = FileErrorCodes[l], g = typeof o[0] == "string" ? o[0] : null, me = g !== null ? e.replaceAll("{path}", g) : e;
1029
+ throw new Error(`${me}: ${c}`, {
1030
+ cause: a
1031
+ });
1032
+ }
1033
+ throw a;
1034
+ }
1035
+ };
1036
+ };
635
1037
  }
636
- function fileToUint8Array(e) {
637
- return new Promise((t) => {
638
- const r = new FileReader();
639
- r.onload = () => {
640
- t(new Uint8Array(r.result));
641
- }, r.readAsArrayBuffer(e);
1038
+ const RuntimeId = Symbol("RuntimeId"), loadedRuntimes = /* @__PURE__ */ new Map();
1039
+ let lastRuntimeId = 0;
1040
+ async function loadPHPRuntime(e, t = {}) {
1041
+ const [r, s, n] = makePromise(), i = e.init(currentJsRuntime, {
1042
+ onAbort(a) {
1043
+ n(a), logger.error(a);
1044
+ },
1045
+ ENV: {},
1046
+ // Emscripten sometimes prepends a '/' to the path, which
1047
+ // breaks vite dev mode. An identity `locateFile` function
1048
+ // fixes it.
1049
+ locateFile: (a) => a,
1050
+ ...t,
1051
+ noInitialRun: !0,
1052
+ onRuntimeInitialized() {
1053
+ t.onRuntimeInitialized && t.onRuntimeInitialized(), s();
1054
+ }
642
1055
  });
1056
+ await r;
1057
+ const o = ++lastRuntimeId;
1058
+ return i.id = o, i.originalExit = i._exit, i._exit = function(a) {
1059
+ return loadedRuntimes.delete(o), i.originalExit(a);
1060
+ }, i[RuntimeId] = o, loadedRuntimes.set(o, i), o;
643
1061
  }
644
- class HttpCookieStore {
645
- constructor() {
646
- this.cookies = {};
647
- }
648
- rememberCookiesFromResponseHeaders(t) {
649
- if (t != null && t["set-cookie"])
650
- for (const r of t["set-cookie"])
651
- try {
652
- if (!r.includes("="))
653
- continue;
654
- const s = r.indexOf("="), n = r.substring(0, s), o = r.substring(s + 1).split(";")[0];
655
- this.cookies[n] = o;
656
- } catch (s) {
657
- console.error(s);
658
- }
659
- }
660
- getCookieRequestHeader() {
661
- const t = [];
662
- for (const r in this.cookies)
663
- t.push(`${r}=${this.cookies[r]}`);
664
- return t.join("; ");
1062
+ function getLoadedRuntime(e) {
1063
+ return loadedRuntimes.get(e);
1064
+ }
1065
+ const currentJsRuntime = function() {
1066
+ var e;
1067
+ return typeof process < "u" && ((e = process.release) == null ? void 0 : e.name) === "node" ? "NODE" : typeof window < "u" ? "WEB" : typeof WorkerGlobalScope < "u" && self instanceof WorkerGlobalScope ? "WORKER" : "NODE";
1068
+ }(), makePromise = () => {
1069
+ const e = [], t = new Promise((r, s) => {
1070
+ e.push(r, s);
1071
+ });
1072
+ return e.unshift(t), e;
1073
+ };
1074
+ var __defProp = Object.defineProperty, __getOwnPropDesc = Object.getOwnPropertyDescriptor, __decorateClass = (e, t, r, s) => {
1075
+ for (var n = s > 1 ? void 0 : s ? __getOwnPropDesc(t, r) : t, i = e.length - 1, o; i >= 0; i--)
1076
+ (o = e[i]) && (n = (s ? o(t, r, n) : o(n)) || n);
1077
+ return s && n && __defProp(t, r, n), n;
1078
+ };
1079
+ const STRING = "string", NUMBER = "number", __private__dont__use = Symbol("__private__dont__use");
1080
+ class PHPExecutionFailureError extends Error {
1081
+ constructor(t, r, s) {
1082
+ super(t), this.response = r, this.source = s;
665
1083
  }
666
1084
  }
667
- var m, v, C, g, S, _, b, E, x, A, Y, k, K, B, Z;
668
- class PHPRequestHandler {
1085
+ var w, v, R, f, P, _, S, C, Z, I, Y, A, K, k, X, M, ee, B, te, L, re, N, se, U, ne, O, ie, D, oe, $, ae, q, le, j, ce, z, ue;
1086
+ class BasePHP {
669
1087
  /**
670
- * @param php - The PHP instance.
671
- * @param config - Request Handler configuration.
1088
+ * Initializes a PHP runtime.
1089
+ *
1090
+ * @internal
1091
+ * @param PHPRuntime - Optional. PHP Runtime ID as initialized by loadPHPRuntime.
1092
+ * @param requestHandlerOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
672
1093
  */
673
- constructor(t, r = {}) {
674
- /**
675
- * Serves a static file from the PHP filesystem.
676
- *
677
- * @param fsPath - Absolute path of the static file to serve.
678
- * @returns The response.
679
- */
680
- d(this, A);
681
- /**
682
- * Runs the requested PHP file with all the request and $_SERVER
683
- * superglobals populated.
684
- *
685
- * @param request - The request.
686
- * @returns The response.
687
- */
688
- d(this, k);
1094
+ constructor(e) {
689
1095
  /**
690
- * Resolve the requested path to the filesystem path of the requested PHP file.
691
- *
692
- * Fall back to index.php as if there was a url rewriting rule in place.
1096
+ * Prepares the $_SERVER entries for the PHP runtime.
693
1097
  *
694
- * @param requestedPath - The requested pathname.
695
- * @throws {Error} If the requested path doesn't exist.
696
- * @returns The resolved filesystem path.
1098
+ * @param defaults Default entries to include in $_SERVER.
1099
+ * @param headers HTTP headers to include in $_SERVER (as HTTP_ prefixed entries).
1100
+ * @param port HTTP port, used to determine infer $_SERVER['HTTPS'] value if none
1101
+ * was provided.
1102
+ * @returns Computed $_SERVER entries.
697
1103
  */
698
- d(this, B);
699
- d(this, m, void 0);
700
- d(this, v, void 0);
701
- d(this, C, void 0);
702
- d(this, g, void 0);
703
- d(this, S, void 0);
704
- d(this, _, void 0);
705
- d(this, b, void 0);
706
- d(this, E, void 0);
707
- d(this, x, void 0);
708
- h(this, E, new Semaphore({ concurrency: 1 }));
709
- const {
710
- documentRoot: s = "/www/",
711
- absoluteUrl: n = typeof location == "object" ? location == null ? void 0 : location.href : "",
712
- rewriteRules: o = []
713
- } = r;
714
- this.php = t, h(this, x, new HttpCookieStore()), h(this, m, s);
715
- const i = new URL(n);
716
- h(this, C, i.hostname), h(this, g, i.port ? Number(i.port) : i.protocol === "https:" ? 443 : 80), h(this, v, (i.protocol || "").replace(":", ""));
717
- const a = c(this, g) !== 443 && c(this, g) !== 80;
718
- h(this, S, [
719
- c(this, C),
720
- a ? `:${c(this, g)}` : ""
721
- ].join("")), h(this, _, i.pathname.replace(/\/+$/, "")), h(this, b, [
722
- `${c(this, v)}://`,
723
- c(this, S),
724
- c(this, _)
725
- ].join("")), this.rewriteRules = o;
1104
+ h(this, C);
1105
+ h(this, I);
1106
+ h(this, A);
1107
+ h(this, k);
1108
+ h(this, M);
1109
+ h(this, B);
1110
+ h(this, L);
1111
+ h(this, N);
1112
+ h(this, U);
1113
+ h(this, O);
1114
+ h(this, D);
1115
+ h(this, $);
1116
+ h(this, q);
1117
+ h(this, j);
1118
+ h(this, z);
1119
+ h(this, w, void 0);
1120
+ h(this, v, void 0);
1121
+ h(this, R, void 0);
1122
+ h(this, f, void 0);
1123
+ h(this, P, void 0);
1124
+ h(this, _, void 0);
1125
+ h(this, S, void 0);
1126
+ d(this, w, []), d(this, f, !1), d(this, P, null), d(this, _, /* @__PURE__ */ new Map()), d(this, S, []), this.semaphore = new Semaphore({ concurrency: 1 }), e !== void 0 && this.initializeRuntime(e);
726
1127
  }
727
- /**
728
- * Converts a path to an absolute URL based at the PHPRequestHandler
729
- * root.
730
- *
731
- * @param path The server path to convert to an absolute URL.
732
- * @returns The absolute URL.
733
- */
734
- pathToInternalUrl(t) {
735
- return `${this.absoluteUrl}${t}`;
1128
+ addEventListener(e, t) {
1129
+ u(this, _).has(e) || u(this, _).set(e, /* @__PURE__ */ new Set()), u(this, _).get(e).add(t);
736
1130
  }
737
- /**
738
- * Converts an absolute URL based at the PHPRequestHandler to a relative path
739
- * without the server pathname and scope.
740
- *
741
- * @param internalUrl An absolute URL based at the PHPRequestHandler root.
742
- * @returns The relative path.
743
- */
744
- internalUrlToPath(t) {
745
- const r = new URL(t);
746
- return r.pathname.startsWith(c(this, _)) && (r.pathname = r.pathname.slice(c(this, _).length)), toRelativeUrl(r);
1131
+ removeEventListener(e, t) {
1132
+ var r;
1133
+ (r = u(this, _).get(e)) == null || r.delete(t);
747
1134
  }
748
- get isRequestRunning() {
749
- return c(this, E).running > 0;
1135
+ dispatchEvent(e) {
1136
+ const t = u(this, _).get(e.type);
1137
+ if (t)
1138
+ for (const r of t)
1139
+ r(e);
750
1140
  }
751
- /**
752
- * The absolute URL of this PHPRequestHandler instance.
753
- */
1141
+ /** @inheritDoc */
1142
+ async onMessage(e) {
1143
+ u(this, S).push(e);
1144
+ }
1145
+ /** @inheritDoc */
1146
+ async setSpawnHandler(handler) {
1147
+ typeof handler == "string" && (handler = createSpawnHandler(eval(handler))), this[__private__dont__use].spawnProcess = handler;
1148
+ }
1149
+ /** @inheritDoc */
754
1150
  get absoluteUrl() {
755
- return c(this, b);
1151
+ return this.requestHandler.absoluteUrl;
756
1152
  }
757
- /**
758
- * The directory in the PHP filesystem where the server will look
759
- * for the files to serve. Default: `/var/www`.
760
- */
1153
+ /** @inheritDoc */
761
1154
  get documentRoot() {
762
- return c(this, m);
1155
+ return this.requestHandler.documentRoot;
1156
+ }
1157
+ /** @inheritDoc */
1158
+ pathToInternalUrl(e) {
1159
+ return this.requestHandler.pathToInternalUrl(e);
1160
+ }
1161
+ /** @inheritDoc */
1162
+ internalUrlToPath(e) {
1163
+ return this.requestHandler.internalUrlToPath(e);
1164
+ }
1165
+ initializeRuntime(e) {
1166
+ if (this[__private__dont__use])
1167
+ throw new Error("PHP runtime already initialized.");
1168
+ const t = getLoadedRuntime(e);
1169
+ if (!t)
1170
+ throw new Error("Invalid PHP runtime id.");
1171
+ this[__private__dont__use] = t, t.onMessage = async (r) => {
1172
+ for (const s of u(this, S)) {
1173
+ const n = await s(r);
1174
+ if (n)
1175
+ return n;
1176
+ }
1177
+ return "";
1178
+ }, d(this, P, improveWASMErrorReporting(t)), this.dispatchEvent({
1179
+ type: "runtime.initialized"
1180
+ });
1181
+ }
1182
+ /** @inheritDoc */
1183
+ async setSapiName(e) {
1184
+ if (this[__private__dont__use].ccall(
1185
+ "wasm_set_sapi_name",
1186
+ NUMBER,
1187
+ [STRING],
1188
+ [e]
1189
+ ) !== 0)
1190
+ throw new Error(
1191
+ "Could not set SAPI name. This can only be done before the PHP WASM module is initialized.Did you already dispatch any requests?"
1192
+ );
1193
+ d(this, R, e);
1194
+ }
1195
+ /** @inheritDoc */
1196
+ setPhpIniPath(e) {
1197
+ if (u(this, f))
1198
+ throw new Error("Cannot set PHP ini path after calling run().");
1199
+ d(this, v, e), this[__private__dont__use].ccall(
1200
+ "wasm_set_phpini_path",
1201
+ null,
1202
+ ["string"],
1203
+ [e]
1204
+ );
1205
+ }
1206
+ /** @inheritDoc */
1207
+ setPhpIniEntry(e, t) {
1208
+ if (u(this, f))
1209
+ throw new Error("Cannot set PHP ini entries after calling run().");
1210
+ u(this, w).push([e, t]);
1211
+ }
1212
+ /** @inheritDoc */
1213
+ chdir(e) {
1214
+ this[__private__dont__use].FS.chdir(e);
763
1215
  }
764
1216
  /**
765
- * Serves the request either by serving a static file, or by
766
- * dispatching it to the PHP runtime.
767
- *
768
- * The request() method mode behaves like a web server and only works if
769
- * the PHP was initialized with a `requestHandler` option (which the online version
770
- * of WordPress Playground does by default).
771
- *
772
- * In the request mode, you pass an object containing the request information
773
- * (method, headers, body, etc.) and the path to the PHP file to run:
774
- *
775
- * ```ts
776
- * const php = PHP.load('7.4', {
777
- * requestHandler: {
778
- * documentRoot: "/www"
779
- * }
780
- * })
781
- * php.writeFile("/www/index.php", `<?php echo file_get_contents("php://input");`);
782
- * const result = await php.request({
783
- * method: "GET",
784
- * headers: {
785
- * "Content-Type": "text/plain"
786
- * },
787
- * body: "Hello world!",
788
- * path: "/www/index.php"
789
- * });
790
- * // result.text === "Hello world!"
791
- * ```
792
- *
793
- * The `request()` method cannot be used in conjunction with `cli()`.
794
- *
795
- * @example
796
- * ```js
797
- * const output = await php.request({
798
- * method: 'GET',
799
- * url: '/index.php',
800
- * headers: {
801
- * 'X-foo': 'bar',
802
- * },
803
- * body: {
804
- * foo: 'bar',
805
- * },
806
- * });
807
- * console.log(output.stdout); // "Hello world!"
808
- * ```
809
- *
810
- * @param request - PHP Request data.
1217
+ * Do not use. Use new PHPRequestHandler() instead.
1218
+ * @deprecated
811
1219
  */
812
- async request(t) {
813
- const r = t.url.startsWith("http://") || t.url.startsWith("https://"), s = new URL(
814
- // Remove the hash part of the URL as it's not meant for the server.
815
- t.url.split("#")[0],
816
- r ? void 0 : DEFAULT_BASE_URL
817
- ), n = applyRewriteRules(
818
- removePathPrefix(
819
- decodeURIComponent(s.pathname),
820
- c(this, _)
821
- ),
822
- this.rewriteRules
823
- ), o = joinPaths(c(this, m), n);
824
- return seemsLikeAPHPRequestHandlerPath(o) ? await p(this, k, K).call(this, t, s) : p(this, A, Y).call(this, o);
1220
+ async request(e) {
1221
+ if (logger.warn(
1222
+ "PHP.request() is deprecated. Please use new PHPRequestHandler() instead."
1223
+ ), !this.requestHandler)
1224
+ throw new Error("No request handler available.");
1225
+ return this.requestHandler.request(e);
825
1226
  }
826
- }
827
- m = new WeakMap(), v = new WeakMap(), C = new WeakMap(), g = new WeakMap(), S = new WeakMap(), _ = new WeakMap(), b = new WeakMap(), E = new WeakMap(), x = new WeakMap(), A = new WeakSet(), Y = function(t) {
828
- if (!this.php.fileExists(t))
829
- return new PHPResponse(
830
- 404,
831
- // Let the service worker know that no static file was found
832
- // and that it's okay to issue a real fetch() to the server.
833
- {
834
- "x-file-type": ["static"]
835
- },
836
- new TextEncoder().encode("404 File not found")
837
- );
838
- const r = this.php.readFileAsBuffer(t);
839
- return new PHPResponse(
840
- 200,
841
- {
842
- "content-length": [`${r.byteLength}`],
843
- // @TODO: Infer the content-type from the arrayBuffer instead of the file path.
844
- // The code below won't return the correct mime-type if the extension
845
- // was tampered with.
846
- "content-type": [inferMimeType(t)],
847
- "accept-ranges": ["bytes"],
848
- "cache-control": ["public, max-age=0"]
849
- },
850
- r
851
- );
852
- }, k = new WeakSet(), K = async function(t, r) {
853
- var n;
854
- if (c(this, E).running > 0 && ((n = t.headers) == null ? void 0 : n["x-request-issuer"]) === "php")
855
- return console.warn(
856
- "Possible deadlock: Called request() before the previous request() have finished. PHP likely issued an HTTP call to itself. Normally this would lead to infinite waiting as Request 1 holds the lock that the Request 2 is waiting to acquire. That's not useful, so PHPRequestHandler will return error 502 instead."
857
- ), new PHPResponse(
858
- 502,
859
- {},
860
- new TextEncoder().encode("502 Bad Gateway")
861
- );
862
- const s = await c(this, E).acquire();
863
- try {
864
- let o = "GET";
865
- const i = {
866
- host: c(this, S),
867
- ...normalizeHeaders(t.headers || {}),
868
- cookie: c(this, x).getCookieRequestHeader()
869
- };
870
- let a = t.body;
871
- if (typeof a == "object" && !(a instanceof Uint8Array)) {
872
- o = "POST";
873
- const { bytes: l, contentType: f } = await encodeAsMultipart(a);
874
- a = l, i["content-type"] = f;
875
- }
876
- let u;
1227
+ /** @inheritDoc */
1228
+ async run(e) {
1229
+ const t = await this.semaphore.acquire();
1230
+ let r;
877
1231
  try {
878
- u = p(this, B, Z).call(this, decodeURIComponent(r.pathname));
879
- } catch {
880
- return new PHPResponse(
881
- 404,
882
- {},
883
- new TextEncoder().encode("404 File not found")
884
- );
885
- }
886
- try {
887
- const l = await this.php.run({
888
- relativeUri: ensurePathPrefix(
889
- toRelativeUrl(r),
890
- c(this, _)
891
- ),
892
- protocol: c(this, v),
893
- method: t.method || o,
894
- $_SERVER: {
895
- REMOTE_ADDR: "127.0.0.1",
896
- DOCUMENT_ROOT: c(this, m),
897
- HTTPS: c(this, b).startsWith("https://") ? "on" : ""
898
- },
899
- body: a,
900
- scriptPath: u,
901
- headers: i
902
- });
903
- return c(this, x).rememberCookiesFromResponseHeaders(
904
- l.headers
905
- ), l;
906
- } catch (l) {
907
- const f = l;
908
- if (f != null && f.response)
909
- return f.response;
910
- throw l;
911
- }
912
- } finally {
913
- s();
914
- }
915
- }, B = new WeakSet(), Z = function(t) {
916
- let r = removePathPrefix(t, c(this, _));
917
- r = applyRewriteRules(r, this.rewriteRules), r.includes(".php") ? r = r.split(".php")[0] + ".php" : this.php.isDir(`${c(this, m)}${r}`) ? (r.endsWith("/") || (r = `${r}/`), r = `${r}index.php`) : r = "/index.php";
918
- const s = `${c(this, m)}${r}`;
919
- if (this.php.fileExists(s))
920
- return s;
921
- throw new Error(`File not found: ${s}`);
922
- };
923
- function inferMimeType(e) {
924
- switch (e.split(".").pop()) {
925
- case "css":
926
- return "text/css";
927
- case "js":
928
- return "application/javascript";
929
- case "png":
930
- return "image/png";
931
- case "jpg":
932
- case "jpeg":
933
- return "image/jpeg";
934
- case "gif":
935
- return "image/gif";
936
- case "svg":
937
- return "image/svg+xml";
938
- case "woff":
939
- return "font/woff";
940
- case "woff2":
941
- return "font/woff2";
942
- case "ttf":
943
- return "font/ttf";
944
- case "otf":
945
- return "font/otf";
946
- case "eot":
947
- return "font/eot";
948
- case "ico":
949
- return "image/x-icon";
950
- case "html":
951
- return "text/html";
952
- case "json":
953
- return "application/json";
954
- case "xml":
955
- return "application/xml";
956
- case "txt":
957
- case "md":
958
- return "text/plain";
959
- case "pdf":
960
- return "application/pdf";
961
- case "webp":
962
- return "image/webp";
963
- case "mp3":
964
- return "audio/mpeg";
965
- case "mp4":
966
- return "video/mp4";
967
- case "csv":
968
- return "text/csv";
969
- case "xls":
970
- return "application/vnd.ms-excel";
971
- case "xlsx":
972
- return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
973
- case "doc":
974
- return "application/msword";
975
- case "docx":
976
- return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
977
- case "ppt":
978
- return "application/vnd.ms-powerpoint";
979
- case "pptx":
980
- return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
981
- case "zip":
982
- return "application/zip";
983
- case "rar":
984
- return "application/x-rar-compressed";
985
- case "tar":
986
- return "application/x-tar";
987
- case "gz":
988
- return "application/gzip";
989
- case "7z":
990
- return "application/x-7z-compressed";
991
- default:
992
- return "application-octet-stream";
993
- }
994
- }
995
- function seemsLikeAPHPRequestHandlerPath(e) {
996
- return seemsLikeAPHPFile(e) || seemsLikeADirectoryRoot(e);
997
- }
998
- function seemsLikeAPHPFile(e) {
999
- return e.endsWith(".php") || e.includes(".php/");
1000
- }
1001
- function seemsLikeADirectoryRoot(e) {
1002
- return !e.split("/").pop().includes(".");
1003
- }
1004
- function applyRewriteRules(e, t) {
1005
- for (const r of t)
1006
- if (new RegExp(r.match).test(e))
1007
- return e.replace(r.match, r.replacement);
1008
- return e;
1009
- }
1010
- const FileErrorCodes = {
1011
- 0: "No error occurred. System call completed successfully.",
1012
- 1: "Argument list too long.",
1013
- 2: "Permission denied.",
1014
- 3: "Address in use.",
1015
- 4: "Address not available.",
1016
- 5: "Address family not supported.",
1017
- 6: "Resource unavailable, or operation would block.",
1018
- 7: "Connection already in progress.",
1019
- 8: "Bad file descriptor.",
1020
- 9: "Bad message.",
1021
- 10: "Device or resource busy.",
1022
- 11: "Operation canceled.",
1023
- 12: "No child processes.",
1024
- 13: "Connection aborted.",
1025
- 14: "Connection refused.",
1026
- 15: "Connection reset.",
1027
- 16: "Resource deadlock would occur.",
1028
- 17: "Destination address required.",
1029
- 18: "Mathematics argument out of domain of function.",
1030
- 19: "Reserved.",
1031
- 20: "File exists.",
1032
- 21: "Bad address.",
1033
- 22: "File too large.",
1034
- 23: "Host is unreachable.",
1035
- 24: "Identifier removed.",
1036
- 25: "Illegal byte sequence.",
1037
- 26: "Operation in progress.",
1038
- 27: "Interrupted function.",
1039
- 28: "Invalid argument.",
1040
- 29: "I/O error.",
1041
- 30: "Socket is connected.",
1042
- 31: "There is a directory under that path.",
1043
- 32: "Too many levels of symbolic links.",
1044
- 33: "File descriptor value too large.",
1045
- 34: "Too many links.",
1046
- 35: "Message too large.",
1047
- 36: "Reserved.",
1048
- 37: "Filename too long.",
1049
- 38: "Network is down.",
1050
- 39: "Connection aborted by network.",
1051
- 40: "Network unreachable.",
1052
- 41: "Too many files open in system.",
1053
- 42: "No buffer space available.",
1054
- 43: "No such device.",
1055
- 44: "There is no such file or directory OR the parent directory does not exist.",
1056
- 45: "Executable file format error.",
1057
- 46: "No locks available.",
1058
- 47: "Reserved.",
1059
- 48: "Not enough space.",
1060
- 49: "No message of the desired type.",
1061
- 50: "Protocol not available.",
1062
- 51: "No space left on device.",
1063
- 52: "Function not supported.",
1064
- 53: "The socket is not connected.",
1065
- 54: "Not a directory or a symbolic link to a directory.",
1066
- 55: "Directory not empty.",
1067
- 56: "State not recoverable.",
1068
- 57: "Not a socket.",
1069
- 58: "Not supported, or operation not supported on socket.",
1070
- 59: "Inappropriate I/O control operation.",
1071
- 60: "No such device or address.",
1072
- 61: "Value too large to be stored in data type.",
1073
- 62: "Previous owner died.",
1074
- 63: "Operation not permitted.",
1075
- 64: "Broken pipe.",
1076
- 65: "Protocol error.",
1077
- 66: "Protocol not supported.",
1078
- 67: "Protocol wrong type for socket.",
1079
- 68: "Result too large.",
1080
- 69: "Read-only file system.",
1081
- 70: "Invalid seek.",
1082
- 71: "No such process.",
1083
- 72: "Reserved.",
1084
- 73: "Connection timed out.",
1085
- 74: "Text file busy.",
1086
- 75: "Cross-device link.",
1087
- 76: "Extension: Capabilities insufficient."
1088
- };
1089
- function getEmscriptenFsError(e) {
1090
- const t = typeof e == "object" ? e == null ? void 0 : e.errno : null;
1091
- if (t in FileErrorCodes)
1092
- return FileErrorCodes[t];
1093
- }
1094
- function rethrowFileSystemError(e = "") {
1095
- return function(r, s, n) {
1096
- const o = n.value;
1097
- n.value = function(...i) {
1098
- try {
1099
- return o.apply(this, i);
1100
- } catch (a) {
1101
- const u = typeof a == "object" ? a == null ? void 0 : a.errno : null;
1102
- if (u in FileErrorCodes) {
1103
- const l = FileErrorCodes[u], f = typeof i[0] == "string" ? i[0] : null, fe = f !== null ? e.replaceAll("{path}", f) : e;
1104
- throw new Error(`${fe}: ${l}`, {
1105
- cause: a
1106
- });
1107
- }
1108
- throw a;
1109
- }
1110
- };
1111
- };
1112
- }
1113
- const RuntimeId = Symbol("RuntimeId"), loadedRuntimes = /* @__PURE__ */ new Map();
1114
- let lastRuntimeId = 0;
1115
- async function loadPHPRuntime(e, t = {}) {
1116
- const [r, s, n] = makePromise(), o = e.init(currentJsRuntime, {
1117
- onAbort(a) {
1118
- n(a), console.error(a);
1119
- },
1120
- ENV: {},
1121
- // Emscripten sometimes prepends a '/' to the path, which
1122
- // breaks vite dev mode. An identity `locateFile` function
1123
- // fixes it.
1124
- locateFile: (a) => a,
1125
- ...t,
1126
- noInitialRun: !0,
1127
- onRuntimeInitialized() {
1128
- t.onRuntimeInitialized && t.onRuntimeInitialized(), s();
1129
- }
1130
- });
1131
- await r;
1132
- const i = ++lastRuntimeId;
1133
- return o.id = i, o.originalExit = o._exit, o._exit = function(a) {
1134
- return loadedRuntimes.delete(i), o.originalExit(a);
1135
- }, o[RuntimeId] = i, loadedRuntimes.set(i, o), i;
1136
- }
1137
- function getLoadedRuntime(e) {
1138
- return loadedRuntimes.get(e);
1139
- }
1140
- const currentJsRuntime = function() {
1141
- var e;
1142
- return typeof process < "u" && ((e = process.release) == null ? void 0 : e.name) === "node" ? "NODE" : typeof window < "u" ? "WEB" : typeof WorkerGlobalScope < "u" && self instanceof WorkerGlobalScope ? "WORKER" : "NODE";
1143
- }(), makePromise = () => {
1144
- const e = [], t = new Promise((r, s) => {
1145
- e.push(r, s);
1146
- });
1147
- return e.unshift(t), e;
1148
- };
1149
- var __defProp = Object.defineProperty, __getOwnPropDesc = Object.getOwnPropertyDescriptor, __decorateClass = (e, t, r, s) => {
1150
- for (var n = s > 1 ? void 0 : s ? __getOwnPropDesc(t, r) : t, o = e.length - 1, i; o >= 0; o--)
1151
- (i = e[o]) && (n = (s ? i(t, r, n) : i(n)) || n);
1152
- return s && n && __defProp(t, r, n), n;
1153
- };
1154
- const STRING = "string", NUMBER = "number", __private__dont__use = Symbol("__private__dont__use");
1155
- class PHPExecutionFailureError extends Error {
1156
- constructor(t, r, s) {
1157
- super(t), this.response = r, this.source = s;
1158
- }
1159
- }
1160
- var R, F, H, y, w, P, T, I, X, N, ee, U, te, L, re, O, se, M, ne, $, ie, D, oe, q, ae, j, le, z, ce, W, ue, G, de, V, he, J, pe;
1161
- class BasePHP {
1162
- /**
1163
- * Initializes a PHP runtime.
1164
- *
1165
- * @internal
1166
- * @param PHPRuntime - Optional. PHP Runtime ID as initialized by loadPHPRuntime.
1167
- * @param serverOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
1168
- */
1169
- constructor(e, t) {
1170
- /**
1171
- * Prepares the $_SERVER entries for the PHP runtime.
1172
- *
1173
- * @param defaults Default entries to include in $_SERVER.
1174
- * @param headers HTTP headers to include in $_SERVER (as HTTP_ prefixed entries).
1175
- * @param port HTTP port, used to determine infer $_SERVER['HTTPS'] value if none
1176
- * was provided.
1177
- * @returns Computed $_SERVER entries.
1178
- */
1179
- d(this, I);
1180
- d(this, N);
1181
- d(this, U);
1182
- d(this, L);
1183
- d(this, O);
1184
- d(this, M);
1185
- d(this, $);
1186
- d(this, D);
1187
- d(this, q);
1188
- d(this, j);
1189
- d(this, z);
1190
- d(this, W);
1191
- d(this, G);
1192
- d(this, V);
1193
- d(this, J);
1194
- d(this, R, void 0);
1195
- d(this, F, void 0);
1196
- d(this, H, void 0);
1197
- d(this, y, void 0);
1198
- d(this, w, void 0);
1199
- d(this, P, void 0);
1200
- d(this, T, void 0);
1201
- h(this, R, []), h(this, y, !1), h(this, w, null), h(this, P, /* @__PURE__ */ new Map()), h(this, T, []), this.semaphore = new Semaphore({ concurrency: 1 }), e !== void 0 && this.initializeRuntime(e), t && (this.requestHandler = new PHPRequestHandler(this, t));
1202
- }
1203
- addEventListener(e, t) {
1204
- c(this, P).has(e) || c(this, P).set(e, /* @__PURE__ */ new Set()), c(this, P).get(e).add(t);
1205
- }
1206
- removeEventListener(e, t) {
1207
- var r;
1208
- (r = c(this, P).get(e)) == null || r.delete(t);
1209
- }
1210
- dispatchEvent(e) {
1211
- const t = c(this, P).get(e.type);
1212
- if (t)
1213
- for (const r of t)
1214
- r(e);
1215
- }
1216
- /** @inheritDoc */
1217
- async onMessage(e) {
1218
- c(this, T).push(e);
1219
- }
1220
- /** @inheritDoc */
1221
- async setSpawnHandler(handler) {
1222
- typeof handler == "string" && (handler = createSpawnHandler(eval(handler))), this[__private__dont__use].spawnProcess = handler;
1223
- }
1224
- /** @inheritDoc */
1225
- get absoluteUrl() {
1226
- return this.requestHandler.absoluteUrl;
1227
- }
1228
- /** @inheritDoc */
1229
- get documentRoot() {
1230
- return this.requestHandler.documentRoot;
1231
- }
1232
- /** @inheritDoc */
1233
- pathToInternalUrl(e) {
1234
- return this.requestHandler.pathToInternalUrl(e);
1235
- }
1236
- /** @inheritDoc */
1237
- internalUrlToPath(e) {
1238
- return this.requestHandler.internalUrlToPath(e);
1239
- }
1240
- initializeRuntime(e) {
1241
- if (this[__private__dont__use])
1242
- throw new Error("PHP runtime already initialized.");
1243
- const t = getLoadedRuntime(e);
1244
- if (!t)
1245
- throw new Error("Invalid PHP runtime id.");
1246
- this[__private__dont__use] = t, t.onMessage = async (r) => {
1247
- for (const s of c(this, T)) {
1248
- const n = await s(r);
1249
- if (n)
1250
- return n;
1251
- }
1252
- return "";
1253
- }, h(this, w, improveWASMErrorReporting(t)), this.dispatchEvent({
1254
- type: "runtime.initialized"
1255
- });
1256
- }
1257
- /** @inheritDoc */
1258
- async setSapiName(e) {
1259
- if (this[__private__dont__use].ccall(
1260
- "wasm_set_sapi_name",
1261
- NUMBER,
1262
- [STRING],
1263
- [e]
1264
- ) !== 0)
1265
- throw new Error(
1266
- "Could not set SAPI name. This can only be done before the PHP WASM module is initialized.Did you already dispatch any requests?"
1267
- );
1268
- h(this, H, e);
1269
- }
1270
- /** @inheritDoc */
1271
- setPhpIniPath(e) {
1272
- if (c(this, y))
1273
- throw new Error("Cannot set PHP ini path after calling run().");
1274
- h(this, F, e), this[__private__dont__use].ccall(
1275
- "wasm_set_phpini_path",
1276
- null,
1277
- ["string"],
1278
- [e]
1279
- );
1280
- }
1281
- /** @inheritDoc */
1282
- setPhpIniEntry(e, t) {
1283
- if (c(this, y))
1284
- throw new Error("Cannot set PHP ini entries after calling run().");
1285
- c(this, R).push([e, t]);
1286
- }
1287
- /** @inheritDoc */
1288
- chdir(e) {
1289
- this[__private__dont__use].FS.chdir(e);
1290
- }
1291
- /** @inheritDoc */
1292
- async request(e) {
1293
- if (!this.requestHandler)
1294
- throw new Error("No request handler available.");
1295
- return this.requestHandler.request(e);
1296
- }
1297
- /** @inheritDoc */
1298
- async run(e) {
1299
- const t = await this.semaphore.acquire();
1300
- let r;
1301
- try {
1302
- if (c(this, y) || (p(this, N, ee).call(this), h(this, y, !0)), e.scriptPath && !this.fileExists(e.scriptPath))
1232
+ if (u(this, f) || (p(this, I, Y).call(this), d(this, f, !0)), e.scriptPath && !this.fileExists(e.scriptPath))
1303
1233
  throw new Error(
1304
1234
  `The script path "${e.scriptPath}" does not exist.`
1305
1235
  );
1306
- p(this, z, ce).call(this, e.scriptPath || ""), p(this, L, re).call(this, e.relativeUri || ""), p(this, D, oe).call(this, e.method || "GET");
1307
- const s = normalizeHeaders(e.headers || {}), n = s.host || "example.com:443", o = p(this, $, ie).call(this, n, e.protocol || "http");
1308
- p(this, O, se).call(this, n), p(this, M, ne).call(this, o), p(this, q, ae).call(this, s), e.body && (r = p(this, j, le).call(this, e.body)), typeof e.code == "string" && p(this, V, he).call(this, " ?>" + e.code);
1309
- const i = p(this, I, X).call(this, e.$_SERVER, s, o);
1310
- for (const l in i)
1311
- p(this, W, ue).call(this, l, i[l]);
1236
+ p(this, D, oe).call(this, e.scriptPath || ""), p(this, k, X).call(this, e.relativeUri || ""), p(this, N, se).call(this, e.method || "GET");
1237
+ const s = normalizeHeaders(e.headers || {}), n = s.host || "example.com:443", i = p(this, L, re).call(this, n, e.protocol || "http");
1238
+ p(this, M, ee).call(this, n), p(this, B, te).call(this, i), p(this, U, ne).call(this, s), e.body && (r = p(this, O, ie).call(this, e.body)), typeof e.code == "string" && p(this, j, ce).call(this, " ?>" + e.code);
1239
+ const o = p(this, C, Z).call(this, e.$_SERVER, s, i);
1240
+ for (const c in o)
1241
+ p(this, $, ae).call(this, c, o[c]);
1312
1242
  const a = e.env || {};
1313
- for (const l in a)
1314
- p(this, G, de).call(this, l, a[l]);
1315
- const u = await p(this, J, pe).call(this);
1316
- if (u.exitCode !== 0) {
1317
- console.warn("PHP.run() output was:", u.text);
1318
- const l = new PHPExecutionFailureError(
1319
- `PHP.run() failed with exit code ${u.exitCode} and the following output: ` + u.errors,
1320
- u,
1243
+ for (const c in a)
1244
+ p(this, q, le).call(this, c, a[c]);
1245
+ const l = await p(this, z, ue).call(this);
1246
+ if (l.exitCode !== 0) {
1247
+ logger.warn("PHP.run() output was:", l.text);
1248
+ const c = new PHPExecutionFailureError(
1249
+ `PHP.run() failed with exit code ${l.exitCode} and the following output: ` + l.errors,
1250
+ l,
1321
1251
  "request"
1322
1252
  );
1323
- throw console.error(l), l;
1253
+ throw logger.error(c), c;
1324
1254
  }
1325
- return u;
1255
+ return l;
1326
1256
  } catch (s) {
1327
1257
  throw this.dispatchEvent({
1328
1258
  type: "request.error",
@@ -1407,7 +1337,7 @@ class BasePHP {
1407
1337
  }
1408
1338
  return r;
1409
1339
  } catch (r) {
1410
- return console.error(r, { path: e }), [];
1340
+ return logger.error(r, { path: e }), [];
1411
1341
  }
1412
1342
  }
1413
1343
  isDir(e) {
@@ -1415,309 +1345,705 @@ class BasePHP {
1415
1345
  this[__private__dont__use].FS.lookupPath(e).node.mode
1416
1346
  ) : !1;
1417
1347
  }
1418
- fileExists(e) {
1419
- try {
1420
- return this[__private__dont__use].FS.lookupPath(e), !0;
1421
- } catch {
1422
- return !1;
1423
- }
1348
+ fileExists(e) {
1349
+ try {
1350
+ return this[__private__dont__use].FS.lookupPath(e), !0;
1351
+ } catch {
1352
+ return !1;
1353
+ }
1354
+ }
1355
+ /**
1356
+ * Hot-swaps the PHP runtime for a new one without
1357
+ * interrupting the operations of this PHP instance.
1358
+ *
1359
+ * @param runtime
1360
+ * @param cwd. Internal, the VFS path to recreate in the new runtime.
1361
+ * This arg is temporary and will be removed once BasePHP
1362
+ * is fully decoupled from the request handler and
1363
+ * accepts a constructor-level cwd argument.
1364
+ */
1365
+ hotSwapPHPRuntime(e, t) {
1366
+ const r = this[__private__dont__use].FS;
1367
+ try {
1368
+ this.exit();
1369
+ } catch {
1370
+ }
1371
+ this.initializeRuntime(e), u(this, v) && this.setPhpIniPath(u(this, v)), u(this, R) && this.setSapiName(u(this, R)), t && copyFS(r, this[__private__dont__use].FS, t);
1372
+ }
1373
+ exit(e = 0) {
1374
+ this.dispatchEvent({
1375
+ type: "runtime.beforedestroy"
1376
+ });
1377
+ try {
1378
+ this[__private__dont__use]._exit(e);
1379
+ } catch {
1380
+ }
1381
+ d(this, f, !1), d(this, P, null), delete this[__private__dont__use].onMessage, delete this[__private__dont__use];
1382
+ }
1383
+ [Symbol.dispose]() {
1384
+ u(this, f) && this.exit(0);
1385
+ }
1386
+ }
1387
+ w = new WeakMap(), v = new WeakMap(), R = new WeakMap(), f = new WeakMap(), P = new WeakMap(), _ = new WeakMap(), S = new WeakMap(), C = new WeakSet(), Z = function(e, t, r) {
1388
+ const s = {
1389
+ ...e || {}
1390
+ };
1391
+ s.HTTPS = s.HTTPS || r === 443 ? "on" : "off";
1392
+ for (const n in t) {
1393
+ let i = "HTTP_";
1394
+ ["content-type", "content-length"].includes(n.toLowerCase()) && (i = ""), s[`${i}${n.toUpperCase().replace(/-/g, "_")}`] = t[n];
1395
+ }
1396
+ return s;
1397
+ }, I = new WeakSet(), Y = function() {
1398
+ if (this.setPhpIniEntry("auto_prepend_file", "/internal/consts.php"), this.fileExists("/internal/consts.php") || this.writeFile(
1399
+ "/internal/consts.php",
1400
+ `<?php
1401
+ if(file_exists('/internal/consts.json')) {
1402
+ $consts = json_decode(file_get_contents('/internal/consts.json'), true);
1403
+ foreach ($consts as $const => $value) {
1404
+ if (!defined($const) && is_scalar($value)) {
1405
+ define($const, $value);
1406
+ }
1407
+ }
1408
+ }`
1409
+ ), u(this, w).length > 0) {
1410
+ const e = u(this, w).map(([t, r]) => `${t}=${r}`).join(`
1411
+ `) + `
1412
+
1413
+ `;
1414
+ this[__private__dont__use].ccall(
1415
+ "wasm_set_phpini_entries",
1416
+ null,
1417
+ [STRING],
1418
+ [e]
1419
+ );
1420
+ }
1421
+ this[__private__dont__use].ccall("php_wasm_init", null, [], []);
1422
+ }, A = new WeakSet(), K = function() {
1423
+ const e = "/internal/headers.json";
1424
+ if (!this.fileExists(e))
1425
+ throw new Error(
1426
+ "SAPI Error: Could not find response headers file."
1427
+ );
1428
+ const t = JSON.parse(this.readFileAsText(e)), r = {};
1429
+ for (const s of t.headers) {
1430
+ if (!s.includes(": "))
1431
+ continue;
1432
+ const n = s.indexOf(": "), i = s.substring(0, n).toLowerCase(), o = s.substring(n + 2);
1433
+ i in r || (r[i] = []), r[i].push(o);
1434
+ }
1435
+ return {
1436
+ headers: r,
1437
+ httpStatusCode: t.status
1438
+ };
1439
+ }, k = new WeakSet(), X = function(e) {
1440
+ if (this[__private__dont__use].ccall(
1441
+ "wasm_set_request_uri",
1442
+ null,
1443
+ [STRING],
1444
+ [e]
1445
+ ), e.includes("?")) {
1446
+ const t = e.substring(e.indexOf("?") + 1);
1447
+ this[__private__dont__use].ccall(
1448
+ "wasm_set_query_string",
1449
+ null,
1450
+ [STRING],
1451
+ [t]
1452
+ );
1453
+ }
1454
+ }, M = new WeakSet(), ee = function(e) {
1455
+ this[__private__dont__use].ccall(
1456
+ "wasm_set_request_host",
1457
+ null,
1458
+ [STRING],
1459
+ [e]
1460
+ );
1461
+ }, B = new WeakSet(), te = function(e) {
1462
+ this[__private__dont__use].ccall(
1463
+ "wasm_set_request_port",
1464
+ null,
1465
+ [NUMBER],
1466
+ [e]
1467
+ );
1468
+ }, L = new WeakSet(), re = function(e, t) {
1469
+ let r;
1470
+ try {
1471
+ r = parseInt(new URL(e).port, 10);
1472
+ } catch {
1473
+ }
1474
+ return (!r || isNaN(r) || r === 80) && (r = t === "https" ? 443 : 80), r;
1475
+ }, N = new WeakSet(), se = function(e) {
1476
+ this[__private__dont__use].ccall(
1477
+ "wasm_set_request_method",
1478
+ null,
1479
+ [STRING],
1480
+ [e]
1481
+ );
1482
+ }, U = new WeakSet(), ne = function(e) {
1483
+ e.cookie && this[__private__dont__use].ccall(
1484
+ "wasm_set_cookies",
1485
+ null,
1486
+ [STRING],
1487
+ [e.cookie]
1488
+ ), e["content-type"] && this[__private__dont__use].ccall(
1489
+ "wasm_set_content_type",
1490
+ null,
1491
+ [STRING],
1492
+ [e["content-type"]]
1493
+ ), e["content-length"] && this[__private__dont__use].ccall(
1494
+ "wasm_set_content_length",
1495
+ null,
1496
+ [NUMBER],
1497
+ [parseInt(e["content-length"], 10)]
1498
+ );
1499
+ }, O = new WeakSet(), ie = function(e) {
1500
+ let t, r;
1501
+ typeof e == "string" ? (logger.warn(
1502
+ "Passing a string as the request body is deprecated. Please use a Uint8Array instead. See https://github.com/WordPress/wordpress-playground/issues/997 for more details"
1503
+ ), r = this[__private__dont__use].lengthBytesUTF8(e), t = r + 1) : (r = e.byteLength, t = e.byteLength);
1504
+ const s = this[__private__dont__use].malloc(t);
1505
+ if (!s)
1506
+ throw new Error("Could not allocate memory for the request body.");
1507
+ return typeof e == "string" ? this[__private__dont__use].stringToUTF8(
1508
+ e,
1509
+ s,
1510
+ t + 1
1511
+ ) : this[__private__dont__use].HEAPU8.set(e, s), this[__private__dont__use].ccall(
1512
+ "wasm_set_request_body",
1513
+ null,
1514
+ [NUMBER],
1515
+ [s]
1516
+ ), this[__private__dont__use].ccall(
1517
+ "wasm_set_content_length",
1518
+ null,
1519
+ [NUMBER],
1520
+ [r]
1521
+ ), s;
1522
+ }, D = new WeakSet(), oe = function(e) {
1523
+ this[__private__dont__use].ccall(
1524
+ "wasm_set_path_translated",
1525
+ null,
1526
+ [STRING],
1527
+ [e]
1528
+ );
1529
+ }, $ = new WeakSet(), ae = function(e, t) {
1530
+ this[__private__dont__use].ccall(
1531
+ "wasm_add_SERVER_entry",
1532
+ null,
1533
+ [STRING, STRING],
1534
+ [e, t]
1535
+ );
1536
+ }, q = new WeakSet(), le = function(e, t) {
1537
+ this[__private__dont__use].ccall(
1538
+ "wasm_add_ENV_entry",
1539
+ null,
1540
+ [STRING, STRING],
1541
+ [e, t]
1542
+ );
1543
+ }, j = new WeakSet(), ce = function(e) {
1544
+ this[__private__dont__use].ccall(
1545
+ "wasm_set_php_code",
1546
+ null,
1547
+ [STRING],
1548
+ [e]
1549
+ );
1550
+ }, z = new WeakSet(), ue = async function() {
1551
+ var n;
1552
+ let e, t;
1553
+ try {
1554
+ e = await new Promise((i, o) => {
1555
+ var l;
1556
+ t = (c) => {
1557
+ logger.error(c), logger.error(c.error);
1558
+ const g = new Error("Rethrown");
1559
+ g.cause = c.error, g.betterMessage = c.message, o(g);
1560
+ }, (l = u(this, P)) == null || l.addEventListener(
1561
+ "error",
1562
+ t
1563
+ );
1564
+ const a = this[__private__dont__use].ccall(
1565
+ "wasm_sapi_handle_request",
1566
+ NUMBER,
1567
+ [],
1568
+ [],
1569
+ { async: !0 }
1570
+ );
1571
+ return a instanceof Promise ? a.then(i, o) : i(a);
1572
+ });
1573
+ } catch (i) {
1574
+ for (const c in this)
1575
+ typeof this[c] == "function" && (this[c] = () => {
1576
+ throw new Error(
1577
+ "PHP runtime has crashed – see the earlier error for details."
1578
+ );
1579
+ });
1580
+ this.functionsMaybeMissingFromAsyncify = getFunctionsMaybeMissingFromAsyncify();
1581
+ const o = i, a = "betterMessage" in o ? o.betterMessage : o.message, l = new Error(a);
1582
+ throw l.cause = o, logger.error(l), l;
1583
+ } finally {
1584
+ (n = u(this, P)) == null || n.removeEventListener("error", t);
1585
+ }
1586
+ const { headers: r, httpStatusCode: s } = p(this, A, K).call(this);
1587
+ return new PHPResponse(
1588
+ e === 0 ? s : 500,
1589
+ r,
1590
+ this.readFileAsBuffer("/internal/stdout"),
1591
+ this.readFileAsText("/internal/stderr"),
1592
+ e
1593
+ );
1594
+ };
1595
+ __decorateClass([
1596
+ rethrowFileSystemError('Could not create directory "{path}"')
1597
+ ], BasePHP.prototype, "mkdir", 1);
1598
+ __decorateClass([
1599
+ rethrowFileSystemError('Could not create directory "{path}"')
1600
+ ], BasePHP.prototype, "mkdirTree", 1);
1601
+ __decorateClass([
1602
+ rethrowFileSystemError('Could not read "{path}"')
1603
+ ], BasePHP.prototype, "readFileAsText", 1);
1604
+ __decorateClass([
1605
+ rethrowFileSystemError('Could not read "{path}"')
1606
+ ], BasePHP.prototype, "readFileAsBuffer", 1);
1607
+ __decorateClass([
1608
+ rethrowFileSystemError('Could not write to "{path}"')
1609
+ ], BasePHP.prototype, "writeFile", 1);
1610
+ __decorateClass([
1611
+ rethrowFileSystemError('Could not unlink "{path}"')
1612
+ ], BasePHP.prototype, "unlink", 1);
1613
+ __decorateClass([
1614
+ rethrowFileSystemError('Could not remove directory "{path}"')
1615
+ ], BasePHP.prototype, "rmdir", 1);
1616
+ __decorateClass([
1617
+ rethrowFileSystemError('Could not list files in "{path}"')
1618
+ ], BasePHP.prototype, "listFiles", 1);
1619
+ __decorateClass([
1620
+ rethrowFileSystemError('Could not stat "{path}"')
1621
+ ], BasePHP.prototype, "isDir", 1);
1622
+ __decorateClass([
1623
+ rethrowFileSystemError('Could not stat "{path}"')
1624
+ ], BasePHP.prototype, "fileExists", 1);
1625
+ function normalizeHeaders(e) {
1626
+ const t = {};
1627
+ for (const r in e)
1628
+ t[r.toLowerCase()] = e[r];
1629
+ return t;
1630
+ }
1631
+ function copyFS(e, t, r) {
1632
+ let s;
1633
+ try {
1634
+ s = e.lookupPath(r);
1635
+ } catch {
1636
+ return;
1637
+ }
1638
+ if (!("contents" in s.node))
1639
+ return;
1640
+ if (!e.isDir(s.node.mode)) {
1641
+ t.writeFile(r, e.readFile(r));
1642
+ return;
1424
1643
  }
1644
+ t.mkdirTree(r);
1645
+ const n = e.readdir(r).filter((i) => i !== "." && i !== "..");
1646
+ for (const i of n)
1647
+ copyFS(e, t, joinPaths(r, i));
1648
+ }
1649
+ function isLocalPHP(e) {
1650
+ return !(e instanceof BasePHP);
1651
+ }
1652
+ function isRemotePHP(e) {
1653
+ return !isLocalPHP(e);
1654
+ }
1655
+ const DEFAULT_BASE_URL = "http://example.com";
1656
+ function toRelativeUrl(e) {
1657
+ return e.toString().substring(e.origin.length);
1658
+ }
1659
+ function removePathPrefix(e, t) {
1660
+ return !t || !e.startsWith(t) ? e : e.substring(t.length);
1661
+ }
1662
+ function ensurePathPrefix(e, t) {
1663
+ return !t || e.startsWith(t) ? e : t + e;
1664
+ }
1665
+ async function encodeAsMultipart(e) {
1666
+ const t = `----${Math.random().toString(36).slice(2)}`, r = `multipart/form-data; boundary=${t}`, s = new TextEncoder(), n = [];
1667
+ for (const [l, c] of Object.entries(e))
1668
+ n.push(`--${t}\r
1669
+ `), n.push(`Content-Disposition: form-data; name="${l}"`), c instanceof File && n.push(`; filename="${c.name}"`), n.push(`\r
1670
+ `), c instanceof File && (n.push("Content-Type: application/octet-stream"), n.push(`\r
1671
+ `)), n.push(`\r
1672
+ `), c instanceof File ? n.push(await fileToUint8Array(c)) : n.push(c), n.push(`\r
1673
+ `);
1674
+ n.push(`--${t}--\r
1675
+ `);
1676
+ const i = n.reduce((l, c) => l + c.length, 0), o = new Uint8Array(i);
1677
+ let a = 0;
1678
+ for (const l of n)
1679
+ o.set(
1680
+ typeof l == "string" ? s.encode(l) : l,
1681
+ a
1682
+ ), a += l.length;
1683
+ return { bytes: o, contentType: r };
1684
+ }
1685
+ function fileToUint8Array(e) {
1686
+ return new Promise((t) => {
1687
+ const r = new FileReader();
1688
+ r.onload = () => {
1689
+ t(new Uint8Array(r.result));
1690
+ }, r.readAsArrayBuffer(e);
1691
+ });
1692
+ }
1693
+ var y, b, T, E, x, m, F, H, W, he, G, de, J, pe, V, fe;
1694
+ class PHPRequestHandler {
1425
1695
  /**
1426
- * Hot-swaps the PHP runtime for a new one without
1427
- * interrupting the operations of this PHP instance.
1696
+ * The request handler needs to decide whether to serve a static asset or
1697
+ * run the PHP interpreter. For static assets it should just reuse the primary
1698
+ * PHP even if there's 50 concurrent requests to serve. However, for
1699
+ * dynamic PHP requests, it needs to grab an available interpreter.
1700
+ * Therefore, it cannot just accept PHP as an argument as serving requests
1701
+ * requires access to ProcessManager.
1428
1702
  *
1429
- * @param runtime
1430
- * @param cwd. Internal, the VFS path to recreate in the new runtime.
1431
- * This arg is temporary and will be removed once BasePHP
1432
- * is fully decoupled from the request handler and
1433
- * accepts a constructor-level cwd argument.
1703
+ * @param php - The PHP instance.
1704
+ * @param config - Request Handler configuration.
1434
1705
  */
1435
- hotSwapPHPRuntime(e, t) {
1436
- const r = this[__private__dont__use].FS;
1437
- try {
1438
- this.exit();
1439
- } catch {
1440
- }
1441
- this.initializeRuntime(e), c(this, F) && this.setPhpIniPath(c(this, F)), c(this, H) && this.setSapiName(c(this, H)), t && copyFS(r, this[__private__dont__use].FS, t);
1706
+ constructor(t) {
1707
+ /**
1708
+ * Serves a static file from the PHP filesystem.
1709
+ *
1710
+ * @param fsPath - Absolute path of the static file to serve.
1711
+ * @returns The response.
1712
+ */
1713
+ h(this, W);
1714
+ /**
1715
+ * Spawns a new PHP instance and dispatches a request to it.
1716
+ */
1717
+ h(this, G);
1718
+ /**
1719
+ * Runs the requested PHP file with all the request and $_SERVER
1720
+ * superglobals populated.
1721
+ *
1722
+ * @param request - The request.
1723
+ * @returns The response.
1724
+ */
1725
+ h(this, J);
1726
+ /**
1727
+ * Resolve the requested path to the filesystem path of the requested PHP file.
1728
+ *
1729
+ * Fall back to index.php as if there was a url rewriting rule in place.
1730
+ *
1731
+ * @param requestedPath - The requested pathname.
1732
+ * @throws {Error} If the requested path doesn't exist.
1733
+ * @returns The resolved filesystem path.
1734
+ */
1735
+ h(this, V);
1736
+ h(this, y, void 0);
1737
+ h(this, b, void 0);
1738
+ h(this, T, void 0);
1739
+ h(this, E, void 0);
1740
+ h(this, x, void 0);
1741
+ h(this, m, void 0);
1742
+ h(this, F, void 0);
1743
+ h(this, H, void 0);
1744
+ const {
1745
+ documentRoot: r = "/www/",
1746
+ absoluteUrl: s = typeof location == "object" ? location == null ? void 0 : location.href : "",
1747
+ rewriteRules: n = []
1748
+ } = t;
1749
+ "processManager" in t ? this.processManager = t.processManager : this.processManager = new PHPProcessManager({
1750
+ phpFactory: async (a) => {
1751
+ const l = await t.phpFactory({
1752
+ ...a,
1753
+ requestHandler: this
1754
+ });
1755
+ return l.requestHandler = this, l;
1756
+ },
1757
+ maxPhpInstances: t.maxPhpInstances
1758
+ }), d(this, H, new HttpCookieStore()), d(this, y, r);
1759
+ const i = new URL(s);
1760
+ d(this, T, i.hostname), d(this, E, i.port ? Number(i.port) : i.protocol === "https:" ? 443 : 80), d(this, b, (i.protocol || "").replace(":", ""));
1761
+ const o = u(this, E) !== 443 && u(this, E) !== 80;
1762
+ d(this, x, [
1763
+ u(this, T),
1764
+ o ? `:${u(this, E)}` : ""
1765
+ ].join("")), d(this, m, i.pathname.replace(/\/+$/, "")), d(this, F, [
1766
+ `${u(this, b)}://`,
1767
+ u(this, x),
1768
+ u(this, m)
1769
+ ].join("")), this.rewriteRules = n;
1770
+ }
1771
+ async getPrimaryPhp() {
1772
+ return await this.processManager.getPrimaryPhp();
1442
1773
  }
1443
- exit(e = 0) {
1444
- this.dispatchEvent({
1445
- type: "runtime.beforedestroy"
1446
- });
1447
- try {
1448
- this[__private__dont__use]._exit(e);
1449
- } catch {
1450
- }
1451
- h(this, y, !1), h(this, w, null), delete this[__private__dont__use].onMessage, delete this[__private__dont__use];
1774
+ /**
1775
+ * Converts a path to an absolute URL based at the PHPRequestHandler
1776
+ * root.
1777
+ *
1778
+ * @param path The server path to convert to an absolute URL.
1779
+ * @returns The absolute URL.
1780
+ */
1781
+ pathToInternalUrl(t) {
1782
+ return `${this.absoluteUrl}${t}`;
1452
1783
  }
1453
- }
1454
- R = new WeakMap(), F = new WeakMap(), H = new WeakMap(), y = new WeakMap(), w = new WeakMap(), P = new WeakMap(), T = new WeakMap(), I = new WeakSet(), X = function(e, t, r) {
1455
- const s = {
1456
- ...e || {}
1457
- };
1458
- s.HTTPS = s.HTTPS || r === 443 ? "on" : "off";
1459
- for (const n in t) {
1460
- let o = "HTTP_";
1461
- ["content-type", "content-length"].includes(n.toLowerCase()) && (o = ""), s[`${o}${n.toUpperCase().replace(/-/g, "_")}`] = t[n];
1784
+ /**
1785
+ * Converts an absolute URL based at the PHPRequestHandler to a relative path
1786
+ * without the server pathname and scope.
1787
+ *
1788
+ * @param internalUrl An absolute URL based at the PHPRequestHandler root.
1789
+ * @returns The relative path.
1790
+ */
1791
+ internalUrlToPath(t) {
1792
+ const r = new URL(t);
1793
+ return r.pathname.startsWith(u(this, m)) && (r.pathname = r.pathname.slice(u(this, m).length)), toRelativeUrl(r);
1462
1794
  }
1463
- return s;
1464
- }, N = new WeakSet(), ee = function() {
1465
- if (this.setPhpIniEntry("auto_prepend_file", "/internal/consts.php"), this.fileExists("/internal/consts.php") || this.writeFile(
1466
- "/internal/consts.php",
1467
- `<?php
1468
- if(file_exists('/internal/consts.json')) {
1469
- $consts = json_decode(file_get_contents('/internal/consts.json'), true);
1470
- foreach ($consts as $const => $value) {
1471
- if (!defined($const) && is_scalar($value)) {
1472
- define($const, $value);
1473
- }
1474
- }
1475
- }`
1476
- ), c(this, R).length > 0) {
1477
- const e = c(this, R).map(([t, r]) => `${t}=${r}`).join(`
1478
- `) + `
1479
-
1480
- `;
1481
- this[__private__dont__use].ccall(
1482
- "wasm_set_phpini_entries",
1483
- null,
1484
- [STRING],
1485
- [e]
1486
- );
1795
+ /**
1796
+ * The absolute URL of this PHPRequestHandler instance.
1797
+ */
1798
+ get absoluteUrl() {
1799
+ return u(this, F);
1487
1800
  }
1488
- this[__private__dont__use].ccall("php_wasm_init", null, [], []);
1489
- }, U = new WeakSet(), te = function() {
1490
- const e = "/internal/headers.json";
1491
- if (!this.fileExists(e))
1492
- throw new Error(
1493
- "SAPI Error: Could not find response headers file."
1494
- );
1495
- const t = JSON.parse(this.readFileAsText(e)), r = {};
1496
- for (const s of t.headers) {
1497
- if (!s.includes(": "))
1498
- continue;
1499
- const n = s.indexOf(": "), o = s.substring(0, n).toLowerCase(), i = s.substring(n + 2);
1500
- o in r || (r[o] = []), r[o].push(i);
1801
+ /**
1802
+ * The directory in the PHP filesystem where the server will look
1803
+ * for the files to serve. Default: `/var/www`.
1804
+ */
1805
+ get documentRoot() {
1806
+ return u(this, y);
1501
1807
  }
1502
- return {
1503
- headers: r,
1504
- httpStatusCode: t.status
1505
- };
1506
- }, L = new WeakSet(), re = function(e) {
1507
- if (this[__private__dont__use].ccall(
1508
- "wasm_set_request_uri",
1509
- null,
1510
- [STRING],
1511
- [e]
1512
- ), e.includes("?")) {
1513
- const t = e.substring(e.indexOf("?") + 1);
1514
- this[__private__dont__use].ccall(
1515
- "wasm_set_query_string",
1516
- null,
1517
- [STRING],
1518
- [t]
1519
- );
1808
+ /**
1809
+ * Serves the request – either by serving a static file, or by
1810
+ * dispatching it to the PHP runtime.
1811
+ *
1812
+ * The request() method mode behaves like a web server and only works if
1813
+ * the PHP was initialized with a `requestHandler` option (which the online version
1814
+ * of WordPress Playground does by default).
1815
+ *
1816
+ * In the request mode, you pass an object containing the request information
1817
+ * (method, headers, body, etc.) and the path to the PHP file to run:
1818
+ *
1819
+ * ```ts
1820
+ * const php = PHP.load('7.4', {
1821
+ * requestHandler: {
1822
+ * documentRoot: "/www"
1823
+ * }
1824
+ * })
1825
+ * php.writeFile("/www/index.php", `<?php echo file_get_contents("php://input");`);
1826
+ * const result = await php.request({
1827
+ * method: "GET",
1828
+ * headers: {
1829
+ * "Content-Type": "text/plain"
1830
+ * },
1831
+ * body: "Hello world!",
1832
+ * path: "/www/index.php"
1833
+ * });
1834
+ * // result.text === "Hello world!"
1835
+ * ```
1836
+ *
1837
+ * The `request()` method cannot be used in conjunction with `cli()`.
1838
+ *
1839
+ * @example
1840
+ * ```js
1841
+ * const output = await php.request({
1842
+ * method: 'GET',
1843
+ * url: '/index.php',
1844
+ * headers: {
1845
+ * 'X-foo': 'bar',
1846
+ * },
1847
+ * body: {
1848
+ * foo: 'bar',
1849
+ * },
1850
+ * });
1851
+ * console.log(output.stdout); // "Hello world!"
1852
+ * ```
1853
+ *
1854
+ * @param request - PHP Request data.
1855
+ */
1856
+ async request(t) {
1857
+ const r = t.url.startsWith("http://") || t.url.startsWith("https://"), s = new URL(
1858
+ // Remove the hash part of the URL as it's not meant for the server.
1859
+ t.url.split("#")[0],
1860
+ r ? void 0 : DEFAULT_BASE_URL
1861
+ ), n = applyRewriteRules(
1862
+ removePathPrefix(
1863
+ decodeURIComponent(s.pathname),
1864
+ u(this, m)
1865
+ ),
1866
+ this.rewriteRules
1867
+ ), i = joinPaths(u(this, y), n);
1868
+ return seemsLikeAPHPRequestHandlerPath(i) ? p(this, G, de).call(this, t, s) : p(this, W, he).call(this, await this.processManager.getPrimaryPhp(), i);
1520
1869
  }
1521
- }, O = new WeakSet(), se = function(e) {
1522
- this[__private__dont__use].ccall(
1523
- "wasm_set_request_host",
1524
- null,
1525
- [STRING],
1526
- [e]
1527
- );
1528
- }, M = new WeakSet(), ne = function(e) {
1529
- this[__private__dont__use].ccall(
1530
- "wasm_set_request_port",
1531
- null,
1532
- [NUMBER],
1533
- [e]
1870
+ }
1871
+ y = new WeakMap(), b = new WeakMap(), T = new WeakMap(), E = new WeakMap(), x = new WeakMap(), m = new WeakMap(), F = new WeakMap(), H = new WeakMap(), W = new WeakSet(), he = function(t, r) {
1872
+ if (!t.fileExists(r))
1873
+ return new PHPResponse(
1874
+ 404,
1875
+ // Let the service worker know that no static file was found
1876
+ // and that it's okay to issue a real fetch() to the server.
1877
+ {
1878
+ "x-file-type": ["static"]
1879
+ },
1880
+ new TextEncoder().encode("404 File not found")
1881
+ );
1882
+ const s = t.readFileAsBuffer(r);
1883
+ return new PHPResponse(
1884
+ 200,
1885
+ {
1886
+ "content-length": [`${s.byteLength}`],
1887
+ // @TODO: Infer the content-type from the arrayBuffer instead of the file path.
1888
+ // The code below won't return the correct mime-type if the extension
1889
+ // was tampered with.
1890
+ "content-type": [inferMimeType(r)],
1891
+ "accept-ranges": ["bytes"],
1892
+ "cache-control": ["public, max-age=0"]
1893
+ },
1894
+ s
1534
1895
  );
1535
- }, $ = new WeakSet(), ie = function(e, t) {
1536
- let r;
1896
+ }, G = new WeakSet(), de = async function(t, r) {
1897
+ let s;
1537
1898
  try {
1538
- r = parseInt(new URL(e).port, 10);
1539
- } catch {
1899
+ s = await this.processManager.acquirePHPInstance();
1900
+ } catch (n) {
1901
+ return n instanceof MaxPhpInstancesError ? PHPResponse.forHttpCode(502) : PHPResponse.forHttpCode(500);
1540
1902
  }
1541
- return (!r || isNaN(r) || r === 80) && (r = t === "https" ? 443 : 80), r;
1542
- }, D = new WeakSet(), oe = function(e) {
1543
- this[__private__dont__use].ccall(
1544
- "wasm_set_request_method",
1545
- null,
1546
- [STRING],
1547
- [e]
1548
- );
1549
- }, q = new WeakSet(), ae = function(e) {
1550
- e.cookie && this[__private__dont__use].ccall(
1551
- "wasm_set_cookies",
1552
- null,
1553
- [STRING],
1554
- [e.cookie]
1555
- ), e["content-type"] && this[__private__dont__use].ccall(
1556
- "wasm_set_content_type",
1557
- null,
1558
- [STRING],
1559
- [e["content-type"]]
1560
- ), e["content-length"] && this[__private__dont__use].ccall(
1561
- "wasm_set_content_length",
1562
- null,
1563
- [NUMBER],
1564
- [parseInt(e["content-length"], 10)]
1565
- );
1566
- }, j = new WeakSet(), le = function(e) {
1567
- let t, r;
1568
- typeof e == "string" ? (console.warn(
1569
- "Passing a string as the request body is deprecated. Please use a Uint8Array instead. See https://github.com/WordPress/wordpress-playground/issues/997 for more details"
1570
- ), r = this[__private__dont__use].lengthBytesUTF8(e), t = r + 1) : (r = e.byteLength, t = e.byteLength);
1571
- const s = this[__private__dont__use].malloc(t);
1572
- if (!s)
1573
- throw new Error("Could not allocate memory for the request body.");
1574
- return typeof e == "string" ? this[__private__dont__use].stringToUTF8(
1575
- e,
1576
- s,
1577
- t + 1
1578
- ) : this[__private__dont__use].HEAPU8.set(e, s), this[__private__dont__use].ccall(
1579
- "wasm_set_request_body",
1580
- null,
1581
- [NUMBER],
1582
- [s]
1583
- ), this[__private__dont__use].ccall(
1584
- "wasm_set_content_length",
1585
- null,
1586
- [NUMBER],
1587
- [r]
1588
- ), s;
1589
- }, z = new WeakSet(), ce = function(e) {
1590
- this[__private__dont__use].ccall(
1591
- "wasm_set_path_translated",
1592
- null,
1593
- [STRING],
1594
- [e]
1595
- );
1596
- }, W = new WeakSet(), ue = function(e, t) {
1597
- this[__private__dont__use].ccall(
1598
- "wasm_add_SERVER_entry",
1599
- null,
1600
- [STRING, STRING],
1601
- [e, t]
1602
- );
1603
- }, G = new WeakSet(), de = function(e, t) {
1604
- this[__private__dont__use].ccall(
1605
- "wasm_add_ENV_entry",
1606
- null,
1607
- [STRING, STRING],
1608
- [e, t]
1609
- );
1610
- }, V = new WeakSet(), he = function(e) {
1611
- this[__private__dont__use].ccall(
1612
- "wasm_set_php_code",
1613
- null,
1614
- [STRING],
1615
- [e]
1616
- );
1617
- }, J = new WeakSet(), pe = async function() {
1618
- var n;
1619
- let e, t;
1620
1903
  try {
1621
- e = await new Promise((o, i) => {
1622
- var u;
1623
- t = (l) => {
1624
- console.error(l), console.error(l.error);
1625
- const f = new Error("Rethrown");
1626
- f.cause = l.error, f.betterMessage = l.message, i(f);
1627
- }, (u = c(this, w)) == null || u.addEventListener(
1628
- "error",
1629
- t
1630
- );
1631
- const a = this[__private__dont__use].ccall(
1632
- "wasm_sapi_handle_request",
1633
- NUMBER,
1634
- [],
1635
- [],
1636
- { async: !0 }
1637
- );
1638
- return a instanceof Promise ? a.then(o, i) : o(a);
1639
- });
1640
- } catch (o) {
1641
- for (const l in this)
1642
- typeof this[l] == "function" && (this[l] = () => {
1643
- throw new Error(
1644
- "PHP runtime has crashed – see the earlier error for details."
1645
- );
1646
- });
1647
- this.functionsMaybeMissingFromAsyncify = getFunctionsMaybeMissingFromAsyncify();
1648
- const i = o, a = "betterMessage" in i ? i.betterMessage : i.message, u = new Error(a);
1649
- throw u.cause = i, console.error(u), u;
1904
+ return await p(this, J, pe).call(this, s.php, t, r);
1650
1905
  } finally {
1651
- (n = c(this, w)) == null || n.removeEventListener("error", t);
1906
+ s.reap();
1907
+ }
1908
+ }, J = new WeakSet(), pe = async function(t, r, s) {
1909
+ let n = "GET";
1910
+ const i = {
1911
+ host: u(this, x),
1912
+ ...normalizeHeaders(r.headers || {}),
1913
+ cookie: u(this, H).getCookieRequestHeader()
1914
+ };
1915
+ let o = r.body;
1916
+ if (typeof o == "object" && !(o instanceof Uint8Array)) {
1917
+ n = "POST";
1918
+ const { bytes: l, contentType: c } = await encodeAsMultipart(o);
1919
+ o = l, i["content-type"] = c;
1652
1920
  }
1653
- const { headers: r, httpStatusCode: s } = p(this, U, te).call(this);
1654
- return new PHPResponse(
1655
- e === 0 ? s : 500,
1656
- r,
1657
- this.readFileAsBuffer("/internal/stdout"),
1658
- this.readFileAsText("/internal/stderr"),
1659
- e
1660
- );
1661
- };
1662
- __decorateClass([
1663
- rethrowFileSystemError('Could not create directory "{path}"')
1664
- ], BasePHP.prototype, "mkdir", 1);
1665
- __decorateClass([
1666
- rethrowFileSystemError('Could not create directory "{path}"')
1667
- ], BasePHP.prototype, "mkdirTree", 1);
1668
- __decorateClass([
1669
- rethrowFileSystemError('Could not read "{path}"')
1670
- ], BasePHP.prototype, "readFileAsText", 1);
1671
- __decorateClass([
1672
- rethrowFileSystemError('Could not read "{path}"')
1673
- ], BasePHP.prototype, "readFileAsBuffer", 1);
1674
- __decorateClass([
1675
- rethrowFileSystemError('Could not write to "{path}"')
1676
- ], BasePHP.prototype, "writeFile", 1);
1677
- __decorateClass([
1678
- rethrowFileSystemError('Could not unlink "{path}"')
1679
- ], BasePHP.prototype, "unlink", 1);
1680
- __decorateClass([
1681
- rethrowFileSystemError('Could not remove directory "{path}"')
1682
- ], BasePHP.prototype, "rmdir", 1);
1683
- __decorateClass([
1684
- rethrowFileSystemError('Could not list files in "{path}"')
1685
- ], BasePHP.prototype, "listFiles", 1);
1686
- __decorateClass([
1687
- rethrowFileSystemError('Could not stat "{path}"')
1688
- ], BasePHP.prototype, "isDir", 1);
1689
- __decorateClass([
1690
- rethrowFileSystemError('Could not stat "{path}"')
1691
- ], BasePHP.prototype, "fileExists", 1);
1692
- function normalizeHeaders(e) {
1693
- const t = {};
1694
- for (const r in e)
1695
- t[r.toLowerCase()] = e[r];
1696
- return t;
1697
- }
1698
- function copyFS(e, t, r) {
1699
- let s;
1921
+ let a;
1700
1922
  try {
1701
- s = e.lookupPath(r);
1923
+ a = p(this, V, fe).call(this, t, decodeURIComponent(s.pathname));
1702
1924
  } catch {
1703
- return;
1925
+ return PHPResponse.forHttpCode(404);
1704
1926
  }
1705
- if (!("contents" in s.node))
1706
- return;
1707
- if (!e.isDir(s.node.mode)) {
1708
- t.writeFile(r, e.readFile(r));
1709
- return;
1927
+ try {
1928
+ const l = await t.run({
1929
+ relativeUri: ensurePathPrefix(
1930
+ toRelativeUrl(s),
1931
+ u(this, m)
1932
+ ),
1933
+ protocol: u(this, b),
1934
+ method: r.method || n,
1935
+ $_SERVER: {
1936
+ REMOTE_ADDR: "127.0.0.1",
1937
+ DOCUMENT_ROOT: u(this, y),
1938
+ HTTPS: u(this, F).startsWith("https://") ? "on" : ""
1939
+ },
1940
+ body: o,
1941
+ scriptPath: a,
1942
+ headers: i
1943
+ });
1944
+ return u(this, H).rememberCookiesFromResponseHeaders(
1945
+ l.headers
1946
+ ), l;
1947
+ } catch (l) {
1948
+ const c = l;
1949
+ if (c != null && c.response)
1950
+ return c.response;
1951
+ throw l;
1952
+ }
1953
+ }, V = new WeakSet(), fe = function(t, r) {
1954
+ let s = removePathPrefix(r, u(this, m));
1955
+ s = applyRewriteRules(s, this.rewriteRules), s.includes(".php") ? s = s.split(".php")[0] + ".php" : t.isDir(`${u(this, y)}${s}`) ? (s.endsWith("/") || (s = `${s}/`), s = `${s}index.php`) : s = "/index.php";
1956
+ const n = `${u(this, y)}${s}`;
1957
+ if (t.fileExists(n))
1958
+ return n;
1959
+ throw new Error(`File not found: ${n}`);
1960
+ };
1961
+ function inferMimeType(e) {
1962
+ switch (e.split(".").pop()) {
1963
+ case "css":
1964
+ return "text/css";
1965
+ case "js":
1966
+ return "application/javascript";
1967
+ case "png":
1968
+ return "image/png";
1969
+ case "jpg":
1970
+ case "jpeg":
1971
+ return "image/jpeg";
1972
+ case "gif":
1973
+ return "image/gif";
1974
+ case "svg":
1975
+ return "image/svg+xml";
1976
+ case "woff":
1977
+ return "font/woff";
1978
+ case "woff2":
1979
+ return "font/woff2";
1980
+ case "ttf":
1981
+ return "font/ttf";
1982
+ case "otf":
1983
+ return "font/otf";
1984
+ case "eot":
1985
+ return "font/eot";
1986
+ case "ico":
1987
+ return "image/x-icon";
1988
+ case "html":
1989
+ return "text/html";
1990
+ case "json":
1991
+ return "application/json";
1992
+ case "xml":
1993
+ return "application/xml";
1994
+ case "txt":
1995
+ case "md":
1996
+ return "text/plain";
1997
+ case "pdf":
1998
+ return "application/pdf";
1999
+ case "webp":
2000
+ return "image/webp";
2001
+ case "mp3":
2002
+ return "audio/mpeg";
2003
+ case "mp4":
2004
+ return "video/mp4";
2005
+ case "csv":
2006
+ return "text/csv";
2007
+ case "xls":
2008
+ return "application/vnd.ms-excel";
2009
+ case "xlsx":
2010
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
2011
+ case "doc":
2012
+ return "application/msword";
2013
+ case "docx":
2014
+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
2015
+ case "ppt":
2016
+ return "application/vnd.ms-powerpoint";
2017
+ case "pptx":
2018
+ return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
2019
+ case "zip":
2020
+ return "application/zip";
2021
+ case "rar":
2022
+ return "application/x-rar-compressed";
2023
+ case "tar":
2024
+ return "application/x-tar";
2025
+ case "gz":
2026
+ return "application/gzip";
2027
+ case "7z":
2028
+ return "application/x-7z-compressed";
2029
+ default:
2030
+ return "application-octet-stream";
1710
2031
  }
1711
- t.mkdirTree(r);
1712
- const n = e.readdir(r).filter((o) => o !== "." && o !== "..");
1713
- for (const o of n)
1714
- copyFS(e, t, joinPaths(r, o));
1715
2032
  }
1716
- function isLocalPHP(e) {
1717
- return !(e instanceof BasePHP);
2033
+ function seemsLikeAPHPRequestHandlerPath(e) {
2034
+ return seemsLikeAPHPFile(e) || seemsLikeADirectoryRoot(e);
1718
2035
  }
1719
- function isRemotePHP(e) {
1720
- return !isLocalPHP(e);
2036
+ function seemsLikeAPHPFile(e) {
2037
+ return e.endsWith(".php") || e.includes(".php/");
2038
+ }
2039
+ function seemsLikeADirectoryRoot(e) {
2040
+ return !e.split("/").pop().includes(".");
2041
+ }
2042
+ function applyRewriteRules(e, t) {
2043
+ for (const r of t)
2044
+ if (new RegExp(r.match).test(e))
2045
+ return e.replace(r.match, r.replacement);
2046
+ return e;
1721
2047
  }
1722
2048
  function rotatePHPRuntime({
1723
2049
  php: e,
@@ -1734,32 +2060,34 @@ function rotatePHPRuntime({
1734
2060
  maxRequests: s = 400
1735
2061
  }) {
1736
2062
  let n = 0;
1737
- async function o() {
2063
+ async function i() {
1738
2064
  if (++n < s)
1739
2065
  return;
1740
2066
  n = 0;
1741
- const i = await e.semaphore.acquire();
2067
+ const o = await e.semaphore.acquire();
1742
2068
  try {
1743
2069
  e.hotSwapPHPRuntime(await r(), t);
1744
2070
  } finally {
1745
- i();
2071
+ o();
1746
2072
  }
1747
2073
  }
1748
- return e.addEventListener("request.end", o), function() {
1749
- e.removeEventListener("request.end", o);
2074
+ return e.addEventListener("request.end", i), function() {
2075
+ e.removeEventListener("request.end", i);
1750
2076
  };
1751
2077
  }
1752
2078
  async function writeFiles(e, t, r, { rmRoot: s = !1 } = {}) {
1753
2079
  s && await e.isDir(t) && await e.rmdir(t, { recursive: !0 });
1754
- for (const [n, o] of Object.entries(r)) {
1755
- const i = joinPaths(t, n);
1756
- await e.fileExists(dirname(i)) || await e.mkdir(dirname(i)), await e.writeFile(i, o);
2080
+ for (const [n, i] of Object.entries(r)) {
2081
+ const o = joinPaths(t, n);
2082
+ await e.fileExists(dirname(o)) || await e.mkdir(dirname(o)), await e.writeFile(o, i);
1757
2083
  }
1758
2084
  }
1759
2085
  export {
1760
2086
  BasePHP,
1761
2087
  DEFAULT_BASE_URL,
2088
+ HttpCookieStore,
1762
2089
  LatestSupportedPHPVersion,
2090
+ PHPProcessManager,
1763
2091
  PHPRequestHandler,
1764
2092
  PHPResponse,
1765
2093
  SupportedPHPExtensionBundles,