// TODO: including these in blob-util.ts causes typedoc to generate docs for them,
// even with --excludePrivate ¯\_(ツ)_/¯
/** @private */

/* global Promise, Image, Blob, FileReader, atob, btoa,
   BlobBuilder, MSBlobBuilder, MozBlobBuilder, WebKitBlobBuilder, webkitURL */
/**
 * Shim for
 * [`new Blob()`](https://developer.mozilla.org/en-US/docs/Web/API/Blob.Blob)
 * to support
 * [older browsers that use the deprecated `BlobBuilder` API](http://caniuse.com/blob).
 *
 * Example:
 *
 * ```js
 * var myBlob = blobUtil.createBlob(['hello world'], {type: 'text/plain'});
 * ```
 *
 * @param parts - content of the Blob
 * @param properties - usually `{type: myContentType}`,
 *                           you can also pass a string for the content type
 * @returns Blob
 */
function createBlob(parts, properties) {
  parts = parts || [];
  properties = properties || {};
  if (typeof properties === 'string') {
    properties = {
      type: properties
    }; // infer content type
  }
  try {
    return new Blob(parts, properties);
  } catch (e) {
    if (e.name !== 'TypeError') {
      throw e;
    }
    var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder;
    var builder = new Builder();
    for (var i = 0; i < parts.length; i += 1) {
      builder.append(parts[i]);
    }
    return builder.getBlob(properties.type);
  }
}
/**
 * Convert a `Blob` to a binary string.
 *
 * Example:
 *
 * ```js
 * blobUtil.blobToBinaryString(blob).then(function (binaryString) {
 *   // success
 * }).catch(function (err) {
 *   // error
 * });
 * ```
 *
 * @param blob
 * @returns Promise that resolves with the binary string
 */
function blobToBinaryString$1(blob) {
  return new Promise(function (resolve, reject) {
    var reader = new FileReader();
    var hasBinaryString = typeof reader.readAsBinaryString === 'function';
    reader.onloadend = function () {
      var result = reader.result || '';
      if (hasBinaryString) {
        return resolve(result);
      }
      resolve(arrayBufferToBinaryString(result));
    };
    reader.onerror = reject;
    if (hasBinaryString) {
      reader.readAsBinaryString(blob);
    } else {
      reader.readAsArrayBuffer(blob);
    }
  });
}
/**
 * Convert a base64-encoded string to a `Blob`.
 *
 * Example:
 *
 * ```js
 * var blob = blobUtil.base64StringToBlob(base64String);
 * ```
 * @param base64 - base64-encoded string
 * @param type - the content type (optional)
 * @returns Blob
 */
function base64StringToBlob(base64, type) {
  var parts = [binaryStringToArrayBuffer(atob(base64))];
  return type ? createBlob(parts, {
    type: type
  }) : createBlob(parts);
}
/**
 * Convert a binary string to a `Blob`.
 *
 * Example:
 *
 * ```js
 * var blob = blobUtil.binaryStringToBlob(binaryString);
 * ```
 *
 * @param binary - binary string
 * @param type - the content type (optional)
 * @returns Blob
 */
function binaryStringToBlob$1(binary, type) {
  return base64StringToBlob(btoa(binary), type);
}
/**
 * Convert an `ArrayBuffer` to a binary string.
 *
 * Example:
 *
 * ```js
 * var myString = blobUtil.arrayBufferToBinaryString(arrayBuff)
 * ```
 *
 * @param buffer - array buffer
 * @returns binary string
 */
function arrayBufferToBinaryString(buffer) {
  var binary = '';
  var bytes = new Uint8Array(buffer);
  var length = bytes.byteLength;
  var i = -1;
  while (++i < length) {
    binary += String.fromCharCode(bytes[i]);
  }
  return binary;
}
/**
 * Convert a binary string to an `ArrayBuffer`.
 *
 * ```js
 * var myBuffer = blobUtil.binaryStringToArrayBuffer(binaryString)
 * ```
 *
 * @param binary - binary string
 * @returns array buffer
 */
function binaryStringToArrayBuffer(binary) {
  var length = binary.length;
  var buf = new ArrayBuffer(length);
  var arr = new Uint8Array(buf);
  var i = -1;
  while (++i < length) {
    arr[i] = binary.charCodeAt(i);
  }
  return buf;
}

const normalizeBlobError = error => {
  const ProgressEventConstructor = globalThis.ProgressEvent;
  if (error && ProgressEventConstructor && error instanceof ProgressEventConstructor && error.target &&
  // @ts-expect-error - target.error may not be in the type definition
  error.target.error) {
    // @ts-expect-error - target.error may not be in the type definition
    return error.target.error;
  }
  return error;
};

const normalizeLine = line => {
  if (line.startsWith('Error: ')) {
    return line.slice('Error: '.length);
  }
  if (line.startsWith('VError: ')) {
    return line.slice('VError: '.length);
  }
  return line;
};
const getCombinedMessage = (error, message) => {
  const stringifiedError = normalizeLine(`${error}`);
  if (message) {
    return `${message}: ${stringifiedError}`;
  }
  return stringifiedError;
};
const NewLine$2 = '\n';
const getNewLineIndex$1 = (string, startIndex = undefined) => {
  return string.indexOf(NewLine$2, startIndex);
};
const mergeStacks = (parent, child) => {
  if (!child) {
    return parent;
  }
  const parentNewLineIndex = getNewLineIndex$1(parent);
  const childNewLineIndex = getNewLineIndex$1(child);
  if (childNewLineIndex === -1) {
    return parent;
  }
  const parentFirstLine = parent.slice(0, parentNewLineIndex);
  const childRest = child.slice(childNewLineIndex);
  const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
  if (parentFirstLine.includes(childFirstLine)) {
    return parentFirstLine + childRest;
  }
  return child;
};
class VError extends Error {
  constructor(error, message) {
    const combinedMessage = getCombinedMessage(error, message);
    super(combinedMessage);
    this.name = 'VError';
    if (error instanceof Error) {
      this.stack = mergeStacks(this.stack, error.stack);
    }
    if (error.codeFrame) {
      // @ts-ignore
      this.codeFrame = error.codeFrame;
    }
    if (error.code) {
      // @ts-ignore
      this.code = error.code;
    }
  }
}

const binaryStringToBlob = async (string, type) => {
  try {
    return binaryStringToBlob$1(string, type);
  } catch (error) {
    const normalizedError = normalizeBlobError(error);
    throw new VError(normalizedError, 'Failed to convert binary string to blob');
  }
};
const blobToBinaryString = async blob => {
  try {
    return await blobToBinaryString$1(blob);
  } catch (error) {
    const normalizedError = normalizeBlobError(error);
    throw new VError(normalizedError, 'Failed to convert blob to binary string');
  }
};

