var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
  if (typeof require !== "undefined")
    return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x + '" is not supported');
});

// packages/php-wasm/node/src/index.ts
import "@php-wasm/node-polyfills";

// packages/php-wasm/node/src/lib/get-php-loader-module.ts
import { LatestSupportedPHPVersion } from "@php-wasm/universal";
async function getPHPLoaderModule(version = LatestSupportedPHPVersion) {
  try {
    switch (version) {
      case "8.5":
        return (await import("@php-wasm/node-8-5")).getPHPLoaderModule();
      case "8.4":
        return (await import("@php-wasm/node-8-4")).getPHPLoaderModule();
      case "8.3":
        return (await import("@php-wasm/node-8-3")).getPHPLoaderModule();
      case "8.2":
        return (await import("@php-wasm/node-8-2")).getPHPLoaderModule();
      case "8.1":
        return (await import("@php-wasm/node-8-1")).getPHPLoaderModule();
      case "8.0":
        return (await import("@php-wasm/node-8-0")).getPHPLoaderModule();
      case "7.4":
        return (await import("@php-wasm/node-7-4")).getPHPLoaderModule();
    }
    throw new Error(`Unsupported PHP version ${version}`);
  } catch (errorCandidate) {
    if (!errorCandidate || typeof errorCandidate !== "object") {
      throw errorCandidate;
    }
    const error = errorCandidate;
    if (error.message?.includes(
      `SyntaxError: Cannot use 'import.meta' outside a module`
    )) {
      throw new Error(
        `Node.js crashed on a 'import.meta' statement. This happens when running in a node:vm context. Some testing libraries like Jest use heavily customized runtime contexts, involving node:vm, jest-runtime, and custom require() implementations. These contexts do not support 'import.meta' statements and, by extension, cannot run the @php-wasm/node package. Consider using the --experimental-vm-modules flag or switching to a different test runner such as vitest.`,
        {
          cause: error
        }
      );
    } else if (error?.code === `ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING` || error?.code === `ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG` || error?.message?.includes(
      `A dynamic import callback was invoked without --experimental-vm-modules`
    ) || error?.message?.includes(
      `A dynamic import callback was not specified`
    ) || error?.message?.includes(`Must use import to load ES Module`)) {
      throw new Error(
        `Node.js crashed on a 'import()' statement. This happens when running in a node:vm context. Some testing libraries like Jest use runtime contexts, involving node:vm, jest-runtime, or other custom require() implementations. These contexts do not support import() statements and, by extension, cannot run the @php-wasm/node package. Consider using the --experimental-vm-modules flag or switching to a different test runner such as vitest.`,
        {
          cause: error
        }
      );
    }
    throw error;
  }
}

// packages/php-wasm/node/src/lib/networking/outbound-ws-to-tcp-proxy.ts
import * as dns from "dns";
import * as http from "http";
import * as net2 from "net";
import { WebSocketServer } from "ws";

// packages/php-wasm/node/src/lib/networking/utils.ts
import * as net from "net";
import { logger } from "@php-wasm/logger";
function debugLog(message, ...args) {
  if (process.env["DEV"] && !process.env["TEST"]) {
    logger.log(message, ...args);
  }
}
async function findFreePorts(n) {
  const serversPromises = [];
  for (let i = 0; i < n; i++) {
    serversPromises.push(listenOnRandomPort());
  }
  const servers = await Promise.all(serversPromises);
  const ports = [];
  for (const server of servers) {
    const address = server.address();
    ports.push(address.port);
    server.close();
  }
  return ports;
}
function listenOnRandomPort() {
  return new Promise((resolve) => {
    const server = net.createServer();
    server.listen(0, () => {
      resolve(server);
    });
  });
}

