@php-wasm/universal 1.1.1 → 1.1.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
@@ -1,8 +1,8 @@
1
- var C = (t) => {
1
+ var D = (t) => {
2
2
  throw TypeError(t);
3
3
  };
4
- var I = (t, e, r) => e.has(t) || C("Cannot " + r);
5
- var c = (t, e, r) => (I(t, e, "read from private field"), r ? r.call(t) : e.get(t)), h = (t, e, r) => e.has(t) ? C("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(t) : e.set(t, r), m = (t, e, r, s) => (I(t, e, "write to private field"), s ? s.call(t, r) : e.set(t, r), r), d = (t, e, r) => (I(t, e, "access private method"), r);
4
+ var U = (t, e, r) => e.has(t) || D("Cannot " + r);
5
+ var c = (t, e, r) => (U(t, e, "read from private field"), r ? r.call(t) : e.get(t)), h = (t, e, r) => e.has(t) ? D("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(t) : e.set(t, r), m = (t, e, r, s) => (U(t, e, "write to private field"), s ? s.call(t, r) : e.set(t, r), r), d = (t, e, r) => (U(t, e, "access private method"), r);
6
6
  import "@php-wasm/node-polyfills";
7
7
  import { logger } from "@php-wasm/logger";
8
8
  import { dirname, joinPaths, Semaphore, createSpawnHandler, normalizePath, AcquireTimeoutError } from "@php-wasm/util";
@@ -98,10 +98,10 @@ function rethrowFileSystemError(t = "") {
98
98
  try {
99
99
  return r.apply(this, s);
100
100
  } catch (i) {
101
- const o = typeof i == "object" ? i == null ? void 0 : i.errno : null;
102
- if (o in FileErrorCodes) {
103
- const n = FileErrorCodes[o], l = typeof s[1] == "string" ? s[1] : null, a = l !== null ? t.replaceAll("{path}", l) : t;
104
- throw new Error(`${a}: ${n}`, {
101
+ const n = typeof i == "object" ? i == null ? void 0 : i.errno : null;
102
+ if (n in FileErrorCodes) {
103
+ const o = FileErrorCodes[n], a = typeof s[1] == "string" ? s[1] : null, l = a !== null ? t.replaceAll("{path}", a) : t;
104
+ throw new Error(`${l}: ${o}`, {
105
105
  cause: i
106
106
  });
107
107
  }
@@ -164,12 +164,12 @@ class FSHelpers {
164
164
  */
165
165
  static mv(e, r, s) {
166
166
  try {
167
- const i = e.lookupPath(r).node.mount, o = FSHelpers.fileExists(e, s) ? e.lookupPath(s).node.mount : e.lookupPath(dirname(s)).node.mount;
168
- i.mountpoint !== o.mountpoint ? (FSHelpers.copyRecursive(e, r, s), FSHelpers.isDir(e, r) ? FSHelpers.rmdir(e, r, { recursive: !0 }) : e.unlink(r)) : e.rename(r, s);
167
+ const i = e.lookupPath(r).node.mount, n = FSHelpers.fileExists(e, s) ? e.lookupPath(s).node.mount : e.lookupPath(dirname(s)).node.mount;
168
+ i.mountpoint !== n.mountpoint ? (FSHelpers.copyRecursive(e, r, s), FSHelpers.isDir(e, r) ? FSHelpers.rmdir(e, r, { recursive: !0 }) : e.unlink(r)) : e.rename(r, s);
169
169
  } catch (i) {
170
- const o = getEmscriptenFsError(i);
171
- throw o ? new Error(
172
- `Could not move ${r} to ${s}: ${o}`,
170
+ const n = getEmscriptenFsError(i);
171
+ throw n ? new Error(
172
+ `Could not move ${r} to ${s}: ${n}`,
173
173
  {
174
174
  cause: i
175
175
  }
@@ -185,9 +185,9 @@ class FSHelpers {
185
185
  */
186
186
  static rmdir(e, r, s = { recursive: !0 }) {
187
187
  s != null && s.recursive && FSHelpers.listFiles(e, r).forEach((i) => {
188
- const o = `${r}/${i}`;
189
- FSHelpers.isDir(e, o) ? FSHelpers.rmdir(e, o, s) : FSHelpers.unlink(e, o);
190
- }), e.rmdir(r);
188
+ const n = `${r}/${i}`;
189
+ FSHelpers.isDir(e, n) ? FSHelpers.rmdir(e, n, s) : FSHelpers.unlink(e, n);
190
+ }), e.getPath(e.lookupPath(r).node) === e.cwd() && e.chdir(joinPaths(e.cwd(), "..")), e.rmdir(r);
191
191
  }
192
192
  /**
193
193
  * Lists the files and directories in the given directory.
@@ -202,11 +202,11 @@ class FSHelpers {
202
202
  return [];
203
203
  try {
204
204
  const i = e.readdir(r).filter(
205
- (o) => o !== "." && o !== ".."
205
+ (n) => n !== "." && n !== ".."
206
206
  );
207
207
  if (s.prependPath) {
208
- const o = r.replace(/\/$/, "");
209
- return i.map((n) => `${o}/${n}`);
208
+ const n = r.replace(/\/$/, "");
209
+ return i.map((o) => `${n}/${o}`);
210
210
  }
211
211
  return i;
212
212
  } catch (i) {
@@ -302,14 +302,14 @@ class FSHelpers {
302
302
  const i = e.lookupPath(r).node;
303
303
  if (e.isDir(i.mode)) {
304
304
  e.mkdirTree(s);
305
- const o = e.readdir(r).filter(
306
- (n) => n !== "." && n !== ".."
305
+ const n = e.readdir(r).filter(
306
+ (o) => o !== "." && o !== ".."
307
307
  );
308
- for (const n of o)
308
+ for (const o of n)
309
309
  FSHelpers.copyRecursive(
310
310
  e,
311
- joinPaths(r, n),
312
- joinPaths(s, n)
311
+ joinPaths(r, o),
312
+ joinPaths(s, o)
313
313
  );
314
314
  } else
315
315
  e.writeFile(s, e.readFile(r));
@@ -497,9 +497,100 @@ const responseTexts = {
497
497
  201: "Created",
498
498
  200: "OK"
499
499
  };
500
+ class StreamedPHPResponse {
501
+ constructor(e, r, s, i) {
502
+ this.parsedHeaders = null, this.cachedStdoutText = null, this.cachedStderrText = null, this.headersStream = e, this.stdout = r, this.stderr = s, this.exitCode = i;
503
+ }
504
+ /**
505
+ * True if the response is successful (HTTP status code 200-399),
506
+ * false otherwise.
507
+ */
508
+ async ok() {
509
+ try {
510
+ const e = await this.httpStatusCode;
511
+ return e >= 200 && e < 400;
512
+ } catch {
513
+ return !1;
514
+ }
515
+ }
516
+ /**
517
+ * Resolves when the response has finished processing – either successfully or not.
518
+ */
519
+ get finished() {
520
+ return Promise.allSettled([this.exitCode.finally(() => {
521
+ })]).then(
522
+ () => {
523
+ }
524
+ );
525
+ }
526
+ /**
527
+ * Resolves once HTTP headers are available.
528
+ */
529
+ get headers() {
530
+ return this.getParsedHeaders().then((e) => e.headers);
531
+ }
532
+ /**
533
+ * Resolves once HTTP status code is available.
534
+ */
535
+ get httpStatusCode() {
536
+ return Promise.race([
537
+ this.getParsedHeaders().then((e) => e.httpStatusCode),
538
+ this.exitCode.then(
539
+ (e) => e !== 0 ? 500 : void 0
540
+ )
541
+ ]).then((e) => e !== void 0 ? e : this.getParsedHeaders().then(
542
+ (r) => r.httpStatusCode,
543
+ () => 200
544
+ )).catch(() => 500);
545
+ }
546
+ /**
547
+ * Exposes the stdout bytes as they're produced by the PHP instance
548
+ */
549
+ get stdoutText() {
550
+ return this.cachedStdoutText || (this.cachedStdoutText = streamToText(this.stdout)), this.cachedStdoutText;
551
+ }
552
+ /**
553
+ * Exposes the stderr bytes as they're produced by the PHP instance
554
+ */
555
+ get stderrText() {
556
+ return this.cachedStderrText || (this.cachedStderrText = streamToText(this.stderr)), this.cachedStderrText;
557
+ }
558
+ async getParsedHeaders() {
559
+ return this.parsedHeaders || (this.parsedHeaders = parseHeadersStream(this.headersStream)), await this.parsedHeaders;
560
+ }
561
+ }
562
+ async function parseHeadersStream(t) {
563
+ const e = await streamToText(t);
564
+ let r;
565
+ try {
566
+ r = JSON.parse(e);
567
+ } catch {
568
+ return { headers: {}, httpStatusCode: 200 };
569
+ }
570
+ const s = {};
571
+ for (const i of r.headers) {
572
+ if (!i.includes(": "))
573
+ continue;
574
+ const n = i.indexOf(": "), o = i.substring(0, n).toLowerCase(), a = i.substring(n + 2);
575
+ o in s || (s[o] = []), s[o].push(a);
576
+ }
577
+ return {
578
+ headers: s,
579
+ httpStatusCode: r.status
580
+ };
581
+ }
582
+ async function streamToText(t) {
583
+ const e = t.pipeThrough(new TextDecoderStream()).getReader(), r = [];
584
+ for (; ; ) {
585
+ const { done: s, value: i } = await e.read();
586
+ if (s)
587
+ return r.join("");
588
+ i && r.push(i);
589
+ }
590
+ }
500
591
  class PHPResponse {
501
- constructor(e, r, s, i = "", o = 0) {
502
- this.httpStatusCode = e, this.headers = r, this.bytes = s, this.exitCode = o, this.errors = i;
592
+ constructor(e, r, s, i = "", n = 0) {
593
+ this.httpStatusCode = e, this.headers = r, this.bytes = s, this.exitCode = n, this.errors = i;
503
594
  }
504
595
  static forHttpCode(e, r = "") {
505
596
  return new PHPResponse(
@@ -519,6 +610,15 @@ class PHPResponse {
519
610
  e.exitCode
520
611
  );
521
612
  }
613
+ static async fromStreamedResponse(e) {
614
+ return await e.finished, new PHPResponse(
615
+ await e.httpStatusCode,
616
+ await e.headers,
617
+ new TextEncoder().encode(await e.stdoutText),
618
+ await e.stderrText,
619
+ await e.exitCode
620
+ );
621
+ }
522
622
  toRawData() {
523
623
  return {
524
624
  headers: this.headers,
@@ -544,26 +644,26 @@ class PHPResponse {
544
644
  const RuntimeId = Symbol("RuntimeId"), loadedRuntimes = /* @__PURE__ */ new Map();
545
645
  let lastRuntimeId = 0;
546
646
  async function loadPHPRuntime(t, ...e) {
547
- const r = Object.assign({}, ...e), [s, i, o] = makePromise(), n = t.init(currentJsRuntime, {
548
- onAbort(a) {
549
- o(a), logger.error(a);
647
+ const r = Object.assign({}, ...e), [s, i, n] = makePromise(), o = t.init(currentJsRuntime, {
648
+ onAbort(l) {
649
+ n(l), logger.error(l);
550
650
  },
551
651
  ENV: {},
552
652
  // Emscripten sometimes prepends a '/' to the path, which
553
653
  // breaks vite dev mode. An identity `locateFile` function
554
654
  // fixes it.
555
- locateFile: (a) => a,
655
+ locateFile: (l) => l,
556
656
  ...r,
557
657
  noInitialRun: !0,
558
658
  onRuntimeInitialized() {
559
- r.onRuntimeInitialized && r.onRuntimeInitialized(n), i();
659
+ r.onRuntimeInitialized && r.onRuntimeInitialized(o), i();
560
660
  }
561
661
  });
562
662
  await s;
563
- const l = ++lastRuntimeId;
564
- return n.FS, n.id = l, n.originalExit = n._exit, n._exit = function(a) {
565
- return n.outboundNetworkProxyServer && (n.outboundNetworkProxyServer.close(), n.outboundNetworkProxyServer.closeAllConnections()), loadedRuntimes.delete(l), n.originalExit(a);
566
- }, n[RuntimeId] = l, loadedRuntimes.set(l, n), l;
663
+ const a = ++lastRuntimeId;
664
+ return o.FS, o.id = a, o.originalExit = o._exit, o._exit = function(l) {
665
+ return o.outboundNetworkProxyServer && (o.outboundNetworkProxyServer.close(), o.outboundNetworkProxyServer.closeAllConnections()), loadedRuntimes.delete(a), o.originalExit(l);
666
+ }, o[RuntimeId] = a, loadedRuntimes.set(a, o), a;
567
667
  }
568
668
  function getLoadedRuntime(t) {
569
669
  return loadedRuntimes.get(t);
@@ -600,18 +700,26 @@ class ErrorEvent2 extends (_a = Event, _a) {
600
700
  Object.defineProperty(ErrorEvent2.prototype, "error", { enumerable: !0 });
601
701
  Object.defineProperty(ErrorEvent2.prototype, "message", { enumerable: !0 });
602
702
  const ErrorEvent = typeof globalThis.ErrorEvent == "function" ? globalThis.ErrorEvent : ErrorEvent2;
603
- function isExitCodeZero(t) {
604
- return t instanceof Error ? "exitCode" in t && (t == null ? void 0 : t.exitCode) === 0 || (t == null ? void 0 : t.name) === "ExitStatus" && "status" in t && t.status === 0 : !1;
703
+ function isExitCode(t) {
704
+ return t instanceof Error ? "exitCode" in t || (t == null ? void 0 : t.name) === "ExitStatus" && "status" in t : !1;
605
705
  }
606
706
  class UnhandledRejectionsTarget extends EventTarget {
607
707
  constructor() {
608
708
  super(...arguments), this.listenersCount = 0;
609
709
  }
610
- addEventListener(e, r) {
611
- ++this.listenersCount, super.addEventListener(e, r);
710
+ addEventListener(e, r, s) {
711
+ ++this.listenersCount, super.addEventListener(
712
+ e,
713
+ r,
714
+ s
715
+ );
612
716
  }
613
- removeEventListener(e, r) {
614
- --this.listenersCount, super.removeEventListener(e, r);
717
+ removeEventListener(e, r, s) {
718
+ --this.listenersCount, super.removeEventListener(
719
+ e,
720
+ r,
721
+ s
722
+ );
615
723
  }
616
724
  hasListeners() {
617
725
  return this.listenersCount > 0;
@@ -623,26 +731,24 @@ function improveWASMErrorReporting(t) {
623
731
  if (typeof t.wasmExports[r] == "function") {
624
732
  const s = t.wasmExports[r];
625
733
  t.wasmExports[r] = function(...i) {
626
- var o;
734
+ var n;
627
735
  try {
628
736
  return s(...i);
629
- } catch (n) {
630
- if (!(n instanceof Error))
631
- throw n;
632
- const l = clarifyErrorMessage(
633
- n,
634
- (o = t.lastAsyncifyStackSource) == null ? void 0 : o.stack
737
+ } catch (o) {
738
+ if (!(o instanceof Error))
739
+ throw o;
740
+ const a = clarifyErrorMessage(
741
+ o,
742
+ (n = t.lastAsyncifyStackSource) == null ? void 0 : n.stack
635
743
  );
636
- if (t.lastAsyncifyStackSource && (n.cause = t.lastAsyncifyStackSource), e.hasListeners()) {
637
- e.dispatchEvent(
638
- new ErrorEvent("error", {
639
- error: n,
640
- message: l
641
- })
642
- );
643
- return;
744
+ if (t.lastAsyncifyStackSource && (o.cause = t.lastAsyncifyStackSource), e.hasListeners()) {
745
+ const l = new ErrorEvent("error", {
746
+ error: o,
747
+ message: a
748
+ });
749
+ throw e.dispatchEvent(l), o;
644
750
  }
645
- throw isExitCodeZero(n) || showCriticalErrorBox(l), n;
751
+ throw (!isExitCode(o) || o.exitCode !== 0) && showCriticalErrorBox(a), o;
646
752
  }
647
753
  };
648
754
  }
@@ -731,7 +837,7 @@ class PHPExecutionFailureError extends Error {
731
837
  }
732
838
  }
733
839
  const PHP_INI_PATH = "/internal/shared/php.ini", AUTO_PREPEND_SCRIPT = "/internal/shared/auto_prepend_file.php";
734
- var F, P, v, _, x, R, u, N, M, j, U, L, q, $, B, O, D, A, z, W, G;
840
+ var b, f, E, P, R, T, p, z, q, W, G, V, J, Y, K, X, L, Q, $, B;
735
841
  class PHP {
736
842
  /**
737
843
  * Initializes a PHP runtime.
@@ -741,13 +847,13 @@ class PHP {
741
847
  * @param requestHandlerOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
742
848
  */
743
849
  constructor(t) {
744
- h(this, u);
745
- h(this, F);
746
- h(this, P, !1);
747
- h(this, v, null);
748
- h(this, _, /* @__PURE__ */ new Map());
749
- h(this, x, []);
750
- h(this, R, {});
850
+ h(this, p);
851
+ h(this, b);
852
+ h(this, f, !1);
853
+ h(this, E, null);
854
+ h(this, P, /* @__PURE__ */ new Map());
855
+ h(this, R, []);
856
+ h(this, T, {});
751
857
  this.semaphore = new Semaphore({ concurrency: 1 }), t !== void 0 && this.initializeRuntime(t);
752
858
  }
753
859
  /**
@@ -756,7 +862,7 @@ class PHP {
756
862
  * @param listener - The listener function to be called when the event is triggered.
757
863
  */
758
864
  addEventListener(t, e) {
759
- c(this, _).has(t) || c(this, _).set(t, /* @__PURE__ */ new Set()), c(this, _).get(t).add(e);
865
+ c(this, P).has(t) || c(this, P).set(t, /* @__PURE__ */ new Set()), c(this, P).get(t).add(e);
760
866
  }
761
867
  /**
762
868
  * Removes an event listener for a PHP event.
@@ -765,10 +871,10 @@ class PHP {
765
871
  */
766
872
  removeEventListener(t, e) {
767
873
  var r;
768
- (r = c(this, _).get(t)) == null || r.delete(e);
874
+ (r = c(this, P).get(t)) == null || r.delete(e);
769
875
  }
770
876
  dispatchEvent(t) {
771
- const e = c(this, _).get(t.type);
877
+ const e = c(this, P).get(t.type);
772
878
  if (e)
773
879
  for (const r of e)
774
880
  r(t);
@@ -813,8 +919,8 @@ class PHP {
813
919
  * @param listener Callback function to handle the message.
814
920
  */
815
921
  onMessage(t) {
816
- return c(this, x).push(t), async () => {
817
- m(this, x, c(this, x).filter(
922
+ return c(this, R).push(t), async () => {
923
+ m(this, R, c(this, R).filter(
818
924
  (e) => e !== t
819
925
  ));
820
926
  };
@@ -890,13 +996,13 @@ class PHP {
890
996
  }
891
997
  `
892
998
  ), e.onMessage = async (r) => {
893
- for (const s of c(this, x)) {
999
+ for (const s of c(this, R)) {
894
1000
  const i = await s(r);
895
1001
  if (i)
896
1002
  return i;
897
1003
  }
898
1004
  return "";
899
- }, m(this, v, improveWASMErrorReporting(e)), this.dispatchEvent({
1005
+ }, m(this, E, improveWASMErrorReporting(e)), this.dispatchEvent({
900
1006
  type: "runtime.initialized"
901
1007
  });
902
1008
  }
@@ -911,7 +1017,7 @@ class PHP {
911
1017
  throw new Error(
912
1018
  "Could not set SAPI name. This can only be done before the PHP WASM module is initialized.Did you already dispatch any requests?"
913
1019
  );
914
- m(this, F, t);
1020
+ m(this, b, t);
915
1021
  }
916
1022
  /**
917
1023
  * Changes the current working directory in the PHP filesystem.
@@ -995,66 +1101,179 @@ class PHP {
995
1101
  *
996
1102
  * @example
997
1103
  * ```js
998
- * const result = await php.run(`<?php
999
- * $fp = fopen('php://stderr', 'w');
1000
- * fwrite($fp, "Hello, world!");
1001
- * `);
1104
+ * const result = await php.run({
1105
+ * code: `<?php
1106
+ * $fp = fopen('php://stderr', 'w');
1107
+ * fwrite($fp, "Hello, world!");
1108
+ * `
1109
+ * });
1002
1110
  * // result.errors === "Hello, world!"
1003
1111
  * ```
1004
1112
  *
1005
- * @param options - PHP runtime options.
1113
+ * @deprecated Use stream() instead.
1114
+ * @param request - PHP runtime options.
1006
1115
  */
1007
1116
  async run(t) {
1117
+ const e = await this.runStream(t), r = await PHPResponse.fromStreamedResponse(
1118
+ e
1119
+ );
1120
+ if (r.exitCode !== 0) {
1121
+ logger.warn("PHP.run() output was:", r.text);
1122
+ const s = new PHPExecutionFailureError(
1123
+ `PHP.run() failed with exit code ${r.exitCode} and the following output: ` + r.errors + `
1124
+
1125
+ ` + r.text,
1126
+ r,
1127
+ "request"
1128
+ );
1129
+ throw logger.error(s), this.dispatchEvent({
1130
+ type: "request.error",
1131
+ error: new Error(
1132
+ "PHP.run() failed with exit code " + r.exitCode
1133
+ ),
1134
+ // Distinguish between PHP request and PHP-wasm errors
1135
+ source: "request"
1136
+ }), s;
1137
+ }
1138
+ return r;
1139
+ }
1140
+ /**
1141
+ * Runs PHP code and returns a StreamedPHPResponse object that can be used to
1142
+ * process the output incrementally.
1143
+ *
1144
+ * This low-level method directly interacts with the WebAssembly
1145
+ * PHP interpreter and provides streaming capabilities for processing
1146
+ * PHP output as it becomes available.
1147
+ *
1148
+ * Every time you call stream(), it prepares the PHP
1149
+ * environment and:
1150
+ *
1151
+ * * Resets the internal PHP state
1152
+ * * Populates superglobals ($_SERVER, $_GET, etc.)
1153
+ * * Handles file uploads
1154
+ * * Populates input streams (stdin, argv, etc.)
1155
+ * * Sets the current working directory
1156
+ *
1157
+ * You can use stream() in two primary modes:
1158
+ *
1159
+ * ### Code snippet mode
1160
+ *
1161
+ * In this mode, you pass a string containing PHP code to run.
1162
+ *
1163
+ * ```ts
1164
+ * const streamedResponse = await php.stream({
1165
+ * code: `<?php echo "Hello world!";`
1166
+ * });
1167
+ * // Process output incrementally
1168
+ * for await (const chunk of streamedResponse.text) {
1169
+ * console.log(chunk);
1170
+ * }
1171
+ * ```
1172
+ *
1173
+ * In this mode, information like __DIR__ or __FILE__ isn't very
1174
+ * useful because the code is not associated with any file.
1175
+ *
1176
+ * Under the hood, the PHP snippet is passed to the `zend_eval_string`
1177
+ * C function.
1178
+ *
1179
+ * ### File mode
1180
+ *
1181
+ * In the file mode, you pass a scriptPath and PHP executes a file
1182
+ * found at that path:
1183
+ *
1184
+ * ```ts
1185
+ * php.writeFile(
1186
+ * "/www/index.php",
1187
+ * `<?php echo "Hello world!";"`
1188
+ * );
1189
+ * const streamedResponse = await php.stream({
1190
+ * scriptPath: "/www/index.php"
1191
+ * });
1192
+ * // Process output incrementally
1193
+ * for await (const chunk of streamedResponse.text) {
1194
+ * console.log(chunk);
1195
+ * }
1196
+ * ```
1197
+ *
1198
+ * In this mode, you can rely on path-related information like __DIR__
1199
+ * or __FILE__.
1200
+ *
1201
+ * Under the hood, the PHP file is executed with the `php_execute_script`
1202
+ * C function.
1203
+ *
1204
+ * The `stream()` method cannot be used in conjunction with `cli()`.
1205
+ *
1206
+ * @example
1207
+ * ```js
1208
+ * const streamedResponse = await php.stream({
1209
+ * code: `<?php
1210
+ * for ($i = 0; $i < 5; $i++) {
1211
+ * echo "Line $i\n";
1212
+ * flush();
1213
+ * }
1214
+ * `
1215
+ * });
1216
+ *
1217
+ * // Process output as it becomes available
1218
+ * for await (const chunk of streamedResponse.text) {
1219
+ * console.log('Received:', chunk);
1220
+ * }
1221
+ *
1222
+ * // Get the final exit code
1223
+ * const exitCode = await streamedResponse.exitCode;
1224
+ * console.log('Exit code:', exitCode);
1225
+ * ```
1226
+ *
1227
+ * @see run() – a synchronous version of this method.
1228
+ * @param request - PHP runtime options.
1229
+ * @returns A StreamedPHPResponse object.
1230
+ */
1231
+ async runStream(t) {
1008
1232
  const e = await this.semaphore.acquire();
1009
1233
  let r;
1010
- try {
1011
- if (c(this, P) || (d(this, u, M).call(this), m(this, P, !0)), t.scriptPath && !this.fileExists(t.scriptPath))
1234
+ const s = d(this, p, B).call(this, () => {
1235
+ if (c(this, f) || (d(this, p, q).call(this), m(this, f, !0)), t.scriptPath && !this.fileExists(t.scriptPath))
1012
1236
  throw new Error(
1013
1237
  `The script path "${t.scriptPath}" does not exist.`
1014
1238
  );
1015
- d(this, u, U).call(this, t.relativeUri || ""), d(this, u, B).call(this, t.method || "GET");
1016
- const s = normalizeHeaders(t.headers || {}), i = s.host || "example.com:443", o = d(this, u, $).call(this, i, t.protocol || "http");
1017
- if (d(this, u, L).call(this, i), d(this, u, q).call(this, o), d(this, u, O).call(this, s), t.body && (r = d(this, u, D).call(this, t.body)), typeof t.code == "string")
1018
- this.writeFile("/internal/eval.php", t.code), d(this, u, A).call(this, "/internal/eval.php");
1239
+ d(this, p, W).call(this, t.relativeUri || ""), d(this, p, Y).call(this, t.method || "GET");
1240
+ const i = normalizeHeaders(t.headers || {}), n = i.host || "example.com:443", o = d(this, p, J).call(this, n, t.protocol || "http");
1241
+ if (d(this, p, G).call(this, n), d(this, p, V).call(this, o), d(this, p, K).call(this, i), t.body && (r = d(this, p, X).call(this, t.body)), typeof t.code == "string")
1242
+ this.writeFile("/internal/eval.php", t.code), d(this, p, L).call(this, "/internal/eval.php");
1019
1243
  else if (typeof t.scriptPath == "string")
1020
- d(this, u, A).call(this, t.scriptPath || "");
1244
+ d(this, p, L).call(this, t.scriptPath || "");
1021
1245
  else
1022
1246
  throw new TypeError(
1023
1247
  "The request object must have either a `code` or a `scriptPath` property."
1024
1248
  );
1025
- const n = d(this, u, N).call(this, t.$_SERVER, s, o);
1026
- for (const p in n)
1027
- d(this, u, z).call(this, p, n[p]);
1249
+ const a = d(this, p, z).call(this, t.$_SERVER, i, o);
1250
+ for (const u in a)
1251
+ d(this, p, Q).call(this, u, a[u]);
1028
1252
  const l = t.env || {};
1029
- for (const p in l)
1030
- d(this, u, W).call(this, p, l[p]);
1031
- const a = await d(this, u, G).call(this);
1032
- if (a.exitCode !== 0) {
1033
- logger.warn("PHP.run() output was:", a.text);
1034
- const p = new PHPExecutionFailureError(
1035
- `PHP.run() failed with exit code ${a.exitCode} and the following output: ` + a.errors,
1036
- a,
1037
- "request"
1038
- );
1039
- throw logger.error(p), p;
1040
- }
1041
- return a;
1042
- } catch (s) {
1043
- throw this.dispatchEvent({
1253
+ for (const u in l)
1254
+ d(this, p, $).call(this, u, l[u]);
1255
+ return c(this, f) || (d(this, p, q).call(this), m(this, f, !0)), this[__private__dont__use].ccall(
1256
+ "wasm_sapi_handle_request",
1257
+ NUMBER,
1258
+ [],
1259
+ [],
1260
+ { async: !0 }
1261
+ );
1262
+ });
1263
+ return await s.catch((i) => {
1264
+ this.dispatchEvent({
1044
1265
  type: "request.error",
1045
- error: s,
1266
+ error: i,
1046
1267
  // Distinguish between PHP request and PHP-wasm errors
1047
- source: s.source ?? "php-wasm"
1048
- }), s;
1049
- } finally {
1050
- try {
1051
- r && this[__private__dont__use].free(r);
1052
- } finally {
1053
- e(), this.dispatchEvent({
1054
- type: "request.end"
1055
- });
1056
- }
1057
- }
1268
+ source: i.source ?? "php-wasm"
1269
+ });
1270
+ }).finally(() => {
1271
+ r && this[__private__dont__use].free(r);
1272
+ }).finally(() => {
1273
+ e(), this.dispatchEvent({
1274
+ type: "request.end"
1275
+ });
1276
+ }), s;
1058
1277
  }
1059
1278
  /**
1060
1279
  * Defines a constant in the PHP runtime.
@@ -1238,15 +1457,15 @@ class PHP {
1238
1457
  */
1239
1458
  async hotSwapPHPRuntime(t, e) {
1240
1459
  const r = this[__private__dont__use].FS, s = [];
1241
- for (const [i, o] of Object.entries(c(this, R)))
1242
- s.push({ mountHandler: o.mountHandler, vfsPath: i }), await o.unmount();
1460
+ for (const [i, n] of Object.entries(c(this, T)))
1461
+ s.push({ mountHandler: n.mountHandler, vfsPath: i }), await n.unmount();
1243
1462
  try {
1244
1463
  this.exit();
1245
1464
  } catch {
1246
1465
  }
1247
- this.initializeRuntime(t), c(this, F) && this.setSapiName(c(this, F)), copyFS(r, this[__private__dont__use].FS, "/internal"), e && copyFS(r, this[__private__dont__use].FS, e);
1248
- for (const { mountHandler: i, vfsPath: o } of s)
1249
- this.mkdir(o), await this.mount(o, i);
1466
+ this.initializeRuntime(t), c(this, b) && this.setSapiName(c(this, b)), copyFS(r, this[__private__dont__use].FS, "/internal"), e && copyFS(r, this[__private__dont__use].FS, e);
1467
+ for (const { mountHandler: i, vfsPath: n } of s)
1468
+ this.mkdir(n), await this.mount(n, i);
1250
1469
  }
1251
1470
  /**
1252
1471
  * Mounts a filesystem to a given path in the PHP filesystem.
@@ -1263,10 +1482,10 @@ class PHP {
1263
1482
  ), s = {
1264
1483
  mountHandler: e,
1265
1484
  unmount: async () => {
1266
- await r(), delete c(this, R)[t];
1485
+ await r(), delete c(this, T)[t];
1267
1486
  }
1268
1487
  };
1269
- return c(this, R)[t] = s, () => {
1488
+ return c(this, T)[t] = s, () => {
1270
1489
  s.unmount();
1271
1490
  };
1272
1491
  }
@@ -1283,29 +1502,21 @@ class PHP {
1283
1502
  * @param argv - The arguments to pass to the CLI.
1284
1503
  * @returns The exit code of the CLI session.
1285
1504
  */
1286
- async cli(t) {
1287
- for (const e of t)
1505
+ async cli(t, e = {}) {
1506
+ const r = await this.semaphore.acquire(), s = e.env || {};
1507
+ for (const [i, n] of Object.entries(s))
1508
+ d(this, p, $).call(this, i, n);
1509
+ t = [t[0], "-c", PHP_INI_PATH, ...t.slice(1)];
1510
+ for (const i of t)
1288
1511
  this[__private__dont__use].ccall(
1289
1512
  "wasm_add_cli_arg",
1290
1513
  null,
1291
1514
  [STRING],
1292
- [e]
1293
- );
1294
- try {
1295
- return await this[__private__dont__use].ccall(
1296
- "run_cli",
1297
- null,
1298
- [],
1299
- [],
1300
- {
1301
- async: !0
1302
- }
1515
+ [i]
1303
1516
  );
1304
- } catch (e) {
1305
- if (isExitCodeZero(e))
1306
- return 0;
1307
- throw e;
1308
- }
1517
+ return await d(this, p, B).call(this, () => this[__private__dont__use].ccall("run_cli", null, [], [], {
1518
+ async: !0
1519
+ })).then((i) => (i.exitCode.finally(r), i));
1309
1520
  }
1310
1521
  setSkipShebang(t) {
1311
1522
  this[__private__dont__use].ccall(
@@ -1323,13 +1534,13 @@ class PHP {
1323
1534
  this[__private__dont__use]._exit(t);
1324
1535
  } catch {
1325
1536
  }
1326
- m(this, P, !1), m(this, v, null), delete this[__private__dont__use].onMessage, delete this[__private__dont__use];
1537
+ m(this, f, !1), m(this, E, null), delete this[__private__dont__use].onMessage, delete this[__private__dont__use];
1327
1538
  }
1328
1539
  [Symbol.dispose]() {
1329
- c(this, P) && this.exit(0);
1540
+ c(this, f) && this.exit(0);
1330
1541
  }
1331
1542
  }
1332
- F = new WeakMap(), P = new WeakMap(), v = new WeakMap(), _ = new WeakMap(), x = new WeakMap(), R = new WeakMap(), u = new WeakSet(), /**
1543
+ b = new WeakMap(), f = new WeakMap(), E = new WeakMap(), P = new WeakMap(), R = new WeakMap(), T = new WeakMap(), p = new WeakSet(), /**
1333
1544
  * Prepares the $_SERVER entries for the PHP runtime.
1334
1545
  *
1335
1546
  * @param defaults Default entries to include in $_SERVER.
@@ -1338,36 +1549,19 @@ F = new WeakMap(), P = new WeakMap(), v = new WeakMap(), _ = new WeakMap(), x =
1338
1549
  * was provided.
1339
1550
  * @returns Computed $_SERVER entries.
1340
1551
  */
1341
- N = function(t, e, r) {
1552
+ z = function(t, e, r) {
1342
1553
  const s = {
1343
1554
  ...t || {}
1344
1555
  };
1345
1556
  s.HTTPS = s.HTTPS || r === 443 ? "on" : "off";
1346
1557
  for (const i in e) {
1347
- let o = "HTTP_";
1348
- ["content-type", "content-length"].includes(i.toLowerCase()) && (o = ""), s[`${o}${i.toUpperCase().replace(/-/g, "_")}`] = e[i];
1558
+ let n = "HTTP_";
1559
+ ["content-type", "content-length"].includes(i.toLowerCase()) && (n = ""), s[`${n}${i.toUpperCase().replace(/-/g, "_")}`] = e[i];
1349
1560
  }
1350
1561
  return s;
1351
- }, M = function() {
1562
+ }, q = function() {
1352
1563
  this[__private__dont__use].ccall("php_wasm_init", null, [], []);
1353
- }, j = function() {
1354
- const t = "/internal/headers.json";
1355
- if (!this.fileExists(t))
1356
- throw new Error(
1357
- "SAPI Error: Could not find response headers file."
1358
- );
1359
- const e = JSON.parse(this.readFileAsText(t)), r = {};
1360
- for (const s of e.headers) {
1361
- if (!s.includes(": "))
1362
- continue;
1363
- const i = s.indexOf(": "), o = s.substring(0, i).toLowerCase(), n = s.substring(i + 2);
1364
- o in r || (r[o] = []), r[o].push(n);
1365
- }
1366
- return {
1367
- headers: r,
1368
- httpStatusCode: e.status
1369
- };
1370
- }, U = function(t) {
1564
+ }, W = function(t) {
1371
1565
  this[__private__dont__use].ccall(
1372
1566
  "wasm_set_request_uri",
1373
1567
  null,
@@ -1381,35 +1575,35 @@ N = function(t, e, r) {
1381
1575
  [STRING],
1382
1576
  [e]
1383
1577
  );
1384
- }, L = function(t) {
1578
+ }, G = function(t) {
1385
1579
  this[__private__dont__use].ccall(
1386
1580
  "wasm_set_request_host",
1387
1581
  null,
1388
1582
  [STRING],
1389
1583
  [t]
1390
1584
  );
1391
- }, q = function(t) {
1585
+ }, V = function(t) {
1392
1586
  this[__private__dont__use].ccall(
1393
1587
  "wasm_set_request_port",
1394
1588
  null,
1395
1589
  [NUMBER],
1396
1590
  [t]
1397
1591
  );
1398
- }, $ = function(t, e) {
1592
+ }, J = function(t, e) {
1399
1593
  let r;
1400
1594
  try {
1401
1595
  r = parseInt(new URL(t).port, 10);
1402
1596
  } catch {
1403
1597
  }
1404
1598
  return (!r || isNaN(r) || r === 80) && (r = e === "https" ? 443 : 80), r;
1405
- }, B = function(t) {
1599
+ }, Y = function(t) {
1406
1600
  this[__private__dont__use].ccall(
1407
1601
  "wasm_set_request_method",
1408
1602
  null,
1409
1603
  [STRING],
1410
1604
  [t]
1411
1605
  );
1412
- }, O = function(t) {
1606
+ }, K = function(t) {
1413
1607
  t.cookie && this[__private__dont__use].ccall(
1414
1608
  "wasm_set_cookies",
1415
1609
  null,
@@ -1426,7 +1620,7 @@ N = function(t, e, r) {
1426
1620
  [NUMBER],
1427
1621
  [parseInt(t["content-length"], 10)]
1428
1622
  );
1429
- }, D = function(t) {
1623
+ }, X = function(t) {
1430
1624
  let e, r;
1431
1625
  typeof t == "string" ? (logger.warn(
1432
1626
  "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"
@@ -1449,70 +1643,88 @@ N = function(t, e, r) {
1449
1643
  [NUMBER],
1450
1644
  [r]
1451
1645
  ), s;
1452
- }, A = function(t) {
1646
+ }, L = function(t) {
1453
1647
  this[__private__dont__use].ccall(
1454
1648
  "wasm_set_path_translated",
1455
1649
  null,
1456
1650
  [STRING],
1457
1651
  [t]
1458
1652
  );
1459
- }, z = function(t, e) {
1653
+ }, Q = function(t, e) {
1460
1654
  this[__private__dont__use].ccall(
1461
1655
  "wasm_add_SERVER_entry",
1462
1656
  null,
1463
1657
  [STRING, STRING],
1464
1658
  [t, e]
1465
1659
  );
1466
- }, W = function(t, e) {
1660
+ }, $ = function(t, e) {
1467
1661
  this[__private__dont__use].ccall(
1468
1662
  "wasm_add_ENV_entry",
1469
1663
  null,
1470
1664
  [STRING, STRING],
1471
1665
  [t, e]
1472
1666
  );
1473
- }, G = async function() {
1474
- var i;
1475
- let t, e;
1476
- try {
1477
- t = await new Promise((o, n) => {
1478
- var a;
1479
- e = (p) => {
1480
- logger.error(p), logger.error(p.error);
1481
- const T = new Error("Rethrown");
1482
- T.cause = p.error, T.betterMessage = p.message, n(T);
1483
- }, (a = c(this, v)) == null || a.addEventListener(
1667
+ }, B = async function(t) {
1668
+ const e = this[__private__dont__use], r = await createInvertedReadableStream();
1669
+ e.onHeaders = (w) => {
1670
+ a || s || r.controller.enqueue(w.slice());
1671
+ };
1672
+ let s = !1;
1673
+ const i = () => {
1674
+ s || (s = !0, r.controller.close());
1675
+ }, n = await createInvertedReadableStream();
1676
+ e.onStdout = (w) => {
1677
+ i(), !a && n.controller.enqueue(w.slice());
1678
+ };
1679
+ const o = await createInvertedReadableStream();
1680
+ e.onStderr = (w) => {
1681
+ a || o.controller.enqueue(w.slice());
1682
+ };
1683
+ let a = !1, l;
1684
+ const O = (async () => {
1685
+ var w;
1686
+ try {
1687
+ return await Promise.race([
1688
+ t(),
1689
+ new Promise((A, M) => {
1690
+ var F;
1691
+ l = (y) => {
1692
+ if (logger.error(y), logger.error(y.error), !isExitCode(y.error)) {
1693
+ const j = new Error("Rethrown");
1694
+ j.cause = y.error, j.betterMessage = y.message, M(j);
1695
+ }
1696
+ }, (F = c(this, E)) == null || F.addEventListener(
1697
+ "error",
1698
+ l,
1699
+ { once: !0 }
1700
+ );
1701
+ })
1702
+ ]);
1703
+ } catch (S) {
1704
+ if (isExitCode(S))
1705
+ return S.exitCode;
1706
+ n.controller.error(S), o.controller.error(S), r.controller.error(S), a = !0;
1707
+ for (const y in this)
1708
+ typeof this[y] == "function" && (this[y] = () => {
1709
+ throw new Error(
1710
+ "PHP runtime has crashed – see the earlier error for details."
1711
+ );
1712
+ });
1713
+ this.functionsMaybeMissingFromAsyncify = getFunctionsMaybeMissingFromAsyncify();
1714
+ const A = S, M = "betterMessage" in A ? A.betterMessage : A.message, F = new Error(M);
1715
+ throw F.cause = A, logger.error(F), F;
1716
+ } finally {
1717
+ a || (n.controller.close(), o.controller.close(), i(), a = !0), (w = c(this, E)) == null || w.removeEventListener(
1484
1718
  "error",
1485
- e
1486
- );
1487
- const l = this[__private__dont__use].ccall(
1488
- "wasm_sapi_handle_request",
1489
- NUMBER,
1490
- [],
1491
- [],
1492
- { async: !0 }
1719
+ l
1493
1720
  );
1494
- return l instanceof Promise ? l.then(o, n) : o(l);
1495
- });
1496
- } catch (o) {
1497
- for (const p in this)
1498
- typeof this[p] == "function" && (this[p] = () => {
1499
- throw new Error(
1500
- "PHP runtime has crashed – see the earlier error for details."
1501
- );
1502
- });
1503
- this.functionsMaybeMissingFromAsyncify = getFunctionsMaybeMissingFromAsyncify();
1504
- const n = o, l = "betterMessage" in n ? n.betterMessage : n.message, a = new Error(l);
1505
- throw a.cause = n, logger.error(a), a;
1506
- } finally {
1507
- (i = c(this, v)) == null || i.removeEventListener("error", e);
1508
- }
1509
- const { headers: r, httpStatusCode: s } = d(this, u, j).call(this);
1510
- return new PHPResponse(
1511
- t === 0 ? s : 500,
1512
- r,
1513
- this.readFileAsBuffer("/internal/stdout"),
1514
- this.readFileAsText("/internal/stderr"),
1515
- t
1721
+ }
1722
+ })();
1723
+ return new StreamedPHPResponse(
1724
+ r.stream,
1725
+ n.stream,
1726
+ o.stream,
1727
+ O
1516
1728
  );
1517
1729
  };
1518
1730
  function normalizeHeaders(t) {
@@ -1535,9 +1747,27 @@ function copyFS(t, e, r) {
1535
1747
  return;
1536
1748
  }
1537
1749
  e.mkdirTree(r);
1538
- const i = t.readdir(r).filter((o) => o !== "." && o !== "..");
1539
- for (const o of i)
1540
- copyFS(t, e, joinPaths(r, o));
1750
+ const i = t.readdir(r).filter((n) => n !== "." && n !== "..");
1751
+ for (const n of i)
1752
+ copyFS(t, e, joinPaths(r, n));
1753
+ }
1754
+ async function createInvertedReadableStream(t = {}) {
1755
+ let e;
1756
+ const r = new Promise(
1757
+ (n) => {
1758
+ e = n;
1759
+ }
1760
+ ), s = new ReadableStream({
1761
+ ...t,
1762
+ start(n) {
1763
+ if (e(n), t.start)
1764
+ return t.start(n);
1765
+ }
1766
+ }), i = await r;
1767
+ return {
1768
+ stream: s,
1769
+ controller: i
1770
+ };
1541
1771
  }
1542
1772
  async function getPhpIniEntries(t, e) {
1543
1773
  const r = parse(await t.readFileAsText(PHP_INI_PATH));
@@ -1572,8 +1802,8 @@ class HttpCookieStore {
1572
1802
  try {
1573
1803
  if (!r.includes("="))
1574
1804
  continue;
1575
- const s = r.indexOf("="), i = r.substring(0, s), o = r.substring(s + 1).split(";")[0];
1576
- this.cookies[i] = o;
1805
+ const s = r.indexOf("="), i = r.substring(0, s), n = r.substring(s + 1).split(";")[0];
1806
+ this.cookies[i] = n;
1577
1807
  } catch (s) {
1578
1808
  logger.error(s);
1579
1809
  }
@@ -1599,22 +1829,22 @@ async function* iteratePhpFiles(t, e, {
1599
1829
  exceptPaths: i = []
1600
1830
  } = {}) {
1601
1831
  e = normalizePath(e);
1602
- const o = [e];
1603
- for (; o.length; ) {
1604
- const n = o.pop();
1605
- if (!n)
1832
+ const n = [e];
1833
+ for (; n.length; ) {
1834
+ const o = n.pop();
1835
+ if (!o)
1606
1836
  return;
1607
- const l = await t.listFiles(n);
1608
- for (const a of l) {
1609
- const p = `${n}/${a}`;
1610
- if (i.includes(p.substring(e.length + 1)))
1837
+ const a = await t.listFiles(o);
1838
+ for (const l of a) {
1839
+ const u = `${o}/${l}`;
1840
+ if (i.includes(u.substring(e.length + 1)))
1611
1841
  continue;
1612
- await t.isDir(p) ? o.push(p) : yield new StreamedFile(
1613
- streamReadFileFromPHP(t, p),
1842
+ await t.isDir(u) ? n.push(u) : yield new StreamedFile(
1843
+ streamReadFileFromPHP(t, u),
1614
1844
  r ? joinPaths(
1615
1845
  s || "",
1616
- p.substring(e.length + 1)
1617
- ) : p
1846
+ u.substring(e.length + 1)
1847
+ ) : u
1618
1848
  );
1619
1849
  }
1620
1850
  }
@@ -1661,11 +1891,7 @@ class PHPProcessManager {
1661
1891
  throw new Error(
1662
1892
  "phpFactory or primaryPhp must be set before calling getPrimaryPhp()."
1663
1893
  );
1664
- if (!this.primaryPhp) {
1665
- const e = await this.spawn({ isPrimary: !0 });
1666
- this.primaryPhp = e.php;
1667
- }
1668
- return this.primaryPhp;
1894
+ return this.primaryPhp || (this.primaryPhpPromise || (this.primaryPhpPromise = this.spawn({ isPrimary: !0 })), this.primaryPhp = (await this.primaryPhpPromise).php, this.primaryPhpPromise = void 0), this.primaryPhp;
1669
1895
  }
1670
1896
  /**
1671
1897
  * Get a PHP instance.
@@ -1674,17 +1900,32 @@ class PHPProcessManager {
1674
1900
  * instance, or a newly spawned PHP instance – depending on the resource
1675
1901
  * availability.
1676
1902
  *
1903
+ * @param considerPrimary - Whether to consider the primary PHP instance.
1904
+ * It matters because PHP.cli() sets the SAPI to CLI and
1905
+ * kills the entire process after it finishes running,
1906
+ * making the primary PHP instance non-reusable for
1907
+ * subsequent .run() calls. This is fine for one-off
1908
+ * child PHP instances, but not for the primary PHP
1909
+ * that's meant to continue working for the entire duration
1910
+ * of the ProcessManager lifetime. Therefore, we don't
1911
+ * consider the primary PHP instance by default unless
1912
+ * the caller explicitly requests it.
1913
+ *
1677
1914
  * @throws {MaxPhpInstancesError} when the maximum number of PHP instances is reached
1678
1915
  * and the waiting timeout is exceeded.
1679
1916
  */
1680
- async acquirePHPInstance() {
1681
- if (this.primaryIdle)
1917
+ async acquirePHPInstance({
1918
+ considerPrimary: e = !0
1919
+ } = {}) {
1920
+ if (this.primaryPhp || await this.getPrimaryPhp(), this.primaryIdle && e)
1682
1921
  return this.primaryIdle = !1, {
1683
1922
  php: await this.getPrimaryPhp(),
1684
- reap: () => this.primaryIdle = !0
1923
+ reap: () => {
1924
+ this.primaryIdle = !0;
1925
+ }
1685
1926
  };
1686
- const e = this.nextInstance || this.spawn({ isPrimary: !1 });
1687
- return this.semaphore.remaining > 0 ? this.nextInstance = this.spawn({ isPrimary: !1 }) : this.nextInstance = null, await e;
1927
+ const r = this.nextInstance || this.spawn({ isPrimary: !1 });
1928
+ return this.semaphore.remaining > 0 ? this.nextInstance = this.spawn({ isPrimary: !1 }) : this.nextInstance = null, await r;
1688
1929
  }
1689
1930
  /**
1690
1931
  * Initiated spawning of a new PHP instance.
@@ -1693,7 +1934,7 @@ class PHPProcessManager {
1693
1934
  * for PHP to spawn.
1694
1935
  */
1695
1936
  spawn(e) {
1696
- if (e.isPrimary && this.allInstances.length > 0)
1937
+ if (e.isPrimary && this.primaryPhpPromise && !this.primaryPhp)
1697
1938
  throw new Error(
1698
1939
  "Requested spawning a primary PHP instance when another primary instance already started spawning."
1699
1940
  );
@@ -1764,23 +2005,23 @@ function ensurePathPrefix(t, e) {
1764
2005
  }
1765
2006
  async function encodeAsMultipart(t) {
1766
2007
  const e = `----${Math.random().toString(36).slice(2)}`, r = `multipart/form-data; boundary=${e}`, s = new TextEncoder(), i = [];
1767
- for (const [a, p] of Object.entries(t))
2008
+ for (const [l, u] of Object.entries(t))
1768
2009
  i.push(`--${e}\r
1769
- `), i.push(`Content-Disposition: form-data; name="${a}"`), p instanceof File && i.push(`; filename="${p.name}"`), i.push(`\r
1770
- `), p instanceof File && (i.push("Content-Type: application/octet-stream"), i.push(`\r
2010
+ `), i.push(`Content-Disposition: form-data; name="${l}"`), u instanceof File && i.push(`; filename="${u.name}"`), i.push(`\r
2011
+ `), u instanceof File && (i.push("Content-Type: application/octet-stream"), i.push(`\r
1771
2012
  `)), i.push(`\r
1772
- `), p instanceof File ? i.push(await fileToUint8Array(p)) : i.push(p), i.push(`\r
2013
+ `), u instanceof File ? i.push(await fileToUint8Array(u)) : i.push(u), i.push(`\r
1773
2014
  `);
1774
2015
  i.push(`--${e}--\r
1775
2016
  `);
1776
- const o = i.reduce((a, p) => a + p.length, 0), n = new Uint8Array(o);
1777
- let l = 0;
1778
- for (const a of i)
1779
- n.set(
1780
- typeof a == "string" ? s.encode(a) : a,
1781
- l
1782
- ), l += a.length;
1783
- return { bytes: n, contentType: r };
2017
+ const n = i.reduce((l, u) => l + u.length, 0), o = new Uint8Array(n);
2018
+ let a = 0;
2019
+ for (const l of i)
2020
+ o.set(
2021
+ typeof l == "string" ? s.encode(l) : l,
2022
+ a
2023
+ ), a += l.length;
2024
+ return { bytes: o, contentType: r };
1784
2025
  }
1785
2026
  function fileToUint8Array(t) {
1786
2027
  return t.arrayBuffer().then((e) => new Uint8Array(e));
@@ -1881,7 +2122,7 @@ const _default = "application/octet-stream", asx = "video/x-ms-asf", atom = "app
1881
2122
  xspf,
1882
2123
  zip
1883
2124
  };
1884
- var g, S, k, E, H, f, b, y, w, V, J, Y;
2125
+ var g, k, N, H, I, _, C, v, x, Z, ee, te;
1885
2126
  class PHPRequestHandler {
1886
2127
  /**
1887
2128
  * The request handler needs to decide whether to serve a static asset or
@@ -1895,42 +2136,42 @@ class PHPRequestHandler {
1895
2136
  * @param config - Request Handler configuration.
1896
2137
  */
1897
2138
  constructor(e) {
1898
- h(this, w);
2139
+ h(this, x);
1899
2140
  h(this, g);
1900
- h(this, S);
1901
2141
  h(this, k);
1902
- h(this, E);
2142
+ h(this, N);
1903
2143
  h(this, H);
1904
- h(this, f);
1905
- h(this, b);
1906
- h(this, y);
2144
+ h(this, I);
2145
+ h(this, _);
2146
+ h(this, C);
2147
+ h(this, v);
1907
2148
  const {
1908
2149
  documentRoot: r = "/www/",
1909
2150
  absoluteUrl: s = typeof location == "object" ? location.href : DEFAULT_BASE_URL,
1910
2151
  rewriteRules: i = [],
1911
- getFileNotFoundAction: o = () => ({ type: "404" })
2152
+ getFileNotFoundAction: n = () => ({ type: "404" })
1912
2153
  } = e;
1913
2154
  "processManager" in e ? this.processManager = e.processManager : this.processManager = new PHPProcessManager({
1914
- phpFactory: async (a) => {
1915
- const p = await e.phpFactory({
1916
- ...a,
2155
+ phpFactory: async (l) => {
2156
+ const u = await e.phpFactory({
2157
+ ...l,
1917
2158
  requestHandler: this
1918
2159
  });
1919
- return p.requestHandler = this, p;
2160
+ return u.isDir(r) || u.mkdir(r), u.chdir(r), u.requestHandler = this, u;
1920
2161
  },
1921
2162
  maxPhpInstances: e.maxPhpInstances
1922
- }), m(this, y, e.cookieStore === void 0 ? new HttpCookieStore() : e.cookieStore), m(this, g, r);
1923
- const n = new URL(s);
1924
- m(this, k, n.hostname), m(this, E, n.port ? Number(n.port) : n.protocol === "https:" ? 443 : 80), m(this, S, (n.protocol || "").replace(":", ""));
1925
- const l = c(this, E) !== 443 && c(this, E) !== 80;
1926
- m(this, H, [
1927
- c(this, k),
1928
- l ? `:${c(this, E)}` : ""
1929
- ].join("")), m(this, f, n.pathname.replace(/\/+$/, "")), m(this, b, [
1930
- `${c(this, S)}://`,
1931
- c(this, H),
1932
- c(this, f)
1933
- ].join("")), this.rewriteRules = i, this.getFileNotFoundAction = o;
2163
+ }), m(this, v, e.cookieStore === void 0 ? new HttpCookieStore() : e.cookieStore), m(this, g, r);
2164
+ const o = new URL(s);
2165
+ m(this, N, o.hostname), m(this, H, o.port ? Number(o.port) : o.protocol === "https:" ? 443 : 80), m(this, k, (o.protocol || "").replace(":", ""));
2166
+ const a = c(this, H) !== 443 && c(this, H) !== 80;
2167
+ m(this, I, [
2168
+ c(this, N),
2169
+ a ? `:${c(this, H)}` : ""
2170
+ ].join("")), m(this, _, o.pathname.replace(/\/+$/, "")), m(this, C, [
2171
+ `${c(this, k)}://`,
2172
+ c(this, I),
2173
+ c(this, _)
2174
+ ].join("")), this.rewriteRules = i, this.getFileNotFoundAction = n;
1934
2175
  }
1935
2176
  async getPrimaryPhp() {
1936
2177
  return await this.processManager.getPrimaryPhp();
@@ -1954,13 +2195,13 @@ class PHPRequestHandler {
1954
2195
  */
1955
2196
  internalUrlToPath(e) {
1956
2197
  const r = new URL(e);
1957
- return r.pathname.startsWith(c(this, f)) && (r.pathname = r.pathname.slice(c(this, f).length)), toRelativeUrl(r);
2198
+ return r.pathname.startsWith(c(this, _)) && (r.pathname = r.pathname.slice(c(this, _).length)), toRelativeUrl(r);
1958
2199
  }
1959
2200
  /**
1960
2201
  * The absolute URL of this PHPRequestHandler instance.
1961
2202
  */
1962
2203
  get absoluteUrl() {
1963
- return c(this, b);
2204
+ return c(this, C);
1964
2205
  }
1965
2206
  /**
1966
2207
  * The directory in the PHP filesystem where the server will look
@@ -2025,65 +2266,65 @@ class PHPRequestHandler {
2025
2266
  ), i = applyRewriteRules(
2026
2267
  removePathPrefix(
2027
2268
  decodeURIComponent(s.pathname),
2028
- c(this, f)
2269
+ c(this, _)
2029
2270
  ),
2030
2271
  this.rewriteRules
2031
- ), o = await this.getPrimaryPhp();
2032
- let n = joinPaths(c(this, g), i);
2033
- if (o.isDir(n)) {
2034
- if (!n.endsWith("/"))
2272
+ ), n = await this.getPrimaryPhp();
2273
+ let o = joinPaths(c(this, g), i);
2274
+ if (n.isDir(o)) {
2275
+ if (!o.endsWith("/"))
2035
2276
  return new PHPResponse(
2036
2277
  301,
2037
2278
  { Location: [`${s.pathname}/`] },
2038
2279
  new Uint8Array(0)
2039
2280
  );
2040
- for (const l of ["index.php", "index.html"]) {
2041
- const a = joinPaths(n, l);
2042
- if (o.isFile(a)) {
2043
- n = a;
2281
+ for (const a of ["index.php", "index.html"]) {
2282
+ const l = joinPaths(o, a);
2283
+ if (n.isFile(l)) {
2284
+ o = l;
2044
2285
  break;
2045
2286
  }
2046
2287
  }
2047
2288
  }
2048
- if (!o.isFile(n)) {
2049
- const l = this.getFileNotFoundAction(
2289
+ if (!n.isFile(o)) {
2290
+ const a = this.getFileNotFoundAction(
2050
2291
  i
2051
2292
  );
2052
- switch (l.type) {
2293
+ switch (a.type) {
2053
2294
  case "response":
2054
- return l.response;
2295
+ return a.response;
2055
2296
  case "internal-redirect":
2056
- n = joinPaths(c(this, g), l.uri);
2297
+ o = joinPaths(c(this, g), a.uri);
2057
2298
  break;
2058
2299
  case "404":
2059
2300
  return PHPResponse.forHttpCode(404);
2060
2301
  default:
2061
2302
  throw new Error(
2062
- `Unsupported file-not-found action type: '${l.type}'`
2303
+ `Unsupported file-not-found action type: '${a.type}'`
2063
2304
  );
2064
2305
  }
2065
2306
  }
2066
- if (o.isFile(n))
2067
- if (n.endsWith(".php")) {
2068
- const l = {
2307
+ if (n.isFile(o))
2308
+ if (o.endsWith(".php")) {
2309
+ const a = {
2069
2310
  ...e,
2070
2311
  // Pass along URL with the #fragment filtered out
2071
2312
  url: s.toString()
2072
2313
  };
2073
- return d(this, w, J).call(this, l, n);
2314
+ return d(this, x, ee).call(this, a, o);
2074
2315
  } else
2075
- return d(this, w, V).call(this, o, n);
2316
+ return d(this, x, Z).call(this, n, o);
2076
2317
  else
2077
2318
  return PHPResponse.forHttpCode(404);
2078
2319
  }
2079
2320
  }
2080
- g = new WeakMap(), S = new WeakMap(), k = new WeakMap(), E = new WeakMap(), H = new WeakMap(), f = new WeakMap(), b = new WeakMap(), y = new WeakMap(), w = new WeakSet(), /**
2321
+ g = new WeakMap(), k = new WeakMap(), N = new WeakMap(), H = new WeakMap(), I = new WeakMap(), _ = new WeakMap(), C = new WeakMap(), v = new WeakMap(), x = new WeakSet(), /**
2081
2322
  * Serves a static file from the PHP filesystem.
2082
2323
  *
2083
2324
  * @param fsPath - Absolute path of the static file to serve.
2084
2325
  * @returns The response.
2085
2326
  */
2086
- V = function(e, r) {
2327
+ Z = function(e, r) {
2087
2328
  const s = e.readFileAsBuffer(r);
2088
2329
  return new PHPResponse(
2089
2330
  200,
@@ -2098,56 +2339,58 @@ V = function(e, r) {
2098
2339
  },
2099
2340
  s
2100
2341
  );
2101
- }, J = async function(e, r) {
2342
+ }, ee = async function(e, r) {
2102
2343
  let s;
2103
2344
  try {
2104
- s = await this.processManager.acquirePHPInstance();
2345
+ s = await this.processManager.acquirePHPInstance({
2346
+ considerPrimary: !0
2347
+ });
2105
2348
  } catch (i) {
2106
2349
  return i instanceof MaxPhpInstancesError ? PHPResponse.forHttpCode(502) : PHPResponse.forHttpCode(500);
2107
2350
  }
2108
2351
  try {
2109
- return await d(this, w, Y).call(this, s.php, e, r);
2352
+ return await d(this, x, te).call(this, s.php, e, r);
2110
2353
  } finally {
2111
2354
  s.reap();
2112
2355
  }
2113
- }, Y = async function(e, r, s) {
2356
+ }, te = async function(e, r, s) {
2114
2357
  let i = "GET";
2115
- const o = {
2116
- host: c(this, H),
2358
+ const n = {
2359
+ host: c(this, I),
2117
2360
  ...normalizeHeaders(r.headers || {})
2118
2361
  };
2119
- c(this, y) && (o.cookie = c(this, y).getCookieRequestHeader());
2120
- let n = r.body;
2121
- if (typeof n == "object" && !(n instanceof Uint8Array)) {
2362
+ c(this, v) && (n.cookie = c(this, v).getCookieRequestHeader());
2363
+ let o = r.body;
2364
+ if (typeof o == "object" && !(o instanceof Uint8Array)) {
2122
2365
  i = "POST";
2123
- const { bytes: l, contentType: a } = await encodeAsMultipart(n);
2124
- n = l, o["content-type"] = a;
2366
+ const { bytes: a, contentType: l } = await encodeAsMultipart(o);
2367
+ o = a, n["content-type"] = l;
2125
2368
  }
2126
2369
  try {
2127
- const l = await e.run({
2370
+ const a = await e.run({
2128
2371
  relativeUri: ensurePathPrefix(
2129
2372
  toRelativeUrl(new URL(r.url)),
2130
- c(this, f)
2373
+ c(this, _)
2131
2374
  ),
2132
- protocol: c(this, S),
2375
+ protocol: c(this, k),
2133
2376
  method: r.method || i,
2134
2377
  $_SERVER: {
2135
2378
  REMOTE_ADDR: "127.0.0.1",
2136
2379
  DOCUMENT_ROOT: c(this, g),
2137
- HTTPS: c(this, b).startsWith("https://") ? "on" : ""
2380
+ HTTPS: c(this, C).startsWith("https://") ? "on" : ""
2138
2381
  },
2139
- body: n,
2382
+ body: o,
2140
2383
  scriptPath: s,
2141
- headers: o
2384
+ headers: n
2142
2385
  });
2143
- return c(this, y) && c(this, y).rememberCookiesFromResponseHeaders(
2144
- l.headers
2145
- ), l;
2146
- } catch (l) {
2147
- const a = l;
2148
- if (a != null && a.response)
2149
- return a.response;
2150
- throw l;
2386
+ return c(this, v) && c(this, v).rememberCookiesFromResponseHeaders(
2387
+ a.headers
2388
+ ), a;
2389
+ } catch (a) {
2390
+ const l = a;
2391
+ if (l != null && l.response)
2392
+ return l.response;
2393
+ throw a;
2151
2394
  }
2152
2395
  };
2153
2396
  function inferMimeType(t) {
@@ -2175,29 +2418,29 @@ function rotatePHPRuntime({
2175
2418
  maxRequests: s = 400
2176
2419
  }) {
2177
2420
  let i = 0;
2178
- async function o() {
2179
- const a = await t.semaphore.acquire();
2421
+ async function n() {
2422
+ const l = await t.semaphore.acquire();
2180
2423
  try {
2181
2424
  await t.hotSwapPHPRuntime(await r(), e), i = 0;
2182
2425
  } finally {
2183
- a();
2426
+ l();
2184
2427
  }
2185
2428
  }
2186
- async function n() {
2187
- ++i < s || await o();
2429
+ async function o() {
2430
+ ++i < s || await n();
2188
2431
  }
2189
- async function l(a) {
2190
- a.type === "request.error" && a.source === "php-wasm" && await o();
2432
+ async function a(l) {
2433
+ l.type === "request.error" && l.source === "php-wasm" && await n();
2191
2434
  }
2192
- return t.addEventListener("request.error", l), t.addEventListener("request.end", n), function() {
2193
- t.removeEventListener("request.error", l), t.removeEventListener("request.end", n);
2435
+ return t.addEventListener("request.error", a), t.addEventListener("request.end", o), function() {
2436
+ t.removeEventListener("request.error", a), t.removeEventListener("request.end", o);
2194
2437
  };
2195
2438
  }
2196
2439
  async function writeFiles(t, e, r, { rmRoot: s = !1 } = {}) {
2197
2440
  s && await t.isDir(e) && await t.rmdir(e, { recursive: !0 });
2198
- for (const [i, o] of Object.entries(r)) {
2199
- const n = joinPaths(e, i);
2200
- await t.fileExists(dirname(n)) || await t.mkdir(dirname(n)), o instanceof Uint8Array || typeof o == "string" ? await t.writeFile(n, o) : await writeFiles(t, n, o);
2441
+ for (const [i, n] of Object.entries(r)) {
2442
+ const o = joinPaths(e, i);
2443
+ await t.fileExists(dirname(o)) || await t.mkdir(dirname(o)), n instanceof Uint8Array || typeof n == "string" ? await t.writeFile(o, n) : await writeFiles(t, o, n);
2201
2444
  }
2202
2445
  }
2203
2446
  function proxyFileSystem(t, e, r) {
@@ -2220,10 +2463,12 @@ export {
2220
2463
  HttpCookieStore,
2221
2464
  LatestSupportedPHPVersion,
2222
2465
  PHP,
2466
+ PHPExecutionFailureError,
2223
2467
  PHPProcessManager,
2224
2468
  PHPRequestHandler,
2225
2469
  PHPResponse,
2226
2470
  PHPWorker,
2471
+ StreamedPHPResponse,
2227
2472
  SupportedPHPVersions,
2228
2473
  SupportedPHPVersionsList,
2229
2474
  UnhandledRejectionsTarget,
@@ -2232,7 +2477,7 @@ export {
2232
2477
  ensurePathPrefix,
2233
2478
  getLoadedRuntime,
2234
2479
  getPhpIniEntries,
2235
- isExitCodeZero,
2480
+ isExitCode,
2236
2481
  iteratePhpFiles as iterateFiles,
2237
2482
  loadPHPRuntime,
2238
2483
  proxyFileSystem,