class AssertionError extends Error {
  constructor(message) {
    super(message);
    this.name = 'AssertionError';
  }
}
const Object$1 = 1;
const Number$1 = 2;
const Array$1 = 3;
const String$1 = 4;
const Boolean = 5;
const Function = 6;
const Null = 7;
const Unknown = 8;
const getType = value => {
  switch (typeof value) {
    case 'number':
      return Number$1;
    case 'function':
      return Function;
    case 'string':
      return String$1;
    case 'object':
      if (value === null) {
        return Null;
      }
      if (Array.isArray(value)) {
        return Array$1;
      }
      return Object$1;
    case 'boolean':
      return Boolean;
    default:
      return Unknown;
  }
};
const object = value => {
  const type = getType(value);
  if (type !== Object$1) {
    throw new AssertionError('expected value to be of type object');
  }
};

const isMessagePort = value => {
  return value && value instanceof MessagePort;
};
const isMessagePortMain = value => {
  return value && value.constructor && value.constructor.name === 'MessagePortMain';
};
const isOffscreenCanvas = value => {
  return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
};
const isInstanceOf = (value, constructorName) => {
  return value?.constructor?.name === constructorName;
};
const isSocket = value => {
  return isInstanceOf(value, 'Socket');
};
const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
const isTransferrable = value => {
  for (const fn of transferrables) {
    if (fn(value)) {
      return true;
    }
  }
  return false;
};
const walkValue = (value, transferrables, isTransferrable) => {
  if (!value) {
    return;
  }
  if (isTransferrable(value)) {
    transferrables.push(value);
    return;
  }
  if (Array.isArray(value)) {
    for (const item of value) {
      walkValue(item, transferrables, isTransferrable);
    }
    return;
  }
  if (typeof value === 'object') {
    for (const property of Object.values(value)) {
      walkValue(property, transferrables, isTransferrable);
    }
    return;
  }
};
const getTransferrables = value => {
  const transferrables = [];
  walkValue(value, transferrables, isTransferrable);
  return transferrables;
};
const attachEvents = that => {
  const handleMessage = (...args) => {
    const data = that.getData(...args);
    that.dispatchEvent(new MessageEvent('message', {
      data
    }));
  };
  that.onMessage(handleMessage);
  const handleClose = event => {
    that.dispatchEvent(new Event('close'));
  };
  that.onClose(handleClose);
};
class Ipc extends EventTarget {
  constructor(rawIpc) {
    super();
    this._rawIpc = rawIpc;
    attachEvents(this);
  }
}
const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
const NewLine$1 = '\n';
const joinLines$1 = lines => {
  return lines.join(NewLine$1);
};
const RE_AT = /^\s+at/;
const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
const isNormalStackLine = line => {
  return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
};
const getDetails = lines => {
  const index = lines.findIndex(isNormalStackLine);
  if (index === -1) {
    return {
      actualMessage: joinLines$1(lines),
      rest: []
    };
  }
  let lastIndex = index - 1;
  while (++lastIndex < lines.length) {
    if (!isNormalStackLine(lines[lastIndex])) {
      break;
    }
  }
  return {
    actualMessage: lines[index - 1],
    rest: lines.slice(index, lastIndex)
  };
};
const splitLines$1 = lines => {
  return lines.split(NewLine$1);
};
const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
const isMessageCodeBlockStartIndex = line => {
  return RE_MESSAGE_CODE_BLOCK_START.test(line);
};
const isMessageCodeBlockEndIndex = line => {
  return RE_MESSAGE_CODE_BLOCK_END.test(line);
};
const getMessageCodeBlock = stderr => {
  const lines = splitLines$1(stderr);
  const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
  const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
  const relevantLines = lines.slice(startIndex, endIndex);
  const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
  return relevantMessage;
};
const isModuleNotFoundMessage = line => {
  return line.includes('[ERR_MODULE_NOT_FOUND]');
};
const getModuleNotFoundError = stderr => {
  const lines = splitLines$1(stderr);
  const messageIndex = lines.findIndex(isModuleNotFoundMessage);
  const message = lines[messageIndex];
  return {
    code: ERR_MODULE_NOT_FOUND,
    message
  };
};
const isModuleNotFoundError = stderr => {
  if (!stderr) {
    return false;
  }
  return stderr.includes('ERR_MODULE_NOT_FOUND');
};
const isModulesSyntaxError = stderr => {
  if (!stderr) {
    return false;
  }
  return stderr.includes('SyntaxError: Cannot use import statement outside a module');
};
const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
const isUnhelpfulNativeModuleError = stderr => {
  return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
};
const getNativeModuleErrorMessage = stderr => {
  const message = getMessageCodeBlock(stderr);
  return {
    code: E_INCOMPATIBLE_NATIVE_MODULE,
    message: `Incompatible native node module: ${message}`
  };
};
const getModuleSyntaxError = () => {
  return {
    code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON,
    message: `ES Modules are not supported in electron`
  };
};
const getHelpfulChildProcessError = (stdout, stderr) => {
  if (isUnhelpfulNativeModuleError(stderr)) {
    return getNativeModuleErrorMessage(stderr);
  }
  if (isModulesSyntaxError(stderr)) {
    return getModuleSyntaxError();
  }
  if (isModuleNotFoundError(stderr)) {
    return getModuleNotFoundError(stderr);
  }
  const lines = splitLines$1(stderr);
  const {
    actualMessage,
    rest
  } = getDetails(lines);
  return {
    code: '',
    message: actualMessage,
    stack: rest
  };
};
class IpcError extends VError {
  // @ts-ignore
  constructor(betterMessage, stdout = '', stderr = '') {
    if (stdout || stderr) {
      // @ts-ignore
      const {
        code,
        message,
        stack
      } = getHelpfulChildProcessError(stdout, stderr);
      const cause = new Error(message);
      // @ts-ignore
      cause.code = code;
      cause.stack = stack;
      super(cause, betterMessage);
    } else {
      super(betterMessage);
    }
    // @ts-ignore
    this.name = 'IpcError';
    // @ts-ignore
    this.stdout = stdout;
    // @ts-ignore
    this.stderr = stderr;
  }
}
const readyMessage = 'ready';
const getData$2 = event => {
  return event.data;
};
const listen$7 = () => {
  // @ts-ignore
  if (typeof WorkerGlobalScope === 'undefined') {
    throw new TypeError('module is not in web worker scope');
  }
  return globalThis;
};
const signal$8 = global => {
  global.postMessage(readyMessage);
};
class IpcChildWithModuleWorker extends Ipc {
  getData(event) {
    return getData$2(event);
  }
  send(message) {
    // @ts-ignore
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    // @ts-ignore
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    // ignore
  }
  onClose(callback) {
    // ignore
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
}
const wrap$f = global => {
  return new IpcChildWithModuleWorker(global);
};
const waitForFirstMessage = async port => {
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  port.addEventListener('message', resolve, {
    once: true
  });
  const event = await promise;
  // @ts-ignore
  return event.data;
};
const listen$6 = async () => {
  const parentIpcRaw = listen$7();
  signal$8(parentIpcRaw);
  const parentIpc = wrap$f(parentIpcRaw);
  const firstMessage = await waitForFirstMessage(parentIpc);
  if (firstMessage.method !== 'initialize') {
    throw new IpcError('unexpected first message');
  }
  const type = firstMessage.params[0];
  if (type === 'message-port') {
    parentIpc.send({
      id: firstMessage.id,
      jsonrpc: '2.0',
      result: null
    });
    parentIpc.dispose();
    const port = firstMessage.params[1];
    return port;
  }
  return globalThis;
};
class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
  getData(event) {
    return getData$2(event);
  }
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    if (this._rawIpc.close) {
      this._rawIpc.close();
    }
  }
  onClose(callback) {
    // ignore
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
    this._rawIpc.start();
  }
}
const wrap$e = port => {
  return new IpcChildWithModuleWorkerAndMessagePort(port);
};
const IpcChildWithModuleWorkerAndMessagePort$1 = {
  __proto__: null,
  listen: listen$6,
  wrap: wrap$e
};
const Error$3 = 1;
const Open = 2;
const Close = 3;
const addListener = (emitter, type, callback) => {
  if ('addEventListener' in emitter) {
    emitter.addEventListener(type, callback);
  } else {
    emitter.on(type, callback);
  }
};
const removeListener = (emitter, type, callback) => {
  if ('removeEventListener' in emitter) {
    emitter.removeEventListener(type, callback);
  } else {
    emitter.off(type, callback);
  }
};
const getFirstEvent = (eventEmitter, eventMap) => {
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  const listenerMap = Object.create(null);
  const cleanup = value => {
    for (const event of Object.keys(eventMap)) {
      removeListener(eventEmitter, event, listenerMap[event]);
    }
    resolve(value);
  };
  for (const [event, type] of Object.entries(eventMap)) {
    const listener = event => {
      cleanup({
        event,
        type
      });
    };
    addListener(eventEmitter, event, listener);
    listenerMap[event] = listener;
  }
  return promise;
};
const Message$1 = 3;
const create$5$1 = async ({
  isMessagePortOpen,
  messagePort
}) => {
  if (!isMessagePort(messagePort)) {
    throw new IpcError('port must be of type MessagePort');
  }
  if (isMessagePortOpen) {
    return messagePort;
  }
  const eventPromise = getFirstEvent(messagePort, {
    message: Message$1
  });
  messagePort.start();
  const {
    event,
    type
  } = await eventPromise;
  if (type !== Message$1) {
    throw new IpcError('Failed to wait for ipc message');
  }
  if (event.data !== readyMessage) {
    throw new IpcError('unexpected first message');
  }
  return messagePort;
};
const signal$1 = messagePort => {
  messagePort.start();
};
class IpcParentWithMessagePort extends Ipc {
  getData = getData$2;
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    this._rawIpc.close();
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
  onClose(callback) {}
}
const wrap$5 = messagePort => {
  return new IpcParentWithMessagePort(messagePort);
};
const IpcParentWithMessagePort$1 = {
  __proto__: null,
  create: create$5$1,
  signal: signal$1,
  wrap: wrap$5
};
const stringifyCompact = value => {
  return JSON.stringify(value);
};
const parse = content => {
  if (content === 'undefined') {
    return null;
  }
  try {
    return JSON.parse(content);
  } catch (error) {
    throw new VError(error, 'failed to parse json');
  }
};
const waitForWebSocketToBeOpen = webSocket => {
  return getFirstEvent(webSocket, {
    close: Close,
    error: Error$3,
    open: Open
  });
};
const create$b = async ({
  webSocket
}) => {
  const firstWebSocketEvent = await waitForWebSocketToBeOpen(webSocket);
  if (firstWebSocketEvent.type === Error$3) {
    throw new IpcError(`WebSocket connection error`);
  }
  if (firstWebSocketEvent.type === Close) {
    throw new IpcError('Websocket connection was immediately closed');
  }
  return webSocket;
};
class IpcParentWithWebSocket extends Ipc {
  getData(event) {
    return parse(event.data);
  }
  send(message) {
    this._rawIpc.send(stringifyCompact(message));
  }
  sendAndTransfer(message) {
    throw new Error('sendAndTransfer not supported');
  }
  dispose() {
    this._rawIpc.close();
  }
  onClose(callback) {
    this._rawIpc.addEventListener('close', callback);
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
}
const wrap = webSocket => {
  return new IpcParentWithWebSocket(webSocket);
};
const IpcParentWithWebSocket$1 = {
  __proto__: null,
  create: create$b,
  wrap
};

class CommandNotFoundError extends Error {
  constructor(command) {
    super(`Command not found ${command}`);
    this.name = 'CommandNotFoundError';
  }
}
const commands = Object.create(null);
const register$1 = commandMap => {
  Object.assign(commands, commandMap);
};
const getCommand = key => {
  return commands[key];
};
const execute = (command, ...args) => {
  const fn = getCommand(command);
  if (!fn) {
    throw new CommandNotFoundError(command);
  }
  return fn(...args);
};

const Two$1 = '2.0';
const callbacks = Object.create(null);
const get$1 = id => {
  return callbacks[id];
};
const remove$6 = id => {
  delete callbacks[id];
};
class JsonRpcError extends Error {
  constructor(message) {
    super(message);
    this.name = 'JsonRpcError';
  }
}
const NewLine = '\n';
const DomException = 'DOMException';
const ReferenceError$1 = 'ReferenceError';
const SyntaxError$1 = 'SyntaxError';
const TypeError$1 = 'TypeError';
const getErrorConstructor = (message, type) => {
  if (type) {
    switch (type) {
      case DomException:
        return DOMException;
      case ReferenceError$1:
        return ReferenceError;
      case SyntaxError$1:
        return SyntaxError;
      case TypeError$1:
        return TypeError;
      default:
        return Error;
    }
  }
  if (message.startsWith('TypeError: ')) {
    return TypeError;
  }
  if (message.startsWith('SyntaxError: ')) {
    return SyntaxError;
  }
  if (message.startsWith('ReferenceError: ')) {
    return ReferenceError;
  }
  return Error;
};
const constructError = (message, type, name) => {
  const ErrorConstructor = getErrorConstructor(message, type);
  if (ErrorConstructor === DOMException && name) {
    return new ErrorConstructor(message, name);
  }
  if (ErrorConstructor === Error) {
    const error = new Error(message);
    if (name && name !== 'VError') {
      error.name = name;
    }
    return error;
  }
  return new ErrorConstructor(message);
};
const joinLines = lines => {
  return lines.join(NewLine);
};
const splitLines = lines => {
  return lines.split(NewLine);
};
const getCurrentStack = () => {
  const stackLinesToSkip = 3;
  const currentStack = joinLines(splitLines(new Error().stack || '').slice(stackLinesToSkip));
  return currentStack;
};
const getNewLineIndex = (string, startIndex = undefined) => {
  return string.indexOf(NewLine, startIndex);
};
const getParentStack = error => {
  let parentStack = error.stack || error.data || error.message || '';
  if (parentStack.startsWith('    at')) {
    parentStack = error.message + NewLine + parentStack;
  }
  return parentStack;
};
const MethodNotFound = -32601;
const Custom = -32001;
const restoreJsonRpcError = error => {
  const currentStack = getCurrentStack();
  if (error && error instanceof Error) {
    if (typeof error.stack === 'string') {
      error.stack = error.stack + NewLine + currentStack;
    }
    return error;
  }
  if (error && error.code && error.code === MethodNotFound) {
    const restoredError = new JsonRpcError(error.message);
    const parentStack = getParentStack(error);
    restoredError.stack = parentStack + NewLine + currentStack;
    return restoredError;
  }
  if (error && error.message) {
    const restoredError = constructError(error.message, error.type, error.name);
    if (error.data) {
      if (error.data.stack && error.data.type && error.message) {
        restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
      } else if (error.data.stack) {
        restoredError.stack = error.data.stack;
      }
      if (error.data.codeFrame) {
        // @ts-ignore
        restoredError.codeFrame = error.data.codeFrame;
      }
      if (error.data.code) {
        // @ts-ignore
        restoredError.code = error.data.code;
      }
      if (error.data.type) {
        // @ts-ignore
        restoredError.name = error.data.type;
      }
    } else {
      if (error.stack) {
        const lowerStack = restoredError.stack || '';
        // @ts-ignore
        const indexNewLine = getNewLineIndex(lowerStack);
        const parentStack = getParentStack(error);
        // @ts-ignore
        restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
      }
      if (error.codeFrame) {
        // @ts-ignore
        restoredError.codeFrame = error.codeFrame;
      }
    }
    return restoredError;
  }
  if (typeof error === 'string') {
    return new Error(`JsonRpc Error: ${error}`);
  }
  return new Error(`JsonRpc Error: ${error}`);
};
const unwrapJsonRpcResult = responseMessage => {
  if ('error' in responseMessage) {
    const restoredError = restoreJsonRpcError(responseMessage.error);
    throw restoredError;
  }
  if ('result' in responseMessage) {
    return responseMessage.result;
  }
  throw new JsonRpcError('unexpected response message');
};
const warn = (...args) => {
  console.warn(...args);
};
const resolve = (id, response) => {
  const fn = get$1(id);
  if (!fn) {
    console.log(response);
    warn(`callback ${id} may already be disposed`);
    return;
  }
  fn(response);
  remove$6(id);
};
const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
const getErrorType = prettyError => {
  if (prettyError && prettyError.type) {
    return prettyError.type;
  }
  if (prettyError && prettyError.constructor && prettyError.constructor.name) {
    return prettyError.constructor.name;
  }
  return undefined;
};
const isAlreadyStack = line => {
  return line.trim().startsWith('at ');
};
const getStack = prettyError => {
  const stackString = prettyError.stack || '';
  const newLineIndex = stackString.indexOf('\n');
  if (newLineIndex !== -1 && !isAlreadyStack(stackString.slice(0, newLineIndex))) {
    return stackString.slice(newLineIndex + 1);
  }
  return stackString;
};
const getErrorProperty = (error, prettyError) => {
  if (error && error.code === E_COMMAND_NOT_FOUND) {
    return {
      code: MethodNotFound,
      data: error.stack,
      message: error.message
    };
  }
  return {
    code: Custom,
    data: {
      code: prettyError.code,
      codeFrame: prettyError.codeFrame,
      name: prettyError.name,
      stack: getStack(prettyError),
      type: getErrorType(prettyError)
    },
    message: prettyError.message
  };
};
const create$1$1 = (id, error) => {
  return {
    error,
    id,
    jsonrpc: Two$1
  };
};
const getErrorResponse = (id, error, preparePrettyError, logError) => {
  const prettyError = preparePrettyError(error);
  logError(error, prettyError);
  const errorProperty = getErrorProperty(error, prettyError);
  return create$1$1(id, errorProperty);
};
const create$a = (message, result) => {
  return {
    id: message.id,
    jsonrpc: Two$1,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$a(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
  return {
    error: {
      code: Custom,
      data: error,
      // @ts-ignore
      message: error.message
    },
    id,
    jsonrpc: Two$1
  };
};
const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
  try {
    const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
    return getSuccessResponse(message, result);
  } catch (error) {
    if (ipc.canUseSimpleErrorResponse) {
      return getErrorResponseSimple(message.id, error);
    }
    return getErrorResponse(message.id, error, preparePrettyError, logError);
  }
};
const defaultPreparePrettyError = error => {
  return error;
};
const defaultLogError = () => {
  // ignore
};
const defaultRequiresSocket = () => {
  return false;
};
const defaultResolve = resolve;

// TODO maybe remove this in v6 or v7, only accept options object to simplify the code
const normalizeParams = args => {
  if (args.length === 1) {
    const options = args[0];
    return {
      execute: options.execute,
      ipc: options.ipc,
      logError: options.logError || defaultLogError,
      message: options.message,
      preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
      requiresSocket: options.requiresSocket || defaultRequiresSocket,
      resolve: options.resolve || defaultResolve
    };
  }
  return {
    execute: args[2],
    ipc: args[0],
    logError: args[5],
    message: args[1],
    preparePrettyError: args[4],
    requiresSocket: args[6],
    resolve: args[3]
  };
};
const handleJsonRpcMessage = async (...args) => {
  const options = normalizeParams(args);
  const {
    execute,
    ipc,
    logError,
    message,
    preparePrettyError,
    requiresSocket,
    resolve
  } = options;
  if ('id' in message) {
    if ('method' in message) {
      const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
      try {
        ipc.send(response);
      } catch (error) {
        const errorResponse = getErrorResponse(message.id, error, preparePrettyError, logError);
        ipc.send(errorResponse);
      }
      return;
    }
    resolve(message.id, message);
    return;
  }
  if ('method' in message) {
    await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
    return;
  }
  throw new JsonRpcError('unexpected message');
};

const Two = '2.0';

const create$9 = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};

const create$8 = (id, method, params) => {
  const message = {
    id,
    jsonrpc: Two,
    method,
    params
  };
  return message;
};

let id = 0;
const create$7 = () => {
  return ++id;
};

const registerPromise = map => {
  const id = create$7();
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  map[id] = resolve;
  return {
    id,
    promise
  };
};

const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
  const {
    id,
    promise
  } = registerPromise(callbacks);
  const message = create$8(id, method, params);
  if (useSendAndTransfer && ipc.sendAndTransfer) {
    ipc.sendAndTransfer(message);
  } else {
    ipc.send(message);
  }
  const responseMessage = await promise;
  return unwrapJsonRpcResult(responseMessage);
};
const createRpc = ipc => {
  const callbacks = Object.create(null);
  ipc._resolve = (id, response) => {
    const fn = callbacks[id];
    if (!fn) {
      console.warn(`callback ${id} may already be disposed`);
      return;
    }
    fn(response);
    delete callbacks[id];
  };
  const rpc = {
    async dispose() {
      await ipc?.dispose();
    },
    invoke(method, ...params) {
      return invokeHelper(callbacks, ipc, method, params, false);
    },
    invokeAndTransfer(method, ...params) {
      return invokeHelper(callbacks, ipc, method, params, true);
    },
    // @ts-ignore
    ipc,
    /**
     * @deprecated
     */
    send(method, ...params) {
      const message = create$9(method, params);
      ipc.send(message);
    }
  };
  return rpc;
};