// packages/php-wasm/node/src/lib/networking/outbound-ws-to-tcp-proxy.ts
function log(...args) {
  debugLog("[WS Server]", ...args);
}
function lookupIPv4(hostname) {
  return new Promise((resolve, reject) => {
    dns.lookup(hostname, { family: 4 }, (err, address) => {
      if (err) {
        reject(err);
      } else {
        resolve(address);
      }
    });
  });
}
function prependByte(chunk, byte) {
  if (typeof chunk === "string") {
    chunk = String.fromCharCode(byte) + chunk;
  } else if (chunk instanceof ArrayBuffer || "byteLength" in chunk) {
    const buffer = new Uint8Array(chunk.byteLength + 1);
    buffer[0] = byte;
    buffer.set(new Uint8Array(chunk), 1);
    chunk = buffer.buffer;
  } else {
    log({ chunk });
    throw new Error("Unsupported chunk type: " + typeof chunk);
  }
  return chunk;
}
var COMMAND_CHUNK = 1;
var COMMAND_SET_SOCKETOPT = 2;
function addSocketOptionsSupportToWebSocketClass(WebSocketConstructor) {
  return class PHPWasmWebSocketConstructor extends WebSocketConstructor {
    // @ts-ignore
    send(chunk, callback) {
      return this.sendCommand(COMMAND_CHUNK, chunk, callback);
    }
    setSocketOpt(optionClass, optionName, optionValue) {
      return this.sendCommand(
        COMMAND_SET_SOCKETOPT,
        new Uint8Array([optionClass, optionName, optionValue]).buffer,
        () => void 0
      );
    }
    sendCommand(commandType, chunk, callback) {
      return WebSocketConstructor.prototype.send.call(
        this,
        prependByte(chunk, commandType),
        callback
      );
    }
  };
}
function initOutboundWebsocketProxyServer(listenPort, listenHost = "127.0.0.1") {
  log(`Binding the WebSockets server to ${listenHost}:${listenPort}...`);
  const webServer = http.createServer((request, response) => {
    response.writeHead(403, { "Content-Type": "text/plain" });
    response.write(
      "403 Permission Denied\nOnly websockets are allowed here.\n"
    );
    response.end();
  });
  return new Promise((resolve) => {
    webServer.listen(listenPort, listenHost, function() {
      const wsServer = new WebSocketServer({ server: webServer });
      wsServer.on("connection", onWsConnect);
      resolve(webServer);
    });
  });
}
async function onWsConnect(client, request) {
  const clientAddr = client?._socket?.remoteAddress || client.url;
  const clientLog = function(...args) {
    log(" " + clientAddr + ": ", ...args);
  };
  clientLog(
    "WebSocket connection from : " + clientAddr + " at URL " + (request ? request.url : client.upgradeReq.url)
  );
  clientLog(
    "Version " + client.protocolVersion + ", subprotocol: " + client.protocol
  );
  const reqUrl = new URL(`ws://0.0.0.0` + request.url);
  const reqTargetPort = Number(reqUrl.searchParams.get("port"));
  const reqTargetHost = reqUrl.searchParams.get("host");
  if (!reqTargetPort || !reqTargetHost) {
    clientLog("Missing host or port information");
    client.close(3e3);
    return;
  }
  if (reqTargetPort < 0 || reqTargetPort > 65535) {
    clientLog("Invalid port number: " + reqTargetPort);
    client.send([]);
    client.close(3e3);
    return;
  }
  let target;
  const recvQueue = [];
  function flushMessagesQueue() {
    while (recvQueue.length > 0) {
      const msg = recvQueue.pop();
      const commandType = msg[0];
      clientLog("flushing", { commandType }, msg);
      if (commandType === COMMAND_CHUNK) {
        target.write(msg.slice(1));
      } else if (commandType === COMMAND_SET_SOCKETOPT) {
        const SOL_SOCKET = 1;
        const SO_KEEPALIVE = 9;
        const IPPROTO_TCP = 6;
        const TCP_NODELAY = 1;
        if (msg[1] === SOL_SOCKET && msg[2] === SO_KEEPALIVE) {
          target.setKeepAlive(msg[3]);
        } else if (msg[1] === IPPROTO_TCP && msg[2] === TCP_NODELAY) {
          target.setNoDelay(msg[3]);
        }
      } else {
        clientLog("Unknown command type: " + commandType);
        process.exit();
      }
    }
  }
  client.on("message", function(msg) {
    recvQueue.unshift(msg);
    if (target) {
      flushMessagesQueue();
    }
  });
  client.on("close", function(code, reason) {
    clientLog(
      "WebSocket client disconnected: " + code + " [" + reason + "]"
    );
    if (target) {
      target.end();
    }
  });
  client.on("error", function(a) {
    clientLog("WebSocket client error: " + a);
    target.end();
  });
  let reqTargetIp;
  if (net2.isIP(reqTargetHost) === 0) {
    clientLog("resolving " + reqTargetHost + "... ");
    try {
      reqTargetIp = await lookupIPv4(reqTargetHost);
      clientLog("resolved " + reqTargetHost + " -> " + reqTargetIp);
    } catch (e) {
      clientLog("can't resolve " + reqTargetHost + " due to:", e);
      client.send([]);
      setTimeout(() => {
        client.close(3e3);
      });
      return;
    }
  } else {
    reqTargetIp = reqTargetHost;
  }
  clientLog(
    "Opening a socket connection to " + reqTargetIp + ":" + reqTargetPort
  );
  target = net2.createConnection(reqTargetPort, reqTargetIp, function() {
    clientLog("Connected to target");
    flushMessagesQueue();
  });
  target.on("data", function(data) {
    try {
      client.send(data);
    } catch {
      clientLog("Client closed, cleaning up target");
      target.end();
    }
  });
  target.on("end", function() {
    clientLog("target disconnected");
    client.close();
  });
  target.on("error", function(e) {
    clientLog("target connection error", e);
    client.send([]);
    setTimeout(() => {
      client.close(3e3);
      try {
        target.end();
      } catch {
      }
    });
  });
}

// packages/php-wasm/node/src/lib/networking/inbound-tcp-to-ws-proxy.ts
import { createServer as createServer3 } from "net";
import { WebSocket } from "ws";
function log2(...args) {
  debugLog("[TCP Server]", ...args);
}
function addTCPServerToWebSocketServerClass(wsListenPort, WSServer) {
  return class PHPWasmWebSocketServer extends WSServer {
    constructor(options, callback) {
      const requestedPort = options.port;
      options.port = wsListenPort;
      listenTCPToWSProxy({
        tcpListenPort: requestedPort,
        wsConnectPort: wsListenPort
      });
      super(options, callback);
    }
  };
}
function listenTCPToWSProxy(options) {
  options = {
    wsConnectHost: "127.0.0.1",
    ...options
  };
  const { tcpListenPort, wsConnectHost, wsConnectPort } = options;
  const server = createServer3();
  server.on("connection", function handleConnection(tcpSource) {
    const inBuffer = [];
    const wsTarget = new WebSocket(
      `ws://${wsConnectHost}:${wsConnectPort}/`
    );
    wsTarget.binaryType = "arraybuffer";
    function wsSend(data) {
      wsTarget.send(new Uint8Array(data));
    }
    wsTarget.addEventListener("open", function() {
      log2("Outbound WebSocket connection established");
      while (inBuffer.length > 0) {
        wsSend(inBuffer.shift());
      }
    });
    wsTarget.addEventListener("message", (e) => {
      log2(
        "WS->TCP message:",
        new TextDecoder().decode(e.data)
      );
      tcpSource.write(Buffer.from(e.data));
    });
    wsTarget.addEventListener("close", () => {
      log2("WebSocket connection closed");
      tcpSource.end();
    });
    tcpSource.on("data", function(data) {
      log2("TCP->WS message:", data);
      if (wsTarget.readyState === WebSocket.OPEN) {
        while (inBuffer.length > 0) {
          wsSend(inBuffer.shift());
        }
        wsSend(data);
      } else {
        inBuffer.push(data);
      }
    });
    tcpSource.once("close", function() {
      log2("TCP connection closed");
      wsTarget.close();
    });
    tcpSource.on("error", function() {
      log2("TCP connection error");
      wsTarget.close();
    });
  });
  server.listen(tcpListenPort, function() {
    log2("TCP server listening");
  });
}

// packages/php-wasm/node/src/lib/networking/with-networking.ts
async function withNetworking(phpModuleArgs = {}) {
  const [inboundProxyWsServerPort, outboundProxyWsServerPort] = await findFreePorts(2);
  const outboundNetworkProxyServer = await initOutboundWebsocketProxyServer(
    outboundProxyWsServerPort
  );
  return {
    ...phpModuleArgs,
    outboundNetworkProxyServer,
    websocket: {
      ...phpModuleArgs["websocket"] || {},
      url: (_, host, port) => {
        const query = new URLSearchParams({
          host,
          port
        }).toString();
        return `ws://127.0.0.1:${outboundProxyWsServerPort}/?${query}`;
      },
      subprotocol: "binary",
      decorator: addSocketOptionsSupportToWebSocketClass,
      serverDecorator: addTCPServerToWebSocketServerClass.bind(
        null,
        inboundProxyWsServerPort
      )
    }
  };
}

