import { mockObject } from './index.js';
import { M as MockerRegistry, R as RedirectedModule, A as AutomockedModule } from './chunk-registry.js';
import { e as extname, j as join } from './chunk-pathe.M-eThtNZ.js';

function createSimpleStackTrace(options) {
  const { message = "$$stack trace error", stackTraceLimit = 1 } = options || {};
  const limit = Error.stackTraceLimit;
  const prepareStackTrace = Error.prepareStackTrace;
  Error.stackTraceLimit = stackTraceLimit;
  Error.prepareStackTrace = (e) => e.stack;
  const err = new Error(message);
  const stackTrace = err.stack || "";
  Error.prepareStackTrace = prepareStackTrace;
  Error.stackTraceLimit = limit;
  return stackTrace;
}

const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const intToChar = new Uint8Array(64);
const charToInt = new Uint8Array(128);
for (let i = 0; i < chars.length; i++) {
  const c = chars.charCodeAt(i);
  intToChar[i] = c;
  charToInt[c] = i;
}
var UrlType;
(function(UrlType2) {
  UrlType2[UrlType2["Empty"] = 1] = "Empty";
  UrlType2[UrlType2["Hash"] = 2] = "Hash";
  UrlType2[UrlType2["Query"] = 3] = "Query";
  UrlType2[UrlType2["RelativePath"] = 4] = "RelativePath";
  UrlType2[UrlType2["AbsolutePath"] = 5] = "AbsolutePath";
  UrlType2[UrlType2["SchemeRelative"] = 6] = "SchemeRelative";
  UrlType2[UrlType2["Absolute"] = 7] = "Absolute";
})(UrlType || (UrlType = {}));
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
function normalizeWindowsPath(input = "") {
  if (!input) {
    return input;
  }
  return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
}
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
function cwd() {
  if (typeof process !== "undefined" && typeof process.cwd === "function") {
    return process.cwd().replace(/\\/g, "/");
  }
  return "/";
}
const resolve = function(...arguments_) {
  arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
  let resolvedPath = "";
  let resolvedAbsolute = false;
  for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
    const path = index >= 0 ? arguments_[index] : cwd();
    if (!path || path.length === 0) {
      continue;
    }
    resolvedPath = `${path}/${resolvedPath}`;
    resolvedAbsolute = isAbsolute(path);
  }
  resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
  if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
    return `/${resolvedPath}`;
  }
  return resolvedPath.length > 0 ? resolvedPath : ".";
};
function normalizeString(path, allowAboveRoot) {
  let res = "";
  let lastSegmentLength = 0;
  let lastSlash = -1;
  let dots = 0;
  let char = null;
  for (let index = 0; index <= path.length; ++index) {
    if (index < path.length) {
      char = path[index];
    } else if (char === "/") {
      break;
    } else {
      char = "/";
    }
    if (char === "/") {
      if (lastSlash === index - 1 || dots === 1) ;
      else if (dots === 2) {
        if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
          if (res.length > 2) {
            const lastSlashIndex = res.lastIndexOf("/");
            if (lastSlashIndex === -1) {
              res = "";
              lastSegmentLength = 0;
            } else {
              res = res.slice(0, lastSlashIndex);
              lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
            }
            lastSlash = index;
            dots = 0;
            continue;
          } else if (res.length > 0) {
            res = "";
            lastSegmentLength = 0;
            lastSlash = index;
            dots = 0;
            continue;
          }
        }
        if (allowAboveRoot) {
          res += res.length > 0 ? "/.." : "..";
          lastSegmentLength = 2;
        }
      } else {
        if (res.length > 0) {
          res += `/${path.slice(lastSlash + 1, index)}`;
        } else {
          res = path.slice(lastSlash + 1, index);
        }
        lastSegmentLength = index - lastSlash - 1;
      }
      lastSlash = index;
      dots = 0;
    } else if (char === "." && dots !== -1) {
      ++dots;
    } else {
      dots = -1;
    }
  }
  return res;
}
const isAbsolute = function(p) {
  return _IS_ABSOLUTE_RE.test(p);
};
const CHROME_IE_STACK_REGEXP = /^\s*at .*(?:\S:\d+|\(native\))/m;
const SAFARI_NATIVE_CODE_REGEXP = /^(?:eval@)?(?:\[native code\])?$/;
function extractLocation(urlLike) {
  if (!urlLike.includes(":")) {
    return [urlLike];
  }
  const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
  const parts = regExp.exec(urlLike.replace(/^\(|\)$/g, ""));
  if (!parts) {
    return [urlLike];
  }
  let url = parts[1];
  if (url.startsWith("async ")) {
    url = url.slice(6);
  }
  if (url.startsWith("http:") || url.startsWith("https:")) {
    const urlObj = new URL(url);
    urlObj.searchParams.delete("import");
    urlObj.searchParams.delete("browserv");
    url = urlObj.pathname + urlObj.hash + urlObj.search;
  }
  if (url.startsWith("/@fs/")) {
    const isWindows = /^\/@fs\/[a-zA-Z]:\//.test(url);
    url = url.slice(isWindows ? 5 : 4);
  }
  return [url, parts[2] || void 0, parts[3] || void 0];
}
function parseSingleFFOrSafariStack(raw) {
  let line = raw.trim();
  if (SAFARI_NATIVE_CODE_REGEXP.test(line)) {
    return null;
  }
  if (line.includes(" > eval")) {
    line = line.replace(
      / line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
      ":$1"
    );
  }
  if (!line.includes("@") && !line.includes(":")) {
    return null;
  }
  const functionNameRegex = /((.*".+"[^@]*)?[^@]*)(@)/;
  const matches = line.match(functionNameRegex);
  const functionName = matches && matches[1] ? matches[1] : void 0;
  const [url, lineNumber, columnNumber] = extractLocation(
    line.replace(functionNameRegex, "")
  );
  if (!url || !lineNumber || !columnNumber) {
    return null;
  }
  return {
    file: url,
    method: functionName || "",
    line: Number.parseInt(lineNumber),
    column: Number.parseInt(columnNumber)
  };
}
function parseSingleStack(raw) {
  const line = raw.trim();
  if (!CHROME_IE_STACK_REGEXP.test(line)) {
    return parseSingleFFOrSafariStack(line);
  }
  return parseSingleV8Stack(line);
}
function parseSingleV8Stack(raw) {
  let line = raw.trim();
  if (!CHROME_IE_STACK_REGEXP.test(line)) {
    return null;
  }
  if (line.includes("(eval ")) {
    line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
  }
  let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
  const location = sanitizedLine.match(/ (\(.+\)$)/);
  sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
  const [url, lineNumber, columnNumber] = extractLocation(
    location ? location[1] : sanitizedLine
  );
  let method = location && sanitizedLine || "";
  let file = url && ["eval", "<anonymous>"].includes(url) ? void 0 : url;
  if (!file || !lineNumber || !columnNumber) {
    return null;
  }
  if (method.startsWith("async ")) {
    method = method.slice(6);
  }
  if (file.startsWith("file://")) {
    file = file.slice(7);
  }
  file = file.startsWith("node:") || file.startsWith("internal:") ? file : resolve(file);
  if (method) {
    method = method.replace(/__vite_ssr_import_\d+__\./g, "");
  }
  return {
    method,
    file,
    line: Number.parseInt(lineNumber),
    column: Number.parseInt(columnNumber)
  };
}