const requiresSocket = () => {
  return false;
};
const preparePrettyError = error => {
  return error;
};
const logError = () => {
  // handled by renderer worker
};
const handleMessage = event => {
  const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
  const actualExecute = event?.target?.execute || execute;
  return handleJsonRpcMessage(event.target, event.data, actualExecute, event.target._resolve, preparePrettyError, logError, actualRequiresSocket);
};

const handleIpc = ipc => {
  if ('addEventListener' in ipc) {
    ipc.addEventListener('message', handleMessage);
  } else if ('on' in ipc) {
    // deprecated
    ipc.on('message', handleMessage);
  }
};

const listen$1 = async (module, options) => {
  const rawIpc = await module.listen(options);
  if (module.signal) {
    module.signal(rawIpc);
  }
  const ipc = module.wrap(rawIpc);
  return ipc;
};

const create$6 = async ({
  commandMap,
  isMessagePortOpen = true,
  messagePort
}) => {
  // TODO create a commandMap per rpc instance
  register$1(commandMap);
  const rawIpc = await IpcParentWithMessagePort$1.create({
    isMessagePortOpen,
    messagePort
  });
  const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  messagePort.start();
  return rpc;
};

const create$5 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  const {
    port1,
    port2
  } = new MessageChannel();
  await send(port1);
  return create$6({
    commandMap,
    isMessagePortOpen,
    messagePort: port2
  });
};