// packages/php-wasm/node/src/lib/load-runtime.ts
import { loadPHPRuntime, FSHelpers as FSHelpers5 } from "@php-wasm/universal";
import fs5 from "fs";

// packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion3, FSHelpers } from "@php-wasm/universal";
import fs from "fs";

// packages/php-wasm/node/src/lib/extensions/xdebug/get-xdebug-extension-module.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion2 } from "@php-wasm/universal";
async function getXdebugExtensionModule(version = LatestSupportedPHPVersion2) {
  switch (version) {
    case "8.5":
      return (await import("@php-wasm/node-8-5")).getXdebugExtensionPath();
    case "8.4":
      return (await import("@php-wasm/node-8-4")).getXdebugExtensionPath();
    case "8.3":
      return (await import("@php-wasm/node-8-3")).getXdebugExtensionPath();
    case "8.2":
      return (await import("@php-wasm/node-8-2")).getXdebugExtensionPath();
    case "8.1":
      return (await import("@php-wasm/node-8-1")).getXdebugExtensionPath();
    case "8.0":
      return (await import("@php-wasm/node-8-0")).getXdebugExtensionPath();
    case "7.4":
      return (await import("@php-wasm/node-7-4")).getXdebugExtensionPath();
  }
  throw new Error(`Unsupported PHP version ${version}`);
}

// packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
async function withXdebug(version = LatestSupportedPHPVersion3, options, xdebugOptions) {
  const fileName = "xdebug.so";
  const filePath = await getXdebugExtensionModule(version);
  const extension = fs.readFileSync(filePath);
  return {
    ...options,
    ENV: {
      ...options.ENV,
      PHP_INI_SCAN_DIR: "/internal/shared/extensions"
    },
    onRuntimeInitialized: (phpRuntime) => {
      if (options.onRuntimeInitialized) {
        options.onRuntimeInitialized(phpRuntime);
      }
      if (!FSHelpers.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions"
      )) {
        phpRuntime.FS.mkdirTree("/internal/shared/extensions");
      }
      if (!FSHelpers.fileExists(
        phpRuntime.FS,
        `/internal/shared/extensions/${fileName}`
      )) {
        phpRuntime.FS.writeFile(
          `/internal/shared/extensions/${fileName}`,
          new Uint8Array(extension)
        );
      }
      if (!FSHelpers.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions/xdebug.ini"
      )) {
        const ideKey = xdebugOptions?.ideKey || "PLAYGROUNDCLI";
        phpRuntime.FS.writeFile(
          "/internal/shared/extensions/xdebug.ini",
          [
            "zend_extension=/internal/shared/extensions/xdebug.so",
            "xdebug.mode=debug,develop",
            "xdebug.start_with_request=yes",
            `xdebug.idekey="${ideKey}"`
          ].join("\n")
        );
      }
      const cwd = process.cwd();
      if (cwd && cwd !== "/") {
        phpRuntime.FS.mkdirTree(cwd);
        phpRuntime.FS.mount(
          phpRuntime.FS.filesystems["NODEFS"],
          { root: cwd },
          cwd
        );
        phpRuntime.FS.chdir(cwd);
      }
    }
  };
}

// packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion5, FSHelpers as FSHelpers2 } from "@php-wasm/universal";
import fs2 from "fs";
import path from "path";

// packages/php-wasm/node/src/lib/extensions/intl/get-intl-extension-module.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion4 } from "@php-wasm/universal";
async function getIntlExtensionModule(version = LatestSupportedPHPVersion4) {
  switch (version) {
    case "8.5":
      return (await import("@php-wasm/node-8-5")).getIntlExtensionPath();
    case "8.4":
      return (await import("@php-wasm/node-8-4")).getIntlExtensionPath();
    case "8.3":
      return (await import("@php-wasm/node-8-3")).getIntlExtensionPath();
    case "8.2":
      return (await import("@php-wasm/node-8-2")).getIntlExtensionPath();
    case "8.1":
      return (await import("@php-wasm/node-8-1")).getIntlExtensionPath();
    case "8.0":
      return (await import("@php-wasm/node-8-0")).getIntlExtensionPath();
    case "7.4":
      return (await import("@php-wasm/node-7-4")).getIntlExtensionPath();
  }
  throw new Error(`Unsupported PHP version ${version}`);
}

// packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
async function withIntl(version = LatestSupportedPHPVersion5, options) {
  const extensionName = "intl.so";
  const extensionPath = await getIntlExtensionModule(version);
  const extension = fs2.readFileSync(extensionPath);
  const dataName = "icu.dat";
  const moduleDir = typeof __dirname !== "undefined" ? __dirname : import.meta.dirname;
  const dataPath = path.join(moduleDir, "shared", dataName);
  const ICUData = fs2.readFileSync(dataPath);
  return {
    ...options,
    ENV: {
      ...options.ENV,
      PHP_INI_SCAN_DIR: "/internal/shared/extensions",
      ICU_DATA: "/internal/shared"
    },
    onRuntimeInitialized: (phpRuntime) => {
      if (options.onRuntimeInitialized) {
        options.onRuntimeInitialized(phpRuntime);
      }
      if (!FSHelpers2.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions"
      )) {
        phpRuntime.FS.mkdirTree("/internal/shared/extensions");
      }
      if (!FSHelpers2.fileExists(
        phpRuntime.FS,
        `/internal/shared/extensions/${extensionName}`
      )) {
        phpRuntime.FS.writeFile(
          `/internal/shared/extensions/${extensionName}`,
          new Uint8Array(extension)
        );
      }
      if (!FSHelpers2.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions/intl.ini"
      )) {
        phpRuntime.FS.writeFile(
          "/internal/shared/extensions/intl.ini",
          [
            `extension=/internal/shared/extensions/${extensionName}`
          ].join("\n")
        );
      }
      if (!FSHelpers2.fileExists(
        phpRuntime.FS,
        `${phpRuntime.ENV.ICU_DATA}/${dataName}`
      )) {
        phpRuntime.FS.mkdirTree(phpRuntime.ENV.ICU_DATA);
        phpRuntime.FS.writeFile(
          `${phpRuntime.ENV.ICU_DATA}/icudt74l.dat`,
          new Uint8Array(ICUData)
        );
      }
    }
  };
}

// packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion7, FSHelpers as FSHelpers3 } from "@php-wasm/universal";
import fs3 from "fs";

// packages/php-wasm/node/src/lib/extensions/redis/get-redis-extension-module.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion6 } from "@php-wasm/universal";
async function getRedisExtensionModule(version = LatestSupportedPHPVersion6) {
  switch (version) {
    case "8.5":
      return (await import("@php-wasm/node-8-5")).getRedisExtensionPath();
    case "8.4":
      return (await import("@php-wasm/node-8-4")).getRedisExtensionPath();
    case "8.3":
      return (await import("@php-wasm/node-8-3")).getRedisExtensionPath();
    case "8.2":
      return (await import("@php-wasm/node-8-2")).getRedisExtensionPath();
    case "8.1":
      return (await import("@php-wasm/node-8-1")).getRedisExtensionPath();
    case "8.0":
      return (await import("@php-wasm/node-8-0")).getRedisExtensionPath();
    case "7.4":
      return (await import("@php-wasm/node-7-4")).getRedisExtensionPath();
  }
  throw new Error(`Unsupported PHP version ${version}`);
}

// packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
async function withRedis(version = LatestSupportedPHPVersion7, options) {
  const extensionName = "redis.so";
  const extensionPath = await getRedisExtensionModule(version);
  const extension = fs3.readFileSync(extensionPath);
  return {
    ...options,
    ENV: {
      ...options.ENV,
      PHP_INI_SCAN_DIR: "/internal/shared/extensions"
    },
    onRuntimeInitialized: (phpRuntime) => {
      if (options.onRuntimeInitialized) {
        options.onRuntimeInitialized(phpRuntime);
      }
      if (!FSHelpers3.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions"
      )) {
        phpRuntime.FS.mkdirTree("/internal/shared/extensions");
      }
      if (!FSHelpers3.fileExists(
        phpRuntime.FS,
        `/internal/shared/extensions/${extensionName}`
      )) {
        phpRuntime.FS.writeFile(
          `/internal/shared/extensions/${extensionName}`,
          new Uint8Array(extension)
        );
      }
      if (!FSHelpers3.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions/redis.ini"
      )) {
        phpRuntime.FS.writeFile(
          "/internal/shared/extensions/redis.ini",
          [
            `extension=/internal/shared/extensions/${extensionName}`
          ].join("\n")
        );
      }
    }
  };
}

// packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion9, FSHelpers as FSHelpers4 } from "@php-wasm/universal";
import fs4 from "fs";

// packages/php-wasm/node/src/lib/extensions/memcached/get-memcached-extension-module.ts
import { LatestSupportedPHPVersion as LatestSupportedPHPVersion8 } from "@php-wasm/universal";
async function getMemcachedExtensionModule(version = LatestSupportedPHPVersion8) {
  switch (version) {
    case "8.5":
      return (await import("@php-wasm/node-8-5")).getMemcachedExtensionPath();
    case "8.4":
      return (await import("@php-wasm/node-8-4")).getMemcachedExtensionPath();
    case "8.3":
      return (await import("@php-wasm/node-8-3")).getMemcachedExtensionPath();
    case "8.2":
      return (await import("@php-wasm/node-8-2")).getMemcachedExtensionPath();
    case "8.1":
      return (await import("@php-wasm/node-8-1")).getMemcachedExtensionPath();
    case "8.0":
      return (await import("@php-wasm/node-8-0")).getMemcachedExtensionPath();
    case "7.4":
      return (await import("@php-wasm/node-7-4")).getMemcachedExtensionPath();
  }
  throw new Error(`Unsupported PHP version ${version}`);
}

// packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
async function withMemcached(version = LatestSupportedPHPVersion9, options) {
  const extensionName = "memcached.so";
  const extensionPath = await getMemcachedExtensionModule(version);
  const extension = fs4.readFileSync(extensionPath);
  return {
    ...options,
    ENV: {
      ...options.ENV,
      PHP_INI_SCAN_DIR: "/internal/shared/extensions"
    },
    onRuntimeInitialized: (phpRuntime) => {
      if (options.onRuntimeInitialized) {
        options.onRuntimeInitialized(phpRuntime);
      }
      if (!FSHelpers4.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions"
      )) {
        phpRuntime.FS.mkdirTree("/internal/shared/extensions");
      }
      if (!FSHelpers4.fileExists(
        phpRuntime.FS,
        `/internal/shared/extensions/${extensionName}`
      )) {
        phpRuntime.FS.writeFile(
          `/internal/shared/extensions/${extensionName}`,
          new Uint8Array(extension)
        );
      }
      if (!FSHelpers4.fileExists(
        phpRuntime.FS,
        "/internal/shared/extensions/memcached.ini"
      )) {
        phpRuntime.FS.writeFile(
          "/internal/shared/extensions/memcached.ini",
          [
            `extension=/internal/shared/extensions/${extensionName}`
          ].join("\n")
        );
      }
    }
  };
}