function createCompilerHints(options) {
  const globalThisAccessor = (options == null ? void 0 : options.globalThisKey) || "__vitest_mocker__";
  function _mocker() {
    return typeof globalThis[globalThisAccessor] !== "undefined" ? globalThis[globalThisAccessor] : new Proxy(
      {},
      {
        get(_, name) {
          throw new Error(
            `Vitest mocker was not initialized in this environment. vi.${String(name)}() is forbidden.`
          );
        }
      }
    );
  }
  return {
    hoisted(factory) {
      if (typeof factory !== "function") {
        throw new TypeError(
          `vi.hoisted() expects a function, but received a ${typeof factory}`
        );
      }
      return factory();
    },
    mock(path, factory) {
      if (typeof path !== "string") {
        throw new TypeError(
          `vi.mock() expects a string path, but received a ${typeof path}`
        );
      }
      const importer = getImporter("mock");
      _mocker().queueMock(
        path,
        importer,
        typeof factory === "function" ? () => factory(
          () => _mocker().importActual(
            path,
            importer
          )
        ) : factory
      );
    },
    unmock(path) {
      if (typeof path !== "string") {
        throw new TypeError(
          `vi.unmock() expects a string path, but received a ${typeof path}`
        );
      }
      _mocker().queueUnmock(path, getImporter("unmock"));
    },
    doMock(path, factory) {
      if (typeof path !== "string") {
        throw new TypeError(
          `vi.doMock() expects a string path, but received a ${typeof path}`
        );
      }
      const importer = getImporter("doMock");
      _mocker().queueMock(
        path,
        importer,
        typeof factory === "function" ? () => factory(
          () => _mocker().importActual(
            path,
            importer
          )
        ) : factory
      );
    },
    doUnmock(path) {
      if (typeof path !== "string") {
        throw new TypeError(
          `vi.doUnmock() expects a string path, but received a ${typeof path}`
        );
      }
      _mocker().queueUnmock(path, getImporter("doUnmock"));
    },
    async importActual(path) {
      return _mocker().importActual(
        path,
        getImporter("importActual")
      );
    },
    async importMock(path) {
      return _mocker().importMock(path, getImporter("importMock"));
    }
  };
}
function getImporter(name) {
  const stackTrace = /* @__PURE__ */ createSimpleStackTrace({ stackTraceLimit: 5 });
  const stackArray = stackTrace.split("\n");
  const importerStackIndex = stackArray.findIndex((stack2) => {
    return stack2.includes(` at Object.${name}`) || stack2.includes(`${name}@`);
  });
  const stack = /* @__PURE__ */ parseSingleStack(stackArray[importerStackIndex + 1]);
  return (stack == null ? void 0 : stack.file) || "";
}