const createSharedLazyRpc = factory => {
  let rpcPromise;
  const getOrCreate = () => {
    if (!rpcPromise) {
      rpcPromise = factory();
    }
    return rpcPromise;
  };
  return {
    async dispose() {
      const rpc = await getOrCreate();
      await rpc.dispose();
    },
    async invoke(method, ...params) {
      const rpc = await getOrCreate();
      return rpc.invoke(method, ...params);
    },
    async invokeAndTransfer(method, ...params) {
      const rpc = await getOrCreate();
      return rpc.invokeAndTransfer(method, ...params);
    },
    async send(method, ...params) {
      const rpc = await getOrCreate();
      rpc.send(method, ...params);
    }
  };
};

const create$4 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  return createSharedLazyRpc(() => {
    return create$5({
      commandMap,
      isMessagePortOpen,
      send
    });
  });
};

const Https$1 = 'https:';
const Ws = 'ws:';
const Wss = 'wss:';

const getWebSocketProtocol = locationProtocol => {
  return locationProtocol === Https$1 ? Wss : Ws;
};

const getWebSocketUrl = (type, host, locationProtocol) => {
  const wsProtocol = getWebSocketProtocol(locationProtocol);
  return `${wsProtocol}//${host}/websocket/${type}`;
};

const getHost = () => {
  return location.host;
};
const getProtocol = () => {
  return location.protocol;
};