// packages/php-wasm/node/src/lib/load-runtime.ts
import { dirname, joinPaths, toPosixPath } from "@php-wasm/util";
async function loadNodeRuntime(phpVersion, options = {}) {
  let emscriptenOptions = {
    /**
     * Emscripten default behavior is to kill the process when
     * the WASM program calls `exit()`. We want to throw an
     * exception instead.
     */
    quit: function(code, error) {
      throw error;
    },
    ...options.emscriptenOptions || {},
    onRuntimeInitialized: (phpRuntime) => {
      if (options?.followSymlinks === true) {
        phpRuntime.FS.filesystems.NODEFS.node_ops.readlink = (node) => {
          const absoluteSourcePath = phpRuntime.FS.filesystems.NODEFS.tryFSOperation(
            () => fs5.realpathSync(
              phpRuntime.FS.filesystems.NODEFS.realPath(node)
            )
          );
          const normalizedPath = toPosixPath(absoluteSourcePath);
          const symlinkMountPath = joinPaths(
            `/internal/symlinks`,
            normalizedPath
          );
          if (fs5.existsSync(absoluteSourcePath)) {
            if (!FSHelpers5.fileExists(
              phpRuntime.FS,
              symlinkMountPath
            )) {
              const sourceStat = fs5.statSync(absoluteSourcePath);
              if (sourceStat.isDirectory()) {
                phpRuntime.FS.mkdirTree(symlinkMountPath);
              } else if (sourceStat.isFile()) {
                phpRuntime.FS.mkdirTree(
                  dirname(symlinkMountPath)
                );
                phpRuntime.FS.writeFile(symlinkMountPath, "");
              } else {
                throw new Error(
                  "Unsupported file type. PHP-wasm supports only symlinks that link to files, directories, or symlinks."
                );
              }
            }
            const symlinkMountNode = phpRuntime.FS.lookupPath(symlinkMountPath).node;
            const isSymlinkMounted = symlinkMountNode.mount.mountpoint === symlinkMountPath;
            if (!isSymlinkMounted) {
              phpRuntime.FS.mount(
                phpRuntime.FS.filesystems.NODEFS,
                { root: absoluteSourcePath },
                symlinkMountPath
              );
            }
          }
          return symlinkMountPath;
        };
      }
      phpRuntime.FS.root.node_ops = {
        ...phpRuntime.FS.root.node_ops,
        statfs: phpRuntime.FS.filesystems.NODEFS.node_ops.statfs
      };
      phpRuntime.FS.root.mount.opts.root = ".";
    }
  };
  if (options?.withXdebug === true) {
    emscriptenOptions = await withXdebug(
      phpVersion,
      emscriptenOptions,
      options.xdebug
    );
  }
  if (options?.withIntl === true) {
    emscriptenOptions = await withIntl(phpVersion, emscriptenOptions);
  }
  if (options?.withRedis === true) {
    emscriptenOptions = await withRedis(phpVersion, emscriptenOptions);
  }
  if (options?.withMemcached === true) {
    emscriptenOptions = await withMemcached(phpVersion, emscriptenOptions);
  }
  emscriptenOptions = await withNetworking(emscriptenOptions);
  const phpLoaderModule = await getPHPLoaderModule(phpVersion);
  const runtimeId = await loadPHPRuntime(phpLoaderModule, emscriptenOptions);
  return runtimeId;
}

// packages/php-wasm/node/src/lib/use-host-filesystem.ts
import { lstatSync as lstatSync2, readdirSync } from "node:fs";

// packages/php-wasm/node/src/lib/node-fs-mount.ts
import {
  FSHelpers as FSHelpers6
} from "@php-wasm/universal";
import { isParentOf } from "@php-wasm/util";
import { lstatSync } from "fs";
import { dirname as dirname2 } from "path";
function createNodeFsMountHandler(localPath) {
  return function(php, FS, vfsMountPoint) {
    let removeVfsNode = false;
    if (!FSHelpers6.fileExists(FS, vfsMountPoint)) {
      const lstat = lstatSync(localPath);
      if (lstat.isFile() || lstat.isSymbolicLink()) {
        FS.mkdirTree(dirname2(vfsMountPoint));
        FS.writeFile(vfsMountPoint, "");
      } else if (lstat.isDirectory()) {
        FS.mkdirTree(vfsMountPoint);
      } else {
        throw new Error(
          "Unsupported file type. PHP-wasm supports only symlinks that link to files, directories, or symlinks."
        );
      }
      removeVfsNode = true;
    }
    let lookup2;
    try {
      lookup2 = FS.lookupPath(vfsMountPoint);
    } catch (e) {
      const error = e;
      if (error.errno === 44) {
        throw new Error(
          `Unable to access the mount point ${vfsMountPoint} in VFS after attempting to create it.`
        );
      }
      throw e;
    }
    FS.mount(FS.filesystems["NODEFS"], { root: localPath }, vfsMountPoint);
    return () => {
      FS.unmount(vfsMountPoint);
      if (removeVfsNode) {
        if (FS.isDir(lookup2.node.mode)) {
          if (isParentOf(vfsMountPoint, FS.cwd())) {
            throw new Error(
              `Cannot remove the VFS directory "${vfsMountPoint}" on umount cleanup \u2013 it is a parent of the CWD "${FS.cwd()}". Change CWD before unmounting or explicitly disable post-unmount node cleanup with createNodeFsMountHandler(path, {cleanupNodesOnUnmount: false}).`
            );
          }
          FS.rmdir(vfsMountPoint);
        } else {
          FS.unlink(vfsMountPoint);
        }
      }
    };
  };
}

// packages/php-wasm/node/src/lib/use-host-filesystem.ts
function useHostFilesystem(php) {
  const dirs = readdirSync("/").filter((file) => file !== "dev").filter((file) => file !== "proc").map((file) => `/${file}`).filter((file) => {
    try {
      return statPathFollowSymlinks(file).isDirectory();
    } catch {
      return false;
    }
  });
  for (const dir of dirs) {
    if (!php.fileExists(dir)) {
      php.mkdirTree(dir);
    }
    php.mount(dir, createNodeFsMountHandler(dir));
  }
  php.chdir(process.cwd());
}
function statPathFollowSymlinks(path2) {
  let stat = lstatSync2(path2);
  if (stat.isSymbolicLink()) {
    const fs6 = __require("fs");
    let target = path2;
    const seen = /* @__PURE__ */ new Set();
    while (true) {
      if (seen.has(target)) {
        throw new Error(`Symlink loop detected: ${path2}`);
      }
      seen.add(target);
      const linkStat = lstatSync2(target);
      if (linkStat.isSymbolicLink()) {
        target = fs6.realpathSync(target);
        continue;
      }
      stat = linkStat;
      break;
    }
  }
  return stat;
}