const hot = import.meta.hot || {
  on: warn,
  off: warn,
  send: warn
};
function warn() {
  console.warn("Vitest mocker cannot work if Vite didn't establish WS connection.");
}
function rpc(event, data) {
  hot.send(event, data);
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      reject(new Error(`Failed to resolve ${event} in time`));
    }, 5e3);
    hot.on(`${event}:result`, function r(data2) {
      resolve(data2);
      clearTimeout(timeout);
      hot.off("vitest:mocks:resolvedId:result", r);
    });
  });
}

const { now } = Date;
class ModuleMocker {
  constructor(interceptor, rpc, spyOn, config) {
    this.interceptor = interceptor;
    this.rpc = rpc;
    this.spyOn = spyOn;
    this.config = config;
  }
  registry = new MockerRegistry();
  queue = /* @__PURE__ */ new Set();
  mockedIds = /* @__PURE__ */ new Set();
  async prepare() {
    if (!this.queue.size) {
      return;
    }
    await Promise.all([...this.queue.values()]);
  }
  async resolveFactoryModule(id) {
    const mock = this.registry.get(id);
    if (!mock || mock.type !== "manual") {
      throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
    }
    const result = await mock.resolve();
    return result;
  }
  getFactoryModule(id) {
    const mock = this.registry.get(id);
    if (!mock || mock.type !== "manual") {
      throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
    }
    if (!mock.cache) {
      throw new Error(`Mock ${id} wasn't resolved. This is probably a Vitest error. Please, open a new issue with reproduction.`);
    }
    return mock.cache;
  }
  async invalidate() {
    const ids = Array.from(this.mockedIds);
    if (!ids.length) {
      return;
    }
    await this.rpc.invalidate(ids);
    this.interceptor.invalidate();
    this.registry.clear();
  }
  async importActual(id, importer) {
    const resolved = await this.rpc.resolveId(id, importer);
    if (resolved == null) {
      throw new Error(
        `[vitest] Cannot resolve "${id}" imported from "${importer}"`
      );
    }
    const ext = extname(resolved.id);
    const url = new URL(resolved.url, location.href);
    const query = `_vitest_original&ext${ext}`;
    const actualUrl = `${url.pathname}${url.search ? `${url.search}&${query}` : `?${query}`}${url.hash}`;
    return this.wrapDynamicImport(() => import(
      /* @vite-ignore */
      actualUrl
    )).then((mod) => {
      if (!resolved.optimized || typeof mod.default === "undefined") {
        return mod;
      }
      const m = mod.default;
      return (m == null ? void 0 : m.__esModule) ? m : { ...typeof m === "object" && !Array.isArray(m) || typeof m === "function" ? m : {}, default: m };
    });
  }
  async importMock(rawId, importer) {
    await this.prepare();
    const { resolvedId, resolvedUrl, redirectUrl } = await this.rpc.resolveMock(
      rawId,
      importer,
      { mock: "auto" }
    );
    const mockUrl = this.resolveMockPath(cleanVersion(resolvedUrl));
    let mock = this.registry.get(mockUrl);
    if (!mock) {
      if (redirectUrl) {
        const resolvedRedirect = new URL(this.resolveMockPath(cleanVersion(redirectUrl)), location.href).toString();
        mock = new RedirectedModule(rawId, mockUrl, resolvedRedirect);
      } else {
        mock = new AutomockedModule(rawId, mockUrl);
      }
    }
    if (mock.type === "manual") {
      return await mock.resolve();
    }
    if (mock.type === "automock" || mock.type === "autospy") {
      const url = new URL(`/@id/${resolvedId}`, location.href);
      const query = url.search ? `${url.search}&t=${now()}` : `?t=${now()}`;
      const moduleObject = await import(
        /* @vite-ignore */
        `${url.pathname}${query}&mock=${mock.type}${url.hash}`
      );
      return this.mockObject(moduleObject, mock.type);
    }
    return import(
      /* @vite-ignore */
      mock.redirect
    );
  }
  mockObject(object, moduleType = "automock") {
    return mockObject({
      globalConstructors: {
        Object,
        Function,
        Array,
        Map,
        RegExp
      },
      spyOn: this.spyOn,
      type: moduleType
    }, object);
  }
  queueMock(rawId, importer, factoryOrOptions) {
    const promise = this.rpc.resolveMock(rawId, importer, {
      mock: typeof factoryOrOptions === "function" ? "factory" : (factoryOrOptions == null ? void 0 : factoryOrOptions.spy) ? "spy" : "auto"
    }).then(async ({ redirectUrl, resolvedId, resolvedUrl, needsInterop, mockType }) => {
      const mockUrl = this.resolveMockPath(cleanVersion(resolvedUrl));
      this.mockedIds.add(resolvedId);
      const factory = typeof factoryOrOptions === "function" ? async () => {
        const data = await factoryOrOptions();
        return needsInterop ? { default: data } : data;
      } : void 0;
      const mockRedirect = typeof redirectUrl === "string" ? new URL(this.resolveMockPath(cleanVersion(redirectUrl)), location.href).toString() : null;
      let module;
      if (mockType === "manual") {
        module = this.registry.register("manual", rawId, mockUrl, factory);
      } else if (mockType === "autospy") {
        module = this.registry.register("autospy", rawId, mockUrl);
      } else if (mockType === "redirect") {
        module = this.registry.register("redirect", rawId, mockUrl, mockRedirect);
      } else {
        module = this.registry.register("automock", rawId, mockUrl);
      }
      await this.interceptor.register(module);
    }).finally(() => {
      this.queue.delete(promise);
    });
    this.queue.add(promise);
  }
  queueUnmock(id, importer) {
    const promise = this.rpc.resolveId(id, importer).then(async (resolved) => {
      if (!resolved) {
        return;
      }
      const mockUrl = this.resolveMockPath(cleanVersion(resolved.url));
      this.mockedIds.add(resolved.id);
      this.registry.delete(mockUrl);
      await this.interceptor.delete(mockUrl);
    }).finally(() => {
      this.queue.delete(promise);
    });
    this.queue.add(promise);
  }
  // We need to await mock registration before importing the actual module
  // In case there is a mocked module in the import chain
  wrapDynamicImport(moduleFactory) {
    if (typeof moduleFactory === "function") {
      const promise = new Promise((resolve, reject) => {
        this.prepare().finally(() => {
          moduleFactory().then(resolve, reject);
        });
      });
      return promise;
    }
    return moduleFactory;
  }
  resolveMockPath(path) {
    const config = this.config;
    const fsRoot = join("/@fs/", config.root);
    if (path.startsWith(config.root)) {
      return path.slice(config.root.length);
    }
    if (path.startsWith(fsRoot)) {
      return path.slice(fsRoot.length);
    }
    return path;
  }
}
const versionRegexp = /(\?|&)v=\w{8}/;
function cleanVersion(url) {
  return url.replace(versionRegexp, "");
}

export { ModuleMocker as M, createCompilerHints as c, hot as h, rpc as r };