const create$3 = async ({
  commandMap,
  webSocket
}) => {
  // TODO create a commandMap per rpc instance
  register$1(commandMap);
  const rawIpc = await IpcParentWithWebSocket$1.create({
    webSocket
  });
  const ipc = IpcParentWithWebSocket$1.wrap(rawIpc);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  return rpc;
};

const create$2 = async ({
  commandMap,
  type
}) => {
  const host = getHost();
  const protocol = getProtocol();
  const wsUrl = getWebSocketUrl(type, host, protocol);
  const webSocket = new WebSocket(wsUrl);
  const rpc = await create$3({
    commandMap,
    webSocket
  });
  return rpc;
};

const create$1 = async ({
  commandMap
}) => {
  // TODO create a commandMap per rpc instance
  register$1(commandMap);
  const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  return rpc;
};

const createMockRpc = ({
  commandMap
}) => {
  const invocations = [];
  const invoke = (method, ...params) => {
    invocations.push([method, ...params]);
    const command = commandMap[method];
    if (!command) {
      throw new Error(`command ${method} not found`);
    }
    return command(...params);
  };
  const mockRpc = {
    invocations,
    invoke,
    invokeAndTransfer: invoke
  };
  return mockRpc;
};

const rpcs = Object.create(null);
const set$5 = (id, rpc) => {
  rpcs[id] = rpc;
};
const get = id => {
  return rpcs[id];
};
const remove$5 = id => {
  delete rpcs[id];
};

/* eslint-disable  @typescript-eslint/explicit-function-return-type */
const create = rpcId => {
  return {
    async dispose() {
      const rpc = get(rpcId);
      await rpc.dispose();
    },
    // @ts-ignore
    invoke(method, ...params) {
      const rpc = get(rpcId);
      // @ts-ignore
      return rpc.invoke(method, ...params);
    },
    // @ts-ignore
    invokeAndTransfer(method, ...params) {
      const rpc = get(rpcId);
      // @ts-ignore
      return rpc.invokeAndTransfer(method, ...params);
    },
    registerMockRpc(commandMap) {
      const mockRpc = createMockRpc({
        commandMap
      });
      set$5(rpcId, mockRpc);
      // @ts-ignore
      mockRpc[Symbol.dispose] = () => {
        remove$5(rpcId);
      };
      // @ts-ignore
      return mockRpc;
    },
    set(rpc) {
      set$5(rpcId, rpc);
    }
  };
};

const DebugWorker = 55;
const ExtensionHostWorker = 44;
const FileSystemProcess$1 = 210;
const FileSystemWorker = 209;
const RendererProcess = 1670;
const RendererWorker = 1;

const {
  invoke: invoke$4,
  set: set$4
} = create(ExtensionHostWorker);

const {
  invoke: invoke$3,
  set: set$3
} = create(FileSystemProcess$1);
const remove$4 = async uri => {
  return invoke$3('FileSystem.remove', uri);
};
const readFile$4 = async uri => {
  return invoke$3('FileSystem.readFile', uri);
};
const appendFile$2 = async (uri, text) => {
  // @ts-ignore
  return invoke$3('FileSystem.appendFile', uri, text);
};
const readDirWithFileTypes$4 = async uri => {
  return invoke$3('FileSystem.readDirWithFileTypes', uri);
};
const getPathSeparator$4 = async root => {
  // @ts-ignore
  return invoke$3('FileSystem.getPathSeparator', root);
};
const readJson$4 = async root => {
  // @ts-ignore
  return invoke$3('FileSystem.readJson', root);
};
const getRealPath$4 = async path => {
  // @ts-ignore
  return invoke$3('FileSystem.getRealPath', path);
};
const stat$4 = async path => {
  // @ts-ignore
  return invoke$3('FileSystem.stat', path);
};
const writeFile$4 = async (path, content) => {
  // @ts-ignore
  return invoke$3('FileSystem.writeFile', path, content);
};
const mkdir$4 = async path => {
  // @ts-ignore
  return invoke$3('FileSystem.mkdir', path);
};
const rename$4 = async (oldUri, newUri) => {
  // @ts-ignore
  return invoke$3('FileSystem.rename', oldUri, newUri);
};
const copy$4 = async (oldUri, newUri) => {
  // @ts-ignore
  return invoke$3('FileSystem.copy', oldUri, newUri);
};
const getFolderSize$4 = async uri => {
  // @ts-ignore
  return invoke$3('FileSystem.getFolderSize', uri);
};
const exists$4 = async uri => {
  // @ts-ignore
  return invoke$3('FileSystem.exists', uri);
};