// packages/php-wasm/node/src/lib/file-lock-manager-for-node.ts
import { logger as logger2 } from "@php-wasm/logger";
import { openSync, closeSync } from "fs";
var MAX_64BIT_OFFSET = BigInt(2n ** 64n - 1n);
var FileLockManagerForNode = class {
  /**
   * Create a new FileLockManagerForNode instance.
   *
   * @param nativeFlockSync A synchronous flock() function to lock files via the host OS.
   */
  constructor(nativeFlockSync = function flockSyncNoOp() {
  }) {
    this.nativeFlockSync = nativeFlockSync;
    this.locks = /* @__PURE__ */ new Map();
  }
  /**
   * Lock the whole file.
   *
   * @param path The path to the file to lock. This should be the path
   *             of the file in the native filesystem.
   * @param op The whole file lock operation to perform.
   * @returns True if the lock was granted, false otherwise.
   */
  lockWholeFile(path2, op) {
    if (this.locks.get(path2) === void 0) {
      if (op.type === "unlock") {
        return true;
      }
      const maybeLock = FileLock.maybeCreate(
        path2,
        op.type,
        this.nativeFlockSync
      );
      if (maybeLock === void 0) {
        return false;
      }
      this.locks.set(path2, maybeLock);
    }
    const lock = this.locks.get(path2);
    const result = lock.lockWholeFile(op);
    this.forgetPathIfUnlocked(path2);
    return result;
  }
  /**
   * Lock a byte range.
   *
   * @param path The path to the file to lock. This should be the path
   *             of the file in the native filesystem.
   * @param requestedLock The byte range lock to perform.
   * @returns True if the lock was granted, false otherwise.
   */
  lockFileByteRange(path2, requestedLock) {
    if (!this.locks.has(path2)) {
      if (requestedLock.type === "unlocked") {
        return true;
      }
      const maybeLock = FileLock.maybeCreate(
        path2,
        requestedLock.type,
        this.nativeFlockSync
      );
      if (maybeLock === void 0) {
        return false;
      }
      this.locks.set(path2, maybeLock);
    }
    const lock = this.locks.get(path2);
    return lock.lockFileByteRange(requestedLock);
  }
  /**
   * Find the first conflicting byte range lock.
   *
   * @param path The path to the file to find the conflicting lock for.
   * @param desiredLock The desired byte range lock.
   * @returns The first conflicting byte range lock, or undefined if no conflicting lock exists.
   */
  findFirstConflictingByteRangeLock(path2, desiredLock) {
    const lock = this.locks.get(path2);
    if (lock === void 0) {
      return void 0;
    }
    return lock.findFirstConflictingByteRangeLock(desiredLock);
  }
  /**
   * Release all locks for the given process.
   *
   * @param pid The process ID to release locks for.
   */
  releaseLocksForProcess(pid) {
    for (const [path2, lock] of this.locks.entries()) {
      lock.releaseLocksForProcess(pid);
      this.forgetPathIfUnlocked(path2);
    }
  }
  /**
   * Release all locks for the given process and file descriptor.
   *
   * @param pid The process ID to release locks for.
   * @param fd The file descriptor to release locks for.
   * @param path The path to the file to release locks for.
   */
  releaseLocksForProcessFd(pid, fd, nativePath) {
    const lock = this.locks.get(nativePath);
    if (!lock) {
      return;
    }
    lock.releaseLocksForProcessFd(pid, fd);
    this.forgetPathIfUnlocked(nativePath);
  }
  /**
   * Forget the path if it is unlocked.
   *
   * @param path The path to the file to forget.
   */
  forgetPathIfUnlocked(path2) {
    const lock = this.locks.get(path2);
    if (!lock) {
      return;
    }
    if (lock.isUnlocked()) {
      lock.dispose();
      this.locks.delete(path2);
    }
  }
};
var FileLock = class _FileLock {
  /**
   * Create a new FileLock instance for the given file and mode.
   * Fail if the underlying native file lock cannot be acquired.
   *
   * @param path The path to the file to lock
   * @param mode The type of lock to acquire
   * @returns A FileLock instance if the lock was acquired, undefined otherwise
   */
  static maybeCreate(path2, mode, nativeFlockSync) {
    let fd;
    try {
      fd = openSync(path2, "a+");
      const flockFlags = mode === "exclusive" ? "exnb" : "shnb";
      nativeFlockSync(fd, flockFlags);
      const nativeLock = { fd, mode, nativeFlockSync };
      return new _FileLock(nativeLock);
    } catch {
      if (fd !== void 0) {
        try {
          closeSync(fd);
        } catch (error) {
          logger2.error(
            "Error closing locking file descriptor",
            error
          );
        }
      }
      return void 0;
    }
  }
  constructor(nativeLock) {
    this.nativeLock = nativeLock;
    this.rangeLocks = new FileLockIntervalTree();
    this.wholeFileLock = { type: "unlocked" };
  }
  /**
   * Close the file descriptor and release the native lock.
   *
   * @TODO Replace this with a Symbol.dispose property once supported by all JS runtimes.
   */
  dispose() {
    try {
      closeSync(this.nativeLock.fd);
    } catch (error) {
      logger2.error("Error closing locking file descriptor", error);
    }
  }
  /**
   * Lock the whole file.
   *
   * This method corresponds to the flock() function.
   *
   * @param op The whole file lock operation to perform.
   * @returns True if the lock was granted, false otherwise.
   */
  lockWholeFile(op) {
    if (op.type === "unlock") {
      const originalType = this.wholeFileLock.type;
      if (originalType === "unlocked") {
      } else if (this.wholeFileLock.type === "exclusive" && this.wholeFileLock.pid === op.pid && this.wholeFileLock.fd === op.fd) {
        this.wholeFileLock = { type: "unlocked" };
      } else if (this.wholeFileLock.type === "shared" && this.wholeFileLock.pidFds.has(op.pid) && this.wholeFileLock.pidFds.get(op.pid).has(op.fd)) {
        this.wholeFileLock.pidFds.get(op.pid).delete(op.fd);
        if (this.wholeFileLock.pidFds.get(op.pid).size === 0) {
          this.wholeFileLock.pidFds.delete(op.pid);
        }
        if (this.wholeFileLock.pidFds.size === 0) {
          this.wholeFileLock = { type: "unlocked" };
        }
      }
      if (!this.ensureCompatibleNativeLock()) {
        logger2.error(
          "Unable to update native lock after removing a whole file lock."
        );
      }
      return true;
    }
    if (this.isThereAConflictWithRequestedWholeFileLock(op)) {
      return false;
    }
    if (!this.ensureCompatibleNativeLock({
      overrideWholeFileLockType: op.type
    })) {
      return false;
    }
    if (op.type === "exclusive") {
      this.wholeFileLock = {
        type: "exclusive",
        pid: op.pid,
        fd: op.fd
      };
      return true;
    }
    if (op.type === "shared") {
      if (this.wholeFileLock.type !== "shared") {
        this.wholeFileLock = {
          type: "shared",
          pidFds: /* @__PURE__ */ new Map()
        };
      }
      const sharedLock = this.wholeFileLock;
      if (!sharedLock.pidFds.has(op.pid)) {
        sharedLock.pidFds.set(op.pid, /* @__PURE__ */ new Set());
      }
      sharedLock.pidFds.get(op.pid).add(op.fd);
      return true;
    }
    throw new Error(`Unexpected wholeFileLock() op: '${op.type}'`);
  }
  /**
   * Lock a byte range.
   *
   * This method corresponds to the fcntl() F_SETLK command.
   *
   * @param requestedLock The byte range lock to perform.
   * @returns True if the lock was granted, false otherwise.
   */
  lockFileByteRange(requestedLock) {
    if (requestedLock.start === requestedLock.end) {
      requestedLock = {
        ...requestedLock,
        end: MAX_64BIT_OFFSET
      };
    }
    if (requestedLock.type === "unlocked") {
      const overlappingLocksBySameProcess = this.rangeLocks.findOverlapping(requestedLock).filter((lock) => lock.pid === requestedLock.pid);
      for (const overlappingLock of overlappingLocksBySameProcess) {
        this.rangeLocks.remove(overlappingLock);
        if (overlappingLock.start < requestedLock.start) {
          this.rangeLocks.insert({
            ...overlappingLock,
            end: requestedLock.start
          });
        }
        if (overlappingLock.end > requestedLock.end) {
          this.rangeLocks.insert({
            ...overlappingLock,
            start: requestedLock.end
          });
        }
      }
      if (!this.ensureCompatibleNativeLock()) {
        logger2.error(
          "Unable to update native lock after removing a byte range lock."
        );
      }
      return true;
    }
    if (this.isThereAConflictWithRequestedRangeLock(requestedLock)) {
      return false;
    }
    if (!this.ensureCompatibleNativeLock({
      overrideRangeLockType: requestedLock.type
    })) {
      return false;
    }
    const overlappingLocksFromSameProcess = this.rangeLocks.findOverlapping(requestedLock).filter((lock) => lock.pid === requestedLock.pid);
    let minStart = requestedLock.start;
    let maxEnd = requestedLock.end;
    for (const overlappingLock of overlappingLocksFromSameProcess) {
      this.rangeLocks.remove(overlappingLock);
      if (overlappingLock.start < minStart) {
        minStart = overlappingLock.start;
      }
      if (overlappingLock.end > maxEnd) {
        maxEnd = overlappingLock.end;
      }
    }
    const mergedLock = {
      ...requestedLock,
      start: minStart,
      end: maxEnd
    };
    this.rangeLocks.insert(mergedLock);
    return true;
  }
  /**
   * Find the first conflicting byte range lock.
   *
   * This method corresponds to the fcntl() F_GETLK command.
   *
   * @param desiredLock The desired byte range lock.
   * @returns The first conflicting byte range lock, or undefined if no conflicting lock exists.
   */
  findFirstConflictingByteRangeLock(desiredLock) {
    const overlappingLocks = this.rangeLocks.findOverlapping(desiredLock);
    const firstConflictingRangeLock = overlappingLocks.find(
      (lock) => lock.pid !== desiredLock.pid && (desiredLock.type === "exclusive" || lock.type === "exclusive")
    );
    if (firstConflictingRangeLock) {
      return firstConflictingRangeLock;
    }
    if (this.wholeFileLock.type === "unlocked") {
      return void 0;
    }
    const wfl = this.wholeFileLock;
    if (wfl.type === "exclusive" || desiredLock.type === "exclusive") {
      return {
        type: this.wholeFileLock.type,
        start: 0n,
        end: 0n,
        pid: -1
      };
    }
    return void 0;
  }
  /**
   * Release all locks for the given process.
   *
   * @param pid The process ID to release locks for.
   */
  releaseLocksForProcess(pid) {
    for (const rangeLock of this.rangeLocks.findLocksForProcess(pid)) {
      this.lockFileByteRange({
        ...rangeLock,
        type: "unlocked"
      });
    }
    if (this.wholeFileLock.type === "exclusive" && this.wholeFileLock.pid === pid) {
      this.lockWholeFile({
        pid,
        fd: this.wholeFileLock.fd,
        type: "unlock"
      });
    } else if (this.wholeFileLock.type === "shared" && this.wholeFileLock.pidFds.has(pid)) {
      for (const fd of this.wholeFileLock.pidFds.get(pid)) {
        this.lockWholeFile({
          pid,
          fd,
          type: "unlock"
        });
      }
    }
  }
  /**
   * Release all locks for the given process and file descriptor.
   *
   * @param pid The process ID to release locks for.
   * @param fd The file descriptor to release locks for.
   */
  releaseLocksForProcessFd(pid, fd) {
    for (const rangeLock of this.rangeLocks.findLocksForProcess(pid)) {
      this.lockFileByteRange({
        ...rangeLock,
        type: "unlocked"
      });
    }
    this.lockWholeFile({
      pid,
      fd,
      type: "unlock"
    });
  }
  /**
   * Check if the file lock is unlocked.
   *
   * @returns True if the file lock is unlocked, false otherwise.
   */
  isUnlocked() {
    return this.wholeFileLock.type === "unlocked" && this.rangeLocks.isEmpty();
  }
  /**
   * Ensure that the native lock is compatible with the php-wasm lock,
   * upgrading or downgrading as needed.
   *
   * @param overrideWholeFileLockType If provided, use this type for the whole file lock.
   * @param overrideRangeLockType If provided, use this type for the range lock.
   * @returns True if the native lock was upgraded or downgraded, false otherwise.
   */
  ensureCompatibleNativeLock({
    overrideWholeFileLockType,
    overrideRangeLockType
  } = {}) {
    const wholeFileLockType = overrideWholeFileLockType ?? this.wholeFileLock.type;
    const rangeLockType = overrideRangeLockType ?? this.rangeLocks.findStrictestExistingLockType();
    let requiredNativeLockType;
    if (wholeFileLockType === "exclusive" || rangeLockType === "exclusive") {
      requiredNativeLockType = "exclusive";
    } else if (wholeFileLockType === "shared" || rangeLockType === "shared") {
      requiredNativeLockType = "shared";
    } else {
      requiredNativeLockType = "unlock";
    }
    if (this.nativeLock.mode === requiredNativeLockType) {
      return true;
    }
    const flockFlags = requiredNativeLockType === "exclusive" && "exnb" || requiredNativeLockType === "shared" && "shnb" || "un";
    try {
      this.nativeLock.nativeFlockSync(this.nativeLock.fd, flockFlags);
      this.nativeLock.mode = requiredNativeLockType;
      return true;
    } catch {
      return false;
    }
  }
  /**
   * Check if a lock exists that conflicts with the requested range lock.
   *
   * @param requestedLock The desired byte range lock.
   * @returns True if a conflicting lock exists, false otherwise.
   */
  isThereAConflictWithRequestedRangeLock(requestedLock) {
    return this.findFirstConflictingByteRangeLock(requestedLock) !== void 0;
  }
  /**
   * Check if a lock exists that conflicts with the requested whole-file lock.
   *
   * @param requestedLock The desired whole-file lock.
   * @returns True if a conflicting lock exists, false otherwise.
   */
  isThereAConflictWithRequestedWholeFileLock(requestedLock) {
    if (requestedLock.type === "exclusive") {
      if (this.wholeFileLock.type === "exclusive" && (this.wholeFileLock.fd !== requestedLock.fd || this.wholeFileLock.pid !== requestedLock.pid)) {
        return true;
      }
      if (this.wholeFileLock.type === "shared" && Array.from(this.wholeFileLock.pidFds).some(
        ([pid]) => pid !== requestedLock.pid
      )) {
        return true;
      }
      const overlappingLocks = this.rangeLocks.findOverlapping({
        type: "unlocked",
        start: 0n,
        end: MAX_64BIT_OFFSET,
        pid: -1
      });
      if (overlappingLocks.length > 0) {
        return true;
      }
      return false;
    }
    if (requestedLock.type === "shared") {
      if (this.wholeFileLock.type === "exclusive" && this.wholeFileLock.pid !== requestedLock.pid) {
        return true;
      }
      const overlappingLocks = this.rangeLocks.findOverlapping({
        type: "unlocked",
        start: 0n,
        end: MAX_64BIT_OFFSET,
        pid: -1
      });
      const exclusiveRangeLocks = overlappingLocks.filter(
        (lock) => lock.type === "exclusive"
      );
      if (exclusiveRangeLocks.length > 0) {
        return true;
      }
      return false;
    }
    return false;
  }
};
var IntervalNode = class {
  constructor(range) {
    this.left = null;
    this.right = null;
    this.range = range;
    this.max = range.end;
  }
};
var FileLockIntervalTree = class {
  constructor() {
    this.root = null;
  }
  isEmpty() {
    return this.root === null;
  }
  /**
   * Insert a new locked range into the tree
   */
  insert(range) {
    this.root = this.insertNode(this.root, range);
  }
  /**
   * Find all ranges that overlap with the given range
   */
  findOverlapping(range) {
    const result = [];
    this.findOverlappingRanges(this.root, range, result);
    return result;
  }
  /**
   * Remove a lock range from the tree
   */
  remove(range) {
    this.root = this.removeNode(this.root, range);
  }
  /**
   * Find all ranges locked by the given process.
   *
   * @param pid The process ID to find locks for.
   * @returns All locked ranges for the given process.
   */
  findLocksForProcess(pid) {
    const result = [];
    this.findLocksForProcessInNode(this.root, pid, result);
    return result;
  }
  /**
   * Find the strictest existing lock type in the range lock tree.
   *
   * @returns The strictest existing lock type, or 'unlocked' if no locks exist.
   */
  findStrictestExistingLockType() {
    let maxType = "unlocked";
    const traverse = (node) => {
      if (!node) {
        return;
      }
      if (node.range.type === "exclusive") {
        maxType = "exclusive";
        return;
      }
      if (node.range.type === "shared") {
        maxType = "shared";
      }
      traverse(node.left);
      traverse(node.right);
    };
    traverse(this.root);
    return maxType;
  }
  insertNode(node, range) {
    if (!node) {
      return new IntervalNode(range);
    }
    if (range.start < node.range.start) {
      node.left = this.insertNode(node.left, range);
    } else {
      node.right = this.insertNode(node.right, range);
    }
    node.max = this.bigintMax(node.max, range.end);
    return node;
  }
  bigintMax(...args) {
    return args.reduce((max, current) => {
      return current > max ? current : max;
    }, args[0]);
  }
  findOverlappingRanges(node, range, result) {
    if (!node) {
      return;
    }
    if (this.doRangesOverlap(node.range, range)) {
      result.push(node.range);
    }
    if (node.left && node.left.max >= range.start) {
      this.findOverlappingRanges(node.left, range, result);
    }
    if (node.right && node.range.start <= range.end) {
      this.findOverlappingRanges(node.right, range, result);
    }
  }
  doRangesOverlap(a, b) {
    return a.start < b.end && b.start < a.end;
  }
  removeNode(node, range) {
    if (!node) {
      return null;
    }
    if (this.areRangesEqual(node.range, range)) {
      if (!node.left) {
        return node.right;
      }
      if (!node.right) {
        return node.left;
      }
      const successor = this.findMin(node.right);
      node.range = successor.range;
      node.right = this.removeNode(node.right, successor.range);
    } else if (range.start < node.range.start) {
      node.left = this.removeNode(node.left, range);
    } else {
      node.right = this.removeNode(node.right, range);
    }
    node.max = node.range.end;
    if (node.left) {
      node.max = this.bigintMax(node.max, node.left.max);
    }
    if (node.right) {
      node.max = this.bigintMax(node.max, node.right.max);
    }
    return node;
  }
  findMin(node) {
    let current = node;
    while (current.left) {
      current = current.left;
    }
    return current;
  }
  areRangesEqual(a, b) {
    return a.start === b.start && a.end === b.end && a.pid === b.pid;
  }
  findLocksForProcessInNode(node, pid, result) {
    if (!node) {
      return;
    }
    if (node.range.pid === pid) {
      result.push(node.range);
    }
    this.findLocksForProcessInNode(node.left, pid, result);
    this.findLocksForProcessInNode(node.right, pid, result);
  }
};
export {
  FileLock,
  FileLockManagerForNode,
  createNodeFsMountHandler,
  getPHPLoaderModule,
  loadNodeRuntime,
  useHostFilesystem,
  withNetworking,
  withXdebug
};
