(function (factory) {
    typeof define === 'function' && define.amd ? define(factory) :
    factory();
})((function () { 'use strict';

    (function() {
        const env = {"NODE_ENV":"production"};
        try {
            if (process) {
                process.env = Object.assign({}, process.env);
                Object.assign(process.env, env);
                return;
            }
        } catch (e) {} // avoid ReferenceError: process is not defined
        globalThis.process = { env:env };
    })();

    /* This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

    function isNode() {
      try {
        return process.release.name == "node";
      } catch (e) {
        return false;
      }
    }

    function isNodeTest() {
      return isNode() && process.env.NODE_ENV != "production";
    }

    let assert;
    // TODO: try to enable these assertions on mochitest by also enabling it on:
    //   import flags from "devtools/shared/flags";
    //   if (flags.testing)
    // Unfortunately it throws a lot on mochitests...

    if (isNodeTest()) {
      assert = function (condition, message) {
        if (!condition) {
          throw new Error(`Assertion failure: ${message}`);
        }
      };
    } else {
      assert = function () {};
    }
    var assert$1 = assert;

    /* This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

    function escapeRegExp(str) {
      const reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
      return str.replace(reRegExpChar, "\\$&");
    }

    /**
     * Ignore doing outline matches for less than 3 whitespaces
     *
     * @memberof utils/source-search
     * @static
     */
    function ignoreWhiteSpace(str) {
      return /^\s{0,2}$/.test(str) ? "(?!\\s*.*)" : str;
    }

    function wholeMatch(query, wholeWord) {
      if (query === "" || !wholeWord) {
        return query;
      }

      return `\\b${query}\\b`;
    }

    function buildFlags(caseSensitive, isGlobal) {
      if (caseSensitive && isGlobal) {
        return "g";
      }

      if (!caseSensitive && isGlobal) {
        return "gi";
      }

      if (!caseSensitive && !isGlobal) {
        return "i";
      }

      return null;
    }

    function buildQuery(
      originalQuery,
      modifiers,
      { isGlobal = false, ignoreSpaces = false }
    ) {
      const { caseSensitive, regexMatch, wholeWord } = modifiers;

      if (originalQuery === "") {
        return new RegExp(originalQuery);
      }

      // Remove the backslashes at the end of the query as it
      // breaks the RegExp
      let query = originalQuery.replace(/\\$/, "");

      // If we don't want to do a regexMatch, we need to escape all regex related characters
      // so they would actually match.
      if (!regexMatch) {
        query = escapeRegExp(query);
      }

      // ignoreWhiteSpace might return a negative lookbehind, and in such case, we want it
      // to be consumed as a RegExp part by the callsite, so this needs to be called after
      // the regexp is escaped.
      if (ignoreSpaces) {
        query = ignoreWhiteSpace(query);
      }

      query = wholeMatch(query, wholeWord);
      const flags = buildFlags(caseSensitive, isGlobal);

      if (flags) {
        return new RegExp(query, flags);
      }

      return new RegExp(query);
    }

    function getMatches(query, text, options) {
      if (!query || !text || !options) {
        return [];
      }
      const regexQuery = buildQuery(query, options, {
        isGlobal: true,
      });
      const matchedLocations = [];
      const lines = text.split("\n");
      for (let i = 0; i < lines.length; i++) {
        let singleMatch;
        const line = lines[i];
        while ((singleMatch = regexQuery.exec(line)) !== null) {
          // Flow doesn't understand the test above.
          if (!singleMatch) {
            throw new Error("no singleMatch");
          }

          matchedLocations.push({
            line: i,
            ch: singleMatch.index,
            match: singleMatch[0],
          });

          // When the match is an empty string the regexQuery.lastIndex will not
          // change resulting in an infinite loop so we need to check for this and
          // increment it manually in that case.  See issue #7023
          if (singleMatch[0] === "") {
            assert$1(
              !regexQuery.unicode,
              "lastIndex++ can cause issues in unicode mode"
            );
            regexQuery.lastIndex++;
          }
        }
      }
      return matchedLocations;
    }

    function findSourceMatches(content, queryText, options) {
      if (queryText == "") {
        return [];
      }

      const text = content.value;
      const lines = text.split("\n");

      return getMatches(queryText, text, options).map(({ line, ch, match }) => {
        const { value, matchIndex } = truncateLine(lines[line], ch);
        return {
          line: line + 1,
          column: ch,

          matchIndex,
          match,
          value,
        };
      });
    }

    // This is used to find start of a word, so that cropped string look nice
    const startRegex = /([ !@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/g;
    // Similarly, find
    const endRegex = new RegExp(
      [
        "([ !@#$%^&*()_+-=[]{};':\"\\|,.<>/?])",
        '[^ !@#$%^&*()_+-=[]{};\':"\\|,.<>/?]*$"/',
      ].join("")
    );
    // For texts over 100 characters this truncates the text (for display)
    // around the context of the matched text.
    function truncateLine(text, column) {
      if (text.length < 100) {
        return {
          matchIndex: column,
          value: text,
        };
      }

      // Initially take 40 chars left to the match
      const offset = Math.max(column - 40, 0);
      // 400 characters should be enough to figure out the context of the match
      const truncStr = text.slice(offset, column + 400);
      let start = truncStr.search(startRegex);
      let end = truncStr.search(endRegex);

      if (start > column) {
        // No word separator found before the match, so we take all characters
        // before the match
        start = -1;
      }
      if (end < column) {
        end = truncStr.length;
      }
      const value = truncStr.slice(start + 1, end);

      return {
        matchIndex: column - start - offset - 1,
        value,
      };
    }

    var workerUtilsExports = {};
    var workerUtils = {
      get exports(){ return workerUtilsExports; },
      set exports(v){ workerUtilsExports = v; },
    };

    (function (module) {

    	class WorkerDispatcher {
    	  #msgId = 1;
    	  #worker = null;
    	  // Map of message ids -> promise resolution functions, for dispatching worker responses
    	  #pendingCalls = new Map();
    	  #url = "";

    	  constructor(url) {
    	    this.#url = url;
    	  }

    	  start() {
    	    // When running in debugger jest test, we don't have access to ChromeWorker
    	    if (typeof ChromeWorker == "function") {
    	      this.#worker = new ChromeWorker(this.#url);
    	    } else {
    	      this.#worker = new Worker(this.#url);
    	    }
    	    this.#worker.onerror = err => {
    	      console.error(`Error in worker ${this.#url}`, err.message);
    	    };
    	    this.#worker.addEventListener("message", this.#onMessage);
    	  }

    	  stop() {
    	    if (!this.#worker) {
    	      return;
    	    }

    	    this.#worker.removeEventListener("message", this.#onMessage);
    	    this.#worker.terminate();
    	    this.#worker = null;
    	    this.#pendingCalls.clear();
    	  }

    	  task(method, { queue = false } = {}) {
    	    const calls = [];
    	    const push = args => {
    	      return new Promise((resolve, reject) => {
    	        if (queue && calls.length === 0) {
    	          Promise.resolve().then(flush);
    	        }

    	        calls.push({ args, resolve, reject });

    	        if (!queue) {
    	          flush();
    	        }
    	      });
    	    };

    	    const flush = () => {
    	      const items = calls.slice();
    	      calls.length = 0;

    	      if (!this.#worker) {
    	        this.start();
    	      }

    	      const id = this.#msgId++;
    	      this.#worker.postMessage({
    	        id,
    	        method,
    	        calls: items.map(item => item.args),
    	      });

    	      this.#pendingCalls.set(id, items);
    	    };

    	    return (...args) => push(args);
    	  }

    	  invoke(method, ...args) {
    	    return this.task(method)(...args);
    	  }

    	  #onMessage = ({ data: result }) => {
    	    const items = this.#pendingCalls.get(result.id);
    	    this.#pendingCalls.delete(result.id);
    	    if (!items) {
    	      return;
    	    }

    	    if (!this.#worker) {
    	      return;
    	    }

    	    result.results.forEach((resultData, i) => {
    	      const { resolve, reject } = items[i];

    	      if (resultData.error) {
    	        const err = new Error(resultData.message);
    	        err.metadata = resultData.metadata;
    	        reject(err);
    	      } else {
    	        resolve(resultData.response);
    	      }
    	    });
    	  };
    	}

    	function workerHandler(publicInterface) {
    	  return function (msg) {
    	    const { id, method, calls } = msg.data;

    	    Promise.all(
    	      calls.map(args => {
    	        try {
    	          const response = publicInterface[method].apply(undefined, args);
    	          if (response instanceof Promise) {
    	            return response.then(
    	              val => ({ response: val }),
    	              err => asErrorMessage(err)
    	            );
    	          }
    	          return { response };
    	        } catch (error) {
    	          return asErrorMessage(error);
    	        }
    	      })
    	    ).then(results => {
    	      globalThis.postMessage({ id, results });
    	    });
    	  };
    	}

    	function asErrorMessage(error) {
    	  if (typeof error === "object" && error && "message" in error) {
    	    // Error can't be sent via postMessage, so be sure to convert to
    	    // string.
    	    return {
    	      error: true,
    	      message: error.message,
    	      metadata: error.metadata,
    	    };
    	  }

    	  return {
    	    error: true,
    	    message: error == null ? error : error.toString(),
    	    metadata: undefined,
    	  };
    	}

    	// Might be loaded within a worker thread where `module` isn't available.
    	{
    	  module.exports = {
    	    WorkerDispatcher,
    	    workerHandler,
    	  };
    	}
    } (workerUtils));

    self.onmessage = workerUtilsExports.workerHandler({ getMatches, findSourceMatches });

}));