const FileSystemProcess = {
   __proto__: null,
   appendFile: appendFile$2,
   copy: copy$4,
   exists: exists$4,
   getFolderSize: getFolderSize$4,
   getPathSeparator: getPathSeparator$4,
   getRealPath: getRealPath$4,
   invoke: invoke$3,
   mkdir: mkdir$4,
   readDirWithFileTypes: readDirWithFileTypes$4,
   readFile: readFile$4,
   readJson: readJson$4,
   remove: remove$4,
   rename: rename$4,
   set: set$3,
   stat: stat$4,
   writeFile: writeFile$4
};

const {
  set: set$2
} = create(RendererProcess);

const {
  invoke: invoke$2,
  invokeAndTransfer,
  set: set$1
} = create(RendererWorker);
const sendMessagePortToExtensionHostWorker = async (port, rpcId = 0) => {
  const command = 'HandleMessagePort.handleMessagePort2';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
};
const sendMessagePortToRendererProcess = async port => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToRendererProcess', port, command, DebugWorker);
};
const sendMessagePortToFileSystemProcess$1 = async (port, rpcId) => {
  const command = 'HandleMessagePortForFileSystemProcess.handleMessagePortForFileSystemProcess';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToSharedProcess', port, command, rpcId);
};

const createLazyRpc = rpcId => {
  let rpcPromise;
  let factory;
  const createRpc = async () => {
    const rpc = await factory();
    set$5(rpcId, rpc);
  };
  const ensureRpc = async () => {
    if (!rpcPromise) {
      rpcPromise = createRpc();
    }
    await rpcPromise;
  };
  return {
    async invoke(method, ...params) {
      await ensureRpc();
      const rpc = get(rpcId);
      return rpc.invoke(method, ...params);
    },
    async invokeAndTransfer(method, ...params) {
      await ensureRpc();
      const rpc = get(rpcId);
      return rpc.invokeAndTransfer(method, ...params);
    },
    setFactory(value) {
      factory = value;
    }
  };
};

const remove$3 = async dirent => {
  throw new Error('not implemented');
};
const readFile$3 = async uri => {
  const response = await fetch(uri);
  if (!response.ok) {
    throw new Error(response.statusText);
  }
  const result = await response.text();
  return result;
};
const readFileAsBlob$2 = async uri => {
  const response = await fetch(uri);
  if (!response.ok) {
    throw new Error(response.statusText);
  }
  const result = await response.blob();
  return result;
};
const exists$3 = async uri => {
  const response = await fetch(uri);
  return response.ok;
};
const readDirWithFileTypes$3 = async uri => {
  throw new Error('not implemented');
};
const getPathSeparator$3 = async root => {
  return '/';
};
const readJson$3 = async uri => {
  const response = await fetch(uri);
  if (!response.ok) {
    throw new Error(`${response.statusText}`);
  }
  const json = await response.json();
  return json;
};
const getRealPath$3 = async path => {
  throw new Error('not implemented');
};
const stat$3 = async dirent => {
  throw new Error('not implemented');
};
const createFile$2 = async uri => {
  throw new Error('not implemented');
};
const writeFile$3 = async (uri, content) => {
  throw new Error('not implemented');
};
const mkdir$3 = async uri => {
  throw new Error('not implemented');
};
const rename$3 = async (oldUri, newUri) => {
  throw new Error('not implemented');
};
const copy$3 = async (oldUri, newUri) => {
  throw new Error('not implemented');
};
const getFolderSize$3 = async uri => {
  throw new Error('not implemented');
};

const FileSystemFetch = {
   __proto__: null,
   copy: copy$3,
   createFile: createFile$2,
   exists: exists$3,
   getFolderSize: getFolderSize$3,
   getPathSeparator: getPathSeparator$3,
   getRealPath: getRealPath$3,
   mkdir: mkdir$3,
   readDirWithFileTypes: readDirWithFileTypes$3,
   readFile: readFile$3,
   readFileAsBlob: readFileAsBlob$2,
   readJson: readJson$3,
   remove: remove$3,
   rename: rename$3,
   stat: stat$3,
   writeFile: writeFile$3
};

const RE_PROTOCOL = /^([a-z-]+):\/\//;
const assertUri = uri => {
  const protocolMatch = uri.match(RE_PROTOCOL);
  if (!protocolMatch) {
    throw new Error(`uri must be a valid uri`);
  }
};

const {
  appendFile: appendFile$1,
  copy: copy$2,
  exists: exists$2,
  getFolderSize: getFolderSize$2,
  getPathSeparator: getPathSeparator$2,
  getRealPath: getRealPath$2,
  invoke: invoke$1,
  mkdir: mkdir$2,
  readDirWithFileTypes: readDirWithFileTypes$2,
  readFile: readFile$2,
  readJson: readJson$2,
  remove: remove$2,
  rename: rename$2,
  set,
  stat: stat$2,
  writeFile: writeFile$2
} = FileSystemProcess;

const Http = 'http:';
const Memory = 'memfs:';
const Https = 'https:';
const File$2 = 'file:';

const watchCallbacks = Object.create(null);
const registerWatchCallback = (id, rpcId, commandId, uri) => {
  watchCallbacks[id] = {
    commandId,
    rpcId,
    uri
  };
};
const executeWatchCallBack = async id => {
  const entry = watchCallbacks[id];
  if (!entry) {
    throw new Error(`watch callback ${id} not found`);
  }
  const {
    commandId,
    rpcId
  } = entry;
  const rpc = get(rpcId);
  await rpc.invoke(commandId, id);
};
const unregisterWatchCallback = id => {
  delete watchCallbacks[id];
};
const getWatchCallbackIdsForUri = uri => {
  const watchIds = [];
  for (const [id, entry] of Object.entries(watchCallbacks)) {
    if (entry.uri === uri) {
      watchIds.push(Number(id));
    }
  }
  return watchIds;
};

const watchFile$2 = async (id, uri, rpcId) => {
  assertUri(uri);
  const commandId = 'Output.executeWatchCallback';
  registerWatchCallback(id, rpcId, commandId, uri);
  if (uri.startsWith(File$2)) {
    // @ts-ignore
    await invoke$1('FileSystem.watchFile', id, uri);
  }
  // memfs file watchers are handled in-memory, no need to register with FileSystemProcess
};
const executeWatchCallback = async id => {
  try {
    await executeWatchCallBack(id);
  } catch (error) {
    console.error(error);
  }
};
const unwatchFile = async id => {
  unregisterWatchCallback(id);
  // TODO only if it is a file uri
  // @ts-ignore
  await invoke$1('FileSystem.unwatchFile', id);
};
const triggerMemfsFileWatcher = async uri => {
  // Find all registered watchers for this URI and trigger them
  const watchCallbackIds = getWatchCallbackIdsForUri(uri);
  for (const id of watchCallbackIds) {
    await executeWatchCallback(id);
  }
};

const getFileExtension = uri => {
  const lastDot = uri.lastIndexOf('.');
  if (lastDot === -1) {
    return '';
  }
  const lastSlash = Math.max(uri.lastIndexOf('/'), uri.lastIndexOf('\\'));
  if (lastSlash > lastDot) {
    return '';
  }
  return uri.slice(lastDot + 1);
};

const getBlobType = uri => {
  const extension = getFileExtension(uri).toLowerCase();
  switch (extension) {
    case 'css':
      return 'text/css';
    case 'html':
      return 'text/html';
    case 'js':
      return 'text/javascript';
    case 'json':
      return 'application/json';
    case 'svg':
      return 'image/svg+xml';
    case 'txt':
      return 'text/plain';
    case 'xml':
      return 'application/xml';
    default:
      return '';
  }
};
const remove$1 = async dirent => {
  await invoke$4('FileSystemMemory.remove', dirent);
  // Trigger file watchers for memfs files
  await triggerMemfsFileWatcher(dirent);
};
const readFile$1 = async uri => {
  return invoke$4('FileSystemMemory.readFile', uri);
};
const readFileAsBlob$1 = async uri => {
  const content = await readFile$1(uri);
  const type = getBlobType(uri);
  if (type) {
    return new Blob([content], {
      type
    });
  }
  return new Blob([content]);
};
const exists$1 = async uri => {
  throw new Error('not implemented');
};
const readDirWithFileTypes$1 = async uri => {
  throw new Error('not implemented');
};
const getPathSeparator$1 = async root => {
  return '/';
};
const readJson$1 = async uri => {
  throw new Error('not implemented');
};
const getRealPath$1 = async path => {
  throw new Error('not implemented');
};
const stat$1 = async dirent => {
  throw new Error('not implemented');
};
const createFile$1 = async uri => {
  await invoke$4('FileSystemMemory.createFile', uri);
  // Trigger file watchers for memfs files
  await triggerMemfsFileWatcher(uri);
};
const writeFile$1 = async (uri, content) => {
  await invoke$4('FileSystemMemory.writeFile', uri, content);
  // Trigger file watchers for memfs files
  await triggerMemfsFileWatcher(uri);
};
const mkdir$1 = async uri => {
  throw new Error('not implemented');
};
const rename$1 = async (oldUri, newUri) => {
  await invoke$4('FileSystemMemory.rename', oldUri, newUri);
  // Trigger file watchers for both old and new URIs
  await triggerMemfsFileWatcher(oldUri);
  await triggerMemfsFileWatcher(newUri);
};
const copy$1 = async (oldUri, newUri) => {
  throw new Error('not implemented');
};
const getFolderSize$1 = async uri => {
  throw new Error('not implemented');
};
const watchFile$1 = async (id, uri) => {
  throw new Error('not implemented');
};

const FileSystemMemory = {
   __proto__: null,
   copy: copy$1,
   createFile: createFile$1,
   exists: exists$1,
   getFolderSize: getFolderSize$1,
   getPathSeparator: getPathSeparator$1,
   getRealPath: getRealPath$1,
   mkdir: mkdir$1,
   readDirWithFileTypes: readDirWithFileTypes$1,
   readFile: readFile$1,
   readFileAsBlob: readFileAsBlob$1,
   readJson: readJson$1,
   remove: remove$1,
   rename: rename$1,
   stat: stat$1,
   watchFile: watchFile$1,
   writeFile: writeFile$1
};

const isHttp = uri => {
  return uri.startsWith(Http) || uri.startsWith(Https);
};

const isMemory = uri => {
  return uri.startsWith(Memory);
};

const remove = async dirent => {
  if (isMemory(dirent)) {
    return remove$1(dirent);
  }
  return remove$2(dirent);
};
const readFile = async uri => {
  if (isHttp(uri)) {
    return readFile$3(uri);
  }
  if (isMemory(uri)) {
    return invoke$2('FileSystem.readFile', uri);
  }
  return readFile$2(uri);
};
const appendFile = async (uri, text) => {
  return appendFile$1(uri, text);
};
const readDirWithFileTypes = async uri => {
  if (isMemory(uri)) {
    return invoke$2('FileSystem.readDirWithFileTypes', uri);
  }
  return readDirWithFileTypes$2(uri);
};
const getPathSeparator = async root => {
  return getPathSeparator$2(root);
};
const readJson = async uri => {
  if (isHttp(uri)) {
    return readJson$3(uri);
  }
  if (isMemory(uri)) {
    return readJson$1();
  }
  return readJson$2(uri);
};
const getRealPath = async path => {
  return getRealPath$2(path);
};
const readFileAsBlob = async uri => {
  if (isHttp(uri)) {
    return readFileAsBlob$2(uri);
  }
  if (isMemory(uri)) {
    return readFileAsBlob$1(uri);
  }
  if (uri.startsWith('file:///')) {
    const rest = uri.slice('file:///'.length);
    const remoteUrl = `/remote/${rest}`;
    const response = await fetch(remoteUrl);
    if (!response.ok) {
      throw new Error(response.statusText);
    }
    const blob = await response.blob();
    return blob;
  }
  throw new Error('uri not supported');
};
const stat = async dirent => {
  return stat$2(dirent);
};
const exists = async uri => {
  if (isHttp(uri)) {
    return exists$3(uri);
  }
  if (isMemory(uri)) {
    return invoke$2('FileSystem.exists', uri);
  }
  return exists$2(uri);
};
const createFile = async uri => {
  if (isMemory(uri)) {
    return createFile$1(uri);
  }
  return writeFile$2(uri, '');
};
const writeFile = async (uri, content) => {
  if (isMemory(uri)) {
    return invoke$2('FileSystem.writeFile', uri, content);
  }
  return writeFile$2(uri, content);
};
const getBytes = async blob => {
  if (blob.bytes) {
    return blob.bytes();
  }
  const buffer = await blob.arrayBuffer();
  const bytes = new Uint8Array(buffer);
  return bytes;
};
const writeBlob = async (uri, blob) => {
  const bytes = await getBytes(blob);
  // @ts-ignore
  await invoke$1('FileSystem.writeBuffer', uri, bytes);
};
const mkdir = async uri => {
  if (isMemory(uri)) {
    await invoke$2('FileSystem.mkdir', uri);
    return;
  }
  return mkdir$2(uri);
};
const rename = async (oldUri, newUri) => {
  if (isMemory(oldUri) || isMemory(newUri)) {
    return rename$1(oldUri, newUri);
  }
  return rename$2(oldUri, newUri);
};
const copy = async (oldUri, newUri) => {
  return copy$2(oldUri, newUri);
};
const getFolderSize = async uri => {
  return getFolderSize$2(uri);
};
const watchFile = async (id, uri) => {
  // @ts-ignore
  await invoke$1('FileSystem.watchFile', id, uri);
};

const FileSystemDisk = {
   __proto__: null,
   appendFile,
   copy,
   createFile,
   exists,
   getFolderSize,
   getPathSeparator,
   getRealPath,
   mkdir,
   readDirWithFileTypes,
   readFile,
   readFileAsBlob,
   readJson,
   remove,
   rename,
   stat,
   watchFile,
   writeBlob,
   writeFile
};

const handleMessagePort = async (port, rpcId) => {
  const rpc = await create$6({
    commandMap: {},
    messagePort: port
  });
  if (rpcId) {
    set$5(rpcId, rpc);
  }
};

const sendMessagePortToFileSystemProcess = async port => {
  await sendMessagePortToFileSystemProcess$1(port, FileSystemWorker);
};

const createFileSystemProcessRpcElectron = async () => {
  try {
    const rpc = await create$5({
      commandMap: {},
      send: sendMessagePortToFileSystemProcess
    });
    return rpc;
  } catch (error) {
    throw new VError(error, `Failed to create file system process rpc`);
  }
};

const commandMapRef = {};

const createFileSystemProcessRpcNode = async () => {
  try {
    const rpc = await create$2({
      commandMap: commandMapRef,
      type: 'file-system-process'
    });
    return rpc;
  } catch (error) {
    throw new VError(error, `Failed to create file system process rpc`);
  }
};

const Web = 1;
const Electron = 2;
const Remote = 3;

const getRpcFactory = platform => {
  switch (platform) {
    case Electron:
      return createFileSystemProcessRpcElectron;
    case Remote:
      return createFileSystemProcessRpcNode;
    default:
      throw new Error(`unexpected platform`);
  }
};

const createFileSystemProcessRpc = async platform => {
  const create = getRpcFactory(platform);
  return create();
};

const initializeFileSystemProcess = async platform => {
  if (platform === Web) {
    return;
  }
  const rpc = await createFileSystemProcessRpc(platform);
  set(rpc);
};

const initialize = async platform => {
  await initializeFileSystemProcess(platform);
};

const Directory = 'directory';
const File$1 = 'file';

const {
  invoke} = createLazyRpc(RendererProcess);

/**
 * Do not use directly, use FileSystemHtml.getChildHandles
 * instead which prompts for the required permission to
 * retrieve the child handles
 */
const getChildHandles = async handle => {
  object(handle);
  // @ts-ignore - values() exists on FileSystemDirectoryHandle but TypeScript types may not include it
  const handles = await Array.fromAsync(handle.values());
  return handles;
};

const uploadDirectory = async (fileSystemHandle, pathSeparator, root, uploadHandles) => {
  const folderPath = root + pathSeparator + fileSystemHandle.name;
  await mkdir(folderPath);
  const childHandles = await getChildHandles(fileSystemHandle);
  await uploadHandles(childHandles, pathSeparator, folderPath);
};

const getBinaryString$1 = file => {
  return blobToBinaryString(file);
};

const getFile = handle => {
  return handle.getFile();
};
const getBinaryString = async handle => {
  const file = await getFile(handle);
  const text = await getBinaryString$1(file);
  return text;
};

const join = (pathSeparator, ...parts) => {
  return parts.join(pathSeparator);
};

const uploadFile = async (fileSystemHandle, pathSeparator, root) => {
  const content = await getBinaryString(fileSystemHandle);
  const to = join(pathSeparator, root, fileSystemHandle.name);
  await writeFile(to, content);
};

const uploadHandle = async (fileSystemHandle, pathSeparator, root, uploadHandles) => {
  const {
    kind
  } = fileSystemHandle;
  switch (kind) {
    case Directory:
      return uploadDirectory(fileSystemHandle, pathSeparator, root, uploadHandles);
    case File$1:
      return uploadFile(fileSystemHandle, pathSeparator, root);
    default:
      throw new Error(`unsupported file system handle type ${kind}`);
  }
};

const uploadHandles = async (fileSystemHandles, pathSeparator, root) => {
  for (const fileSystemHandle of fileSystemHandles) {
    await uploadHandle(fileSystemHandle, pathSeparator, root, uploadHandles);
  }
};
const uploadFileSystemHandles = async (root, pathSeparator, fileSystemHandles) => {
  if (fileSystemHandles.length === 1) {
    const file = fileSystemHandles[0];
    const {
      kind,
      name
    } = file;
    if (kind === Directory) {
      await invoke('PersistentFileHandle.addHandle', `/${name}`, file);
      await invoke('Workspace.setPath', `html:///${name}`);
      return true;
    }
  }
  await uploadHandles(fileSystemHandles, pathSeparator, root);
  return false;
};

const commandMap = {
  'Blob.base64StringToBlob': base64StringToBlob,
  'Blob.binaryStringToBlob': binaryStringToBlob,
  'Blob.blobToBinaryString': blobToBinaryString,
  'FileSystem.appendFile': appendFile,
  'FileSystem.copy': copy,
  'FileSystem.createFile': createFile,
  'FileSystem.executeWatchCallback': executeWatchCallback,
  'FileSystem.exists': exists,
  'FileSystem.getFolderSize': getFolderSize,
  'FileSystem.getPathSeparator': getPathSeparator,
  'FileSystem.getRealPath': getRealPath,
  'FileSystem.handleMessagePort': handleMessagePort,
  'FileSystem.mkdir': mkdir,
  'FileSystem.readDirWithFileTypes': readDirWithFileTypes,
  'FileSystem.readFile': readFile,
  'FileSystem.readFileAsBlob': readFileAsBlob,
  'FileSystem.readJson': readJson,
  'FileSystem.remove': remove,
  'FileSystem.rename': rename,
  'FileSystem.stat': stat,
  'FileSystem.unwatchFile': unwatchFile,
  'FileSystem.uploadFileSystemHandles': uploadFileSystemHandles,
  'FileSystem.watchFile': watchFile$2,
  'FileSystem.writeBlob': writeBlob,
  'FileSystem.writeFile': writeFile,
  'Initialize.initialize': initialize
};

/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */

const state = Object.create(null);
const register = modules => {
  Object.assign(state, modules);
};

const Memfs = 'memfs';
const Fetch = 'fetch';
const File = 'file';

// TODO add others
const fileSystemModules = {
  [Fetch]: FileSystemFetch,
  [File]: FileSystemDisk,
  [Memfs]: FileSystemMemory
};

const initializeExtensionHostWorker = async () => {
  try {
    const rpc = await create$4({
      commandMap: {},
      send: sendMessagePortToExtensionHostWorker
    });
    set$4(rpc);
  } catch (error) {
    throw new VError(error, `Failed to create extension host rpc`);
  }
};

const initializeRendererProcess = async () => {
  try {
    const rpc = await create$4({
      commandMap: {},
      send: sendMessagePortToRendererProcess
    });
    set$2(rpc);
  } catch (error) {
    throw new VError(error, `Failed to create renderer process rpc`);
  }
};

const initializeRendererWorker = async () => {
  const rpc = await create$1({
    commandMap: commandMap
  });
  set$1(rpc);
};

const listen = async () => {
  Object.assign(commandMapRef, commandMap);
  register(fileSystemModules);
  await Promise.all([initializeExtensionHostWorker(), initializeRendererProcess(), initializeRendererWorker()]);
};

const main = async () => {
  await listen();
};

main();
