const toCommandId = key => {
  const dotIndex = key.indexOf('.');
  return key.slice(dotIndex + 1);
};
const create$a = () => {
  const states = Object.create(null);
  const commandMapRef = {};
  return {
    clear() {
      for (const key of Object.keys(states)) {
        delete states[key];
      }
    },
    diff(uid, modules, numbers) {
      const {
        newState,
        oldState
      } = states[uid];
      const diffResult = [];
      for (let i = 0; i < modules.length; i++) {
        const fn = modules[i];
        if (!fn(oldState, newState)) {
          diffResult.push(numbers[i]);
        }
      }
      return diffResult;
    },
    dispose(uid) {
      delete states[uid];
    },
    get(uid) {
      return states[uid];
    },
    getCommandIds() {
      const keys = Object.keys(commandMapRef);
      const ids = keys.map(toCommandId);
      return ids;
    },
    getKeys() {
      return Object.keys(states).map(key => {
        return Number.parseFloat(key);
      });
    },
    registerCommands(commandMap) {
      Object.assign(commandMapRef, commandMap);
    },
    set(uid, oldState, newState) {
      states[uid] = {
        newState,
        oldState
      };
    },
    wrapCommand(fn) {
      const wrapped = async (uid, ...args) => {
        const {
          newState,
          oldState
        } = states[uid];
        const newerState = await fn(newState, ...args);
        if (oldState === newerState || newState === newerState) {
          return;
        }
        const latestOld = states[uid];
        const latestNew = {
          ...latestOld.newState,
          ...newerState
        };
        states[uid] = {
          newState: latestNew,
          oldState: latestOld.oldState
        };
      };
      return wrapped;
    },
    wrapGetter(fn) {
      const wrapped = (uid, ...args) => {
        const {
          newState
        } = states[uid];
        return fn(newState, ...args);
      };
      return wrapped;
    },
    wrapLoadContent(fn) {
      const wrapped = async (uid, ...args) => {
        const {
          newState,
          oldState
        } = states[uid];
        const result = await fn(newState, ...args);
        const {
          error,
          state
        } = result;
        if (oldState === state || newState === state) {
          return {
            error
          };
        }
        const latestOld = states[uid];
        const latestNew = {
          ...latestOld.newState,
          ...state
        };
        states[uid] = {
          newState: latestNew,
          oldState: latestOld.oldState
        };
        return {
          error
        };
      };
      return wrapped;
    }
  };
};
const terminate = () => {
  globalThis.close();
};

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;
    }
  }
}

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 = 4;
const Boolean$1 = 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;
    case 'object':
      if (value === null) {
        return Null;
      }
      if (Array.isArray(value)) {
        return Array$1;
      }
      return Object$1;
    case 'boolean':
      return Boolean$1;
    default:
      return Unknown;
  }
};
const number = value => {
  const type = getType(value);
  if (type !== Number$1) {
    throw new AssertionError('expected value to be of type number');
  }
};
const array = value => {
  const type = getType(value);
  if (type !== Array$1) {
    throw new AssertionError('expected value to be of type array');
  }
};
const string = value => {
  const type = getType(value);
  if (type !== String) {
    throw new AssertionError('expected value to be of type string');
  }
};

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 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$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$1
  });
  messagePort.start();
  const {
    event,
    type
  } = await eventPromise;
  if (type !== Message$1$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
};

class CommandNotFoundError extends Error {
  constructor(command) {
    super(`Command not found ${command}`);
    this.name = 'CommandNotFoundError';
  }
}
const commands = Object.create(null);
const register = 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$2 = id => {
  return callbacks[id];
};
const remove$1 = 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$2(id);
  if (!fn) {
    console.log(response);
    warn(`callback ${id} may already be disposed`);
    return;
  }
  fn(response);
  remove$1(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$9 = (message, result) => {
  return {
    id: message.id,
    jsonrpc: Two$1,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$9(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$8 = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};

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

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

const registerPromise = map => {
  const id = create$6();
  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$7(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$8(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$5 = async ({
  commandMap,
  isMessagePortOpen = true,
  messagePort
}) => {
  // TODO create a commandMap per rpc instance
  register(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$4 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  const {
    port1,
    port2
  } = new MessageChannel();
  await send(port1);
  return create$5({
    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$3 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  return createSharedLazyRpc(() => {
    return create$4({
      commandMap,
      isMessagePortOpen,
      send
    });
  });
};

const create$2 = async ({
  commandMap
}) => {
  // TODO create a commandMap per rpc instance
  register(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$4 = (id, rpc) => {
  rpcs[id] = rpc;
};
const get$1 = id => {
  return rpcs[id];
};
const remove = id => {
  delete rpcs[id];
};

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

const None$1 = 'none';
const ToolBar = 'toolbar';
const Tree = 'tree';
const TreeItem = 'treeitem';

const Button$1 = 1;
const Div = 4;
const Input = 6;
const Span = 8;
const Text = 12;
const Img = 17;
const A = 53;

const Space = 9;
const PageUp = 10;
const PageDown = 11;
const End = 255;
const Home = 12;
const LeftArrow = 13;
const UpArrow = 14;
const RightArrow = 15;
const DownArrow = 16;

const ProblemsFilter$1 = 25;

const Checked = 2;
const Unchecked = 3;

const EditorWorker$1 = 99;
const RendererWorker$1 = 1;

const {
  invoke: invoke$1,
  set: set$3
} = create$1(EditorWorker$1);
const getProblems$2 = async () => {
  // @ts-ignore
  return invoke$1('Editor.getProblems');
};

const EditorWorker = {
  __proto__: null,
  getProblems: getProblems$2,
  invoke: invoke$1,
  set: set$3
};

const {
  invoke,
  invokeAndTransfer,
  set: set$2
} = create$1(RendererWorker$1);
const showContextMenu2 = async (uid, menuId, x, y, args) => {
  number(uid);
  number(menuId);
  number(x);
  number(y);
  await invoke('ContextMenu.show2', uid, menuId, x, y, args);
};
const sendMessagePortToEditorWorker$1 = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToEditorWorker', port, command, rpcId);
};
const writeClipBoardText$1 = async text => {
  await invoke('ClipBoard.writeText', /* text */text);
};

const RendererWorker = {
  __proto__: null,
  invoke,
  invokeAndTransfer,
  sendMessagePortToEditorWorker: sendMessagePortToEditorWorker$1,
  set: set$2,
  showContextMenu2,
  writeClipBoardText: writeClipBoardText$1
};

const {
  sendMessagePortToEditorWorker,
  set: set$1,
  writeClipBoardText
} = RendererWorker;

const writeText = async text => {
  await writeClipBoardText(text);
};

const copyMessage = async state => {
  const {
    focusedIndex,
    problems
  } = state;
  const problem = problems[focusedIndex];
  await writeText(problem.message);
  return state;
};

const User = 1;
const Script = 2;

const {
  get,
  getCommandIds,
  registerCommands,
  set,
  wrapCommand,
  wrapGetter
} = create$a();

const None = 0;
const Table = 1;
const List = 2;

const create = (id, uri, x, y, width, height, workspaceUri) => {
  const state = {
    collapsedUris: [],
    filteredProblems: [],
    filterValue: '',
    focusedIndex: -2,
    height,
    inputSource: User,
    itemHeight: 22,
    listItems: [],
    maxLineY: 0,
    message: '',
    minLineY: 0,
    problems: [],
    showErrors: true,
    showInfos: true,
    showWarnings: true,
    smallWidthBreakPoint: 650,
    uid: id,
    viewMode: None,
    width,
    workspaceUri,
    x,
    y
  };
  set(id, state, state);
};

const isEqual$1 = (oldState, newState) => {
  return newState.inputSource === User || oldState.filterValue === newState.filterValue;
};

const isEqual = (oldState, newState) => {
  return oldState.problems === newState.problems && oldState.filterValue === newState.filterValue && oldState.message === newState.message && oldState.viewMode === newState.viewMode;
};

const RenderItems = 1;
const RenderFilterValue = 2;

const modules = [isEqual, isEqual$1];
const numbers = [RenderItems, RenderFilterValue];

const diff = (oldState, newState) => {
  const diffResult = [];
  for (let i = 0; i < modules.length; i++) {
    const fn = modules[i];
    if (!fn(oldState, newState)) {
      diffResult.push(numbers[i]);
    }
  }
  return diffResult;
};

const diff2 = uid => {
  const {
    newState,
    oldState
  } = get(uid);
  const diffResult = diff(oldState, newState);
  return diffResult;
};

const focusIndex = (state, index) => {
  return {
    ...state,
    focusedIndex: index
  };
};

const mergeClassNames = (...classNames) => {
  return classNames.filter(Boolean).join(' ');
};

const text = data => {
  return {
    childCount: 0,
    text: data,
    type: Text
  };
};

const FocusProblems = 19;

const getKeyBindings = () => {
  return [{
    command: 'Problems.focusNext',
    key: DownArrow,
    when: FocusProblems
  }, {
    command: 'Problems.focusPrevious',
    key: UpArrow,
    when: FocusProblems
  }, {
    command: 'Problems.focusFirst',
    key: Home,
    when: FocusProblems
  }, {
    command: 'Problems.focusFirst',
    key: PageUp,
    when: FocusProblems
  }, {
    command: 'Problems.focusLast',
    key: PageDown,
    when: FocusProblems
  }, {
    command: 'Problems.focusLast',
    key: End,
    when: FocusProblems
  }, {
    command: 'Problems.selectCurrent',
    key: Space,
    when: FocusProblems
  }, {
    command: 'Problems.focusFirst',
    key: Home,
    when: FocusProblems
  }, {
    command: 'Problems.focusLast',
    key: End,
    when: FocusProblems
  }, {
    command: 'Problems.handleArrowLeft',
    key: LeftArrow,
    when: FocusProblems
  }, {
    command: 'Problems.handleArrowRight',
    key: RightArrow,
    when: FocusProblems
  }];
};

const emptyObject = {};
const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
const i18nString = (key, placeholders = emptyObject) => {
  if (placeholders === emptyObject) {
    return key;
  }
  const replacer = (match, rest) => {
    return placeholders[rest];
  };
  return key.replaceAll(RE_PLACEHOLDER, replacer);
};

const ClearFilters = 'Clear Filters';
const Code = 'Code';
const File = 'File';
const Filter$3 = 'Filter';
const LineColumn = '[Ln {PH1}, Col {PH2}]';
const Message$1 = 'Message';
const NoProblemsDetected = 'No problems have been detected in the workspace.';
const NoResultsFoundWithProvidedFilterCriteria = 'No results found with provided filter criteria.';
const ProblemsDetected = 'Some problems have been detected in the workspace.';
const ShowingOfItems = 'Showing {PH1} of {PH2} ';
const Source = 'Source';
const CollapseAll$1 = 'Collapse All';
const ViewAsTable = 'View as Table';
const ViewAsList = 'View as List';
const ShowErrors = 'Show Errors';
const ShowWarnings = 'Show Warnings';
const ShowInfos = 'Show Infos';

const noProblemsDetected = () => {
  return i18nString(NoProblemsDetected);
};
const getMessage = problemsCount => {
  if (problemsCount === 0) {
    return i18nString(NoProblemsDetected);
  }
  return i18nString(ProblemsDetected);
};
const atLineColumn = (line, column) => {
  return i18nString(LineColumn, {
    PH1: line,
    PH2: column
  });
};
const clearFilter = () => {
  return i18nString(ClearFilters);
};
const code = () => {
  return i18nString(Code);
};
const message = () => {
  return i18nString(Message$1);
};
const file = () => {
  return i18nString(File);
};
const filter = () => {
  return i18nString(Filter$3);
};
const showingOf = (visibleCount, totalCount) => {
  return i18nString(ShowingOfItems, {
    PH1: visibleCount,
    PH2: totalCount
  });
};
const source = () => {
  return i18nString(Source);
};
const noResultsFoundWithProvidedFilterCriteria = () => {
  return i18nString(NoResultsFoundWithProvidedFilterCriteria);
};
const collapseAll = () => {
  return i18nString(CollapseAll$1);
};
const viewAsList$1 = () => {
  return i18nString(ViewAsList);
};
const viewAsTable$1 = () => {
  return i18nString(ViewAsTable);
};
const showErrors = () => {
  return i18nString(ShowErrors);
};
const showWarnings = () => {
  return i18nString(ShowWarnings);
};
const showInfos = () => {
  return i18nString(ShowInfos);
};

const getMenuEntriesFilter = state => [{
  command: '-1',
  flags: state.showErrors ? Checked : Unchecked,
  id: 'show-errors',
  label: showErrors()
}, {
  command: '-1',
  flags: state.showWarnings ? Checked : Unchecked,
  id: 'show-warnings',
  label: showWarnings()
}, {
  command: '-1',
  flags: state.showInfos ? Checked : Unchecked,
  id: 'show-infos',
  label: showInfos()
}];

const getMenuEntries2 = (state, props) => {
  const {
    menuId
  } = props;
  switch (menuId) {
    case ProblemsFilter$1:
      return getMenuEntriesFilter(state);
    default:
      return [];
  }
};

const getMenuIds = () => {
  return [ProblemsFilter$1];
};

const Item = 0;
const Expanded = 1;
const Collapsed = 2;

const getArrowLeftNewFocusedIndex = (problems, collapsedUris, focusedIndex) => {
  for (let i = focusedIndex; i >= 0; i--) {
    const problem = problems[i];
    if (problem.listItemType !== Item) {
      return {
        index: i,
        newCollapsedUris: [...collapsedUris, problem.uri]
      };
    }
  }
  return {
    index: 0,
    newCollapsedUris: collapsedUris
  };
};
const handleArrowLeft = state => {
  const {
    collapsedUris,
    focusedIndex,
    problems
  } = state;
  const {
    index,
    newCollapsedUris
  } = getArrowLeftNewFocusedIndex(problems, collapsedUris, focusedIndex);
  return {
    ...state,
    collapsedUris: newCollapsedUris,
    focusedIndex: index
  };
};

const removeElement = (array, element) => {
  const index = array.indexOf(element);
  if (index === -1) {
    return array;
  }
  return [...array.slice(0, index), ...array.slice(index + 1)];
};

const getArrowRightNewFocusedIndex = (problems, collapsedUris, focusedIndex) => {
  const problem = problems[focusedIndex];
  const newCollapsedUris = removeElement(collapsedUris, problem.uri);
  return {
    index: focusedIndex,
    newCollapsedUris
  };
};
const handleArrowRight = state => {
  const {
    collapsedUris,
    focusedIndex,
    problems
  } = state;
  const {
    index,
    newCollapsedUris
  } = getArrowRightNewFocusedIndex(problems, collapsedUris, focusedIndex);
  return {
    ...state,
    collapsedUris: newCollapsedUris,
    focusedIndex: index
  };
};

const handleBlur = state => {
  return {
    ...state,
    focusedIndex: -2
  };
};

const getListIndex = (eventX, eventY, x, y, deltaY, itemHeight) => {
  const relativeY = eventY - y + deltaY;
  const index = Math.floor(relativeY / itemHeight);
  return index;
};

const handleClickAt = (state, eventX, eventY) => {
  const {
    itemHeight,
    problems,
    x,
    y
  } = state;

  // TODO use functional focus rendering
  // Focus.setFocus(FocusKey.Problems)
  if (problems.length === 0) {
    return focusIndex(state, -1);
  }
  const index = getListIndex(eventX, eventY, x, y, 0, itemHeight);
  if (index > problems.length) {
    return focusIndex(state, -1);
  }
  return {
    ...state,
    focusedIndex: index
  };
};

const handleClickButton = async (state, name) => {
  return state;
};

const show2 = async (uid, menuId, x, y, args) => {
  await showContextMenu2(uid, menuId, x, y, args);
};

const handleClickMoreFilters = async (state, eventX, eventY) => {
  const {
    uid
  } = state;
  await show2(uid, ProblemsFilter$1, eventX, eventY, {
    menuId: ProblemsFilter$1
  });
  return state;
};

const handleContextMenu = async (state, eventX, eventY) => {
  const {
    uid
  } = state;
  await show2(uid, ProblemsFilter$1, eventX, eventY, {
    menuId: ProblemsFilter$1
  });
  return state;
};

const handleFilterInput = (state, value, inputSource = Script) => {
  return {
    ...state,
    filterValue: value,
    inputSource
  };
};

const handleIconThemeChange = state => {
  return {
    ...state,
    problems: [...state.problems]
  };
};

const initialize = async () => {
  // function is not needed anymore
};

const {
  getProblems: getProblems$1} = EditorWorker;

const toProblem = (diagnostic, index) => {
  const {
    code,
    columnIndex,
    message,
    rowIndex,
    source,
    type,
    uri
  } = diagnostic;
  return {
    code: code || '',
    columnIndex: columnIndex || 0,
    count: 0,
    fileName: '',
    level: 2,
    listItemType: Item,
    message: message || '',
    posInSet: index,
    relativePath: '',
    rowIndex: rowIndex || 0,
    setSize: 1,
    source: source || '',
    type: type || 'error',
    uri
  };
};
const getRelativeParentUri = (uri, workspaceUri) => {
  const slashIndex = uri.lastIndexOf('/');
  return uri.slice(0, slashIndex).slice(workspaceUri.length);
};
const getFileName = uri => {
  const slashIndex = uri.lastIndexOf('/');
  return uri.slice(slashIndex + 1);
};
const toProblems = (diagnostics, workspaceUri = '') => {
  const problems = [];
  let problem = {
    code: '',
    columnIndex: 0,
    count: 0,
    fileName: '',
    level: 0,
    listItemType: 0,
    message: '',
    posInSet: 0,
    relativePath: '',
    rowIndex: 0,
    setSize: 0,
    source: '',
    type: '',
    uri: ''
  };
  let relativeIndex = 0;
  for (const diagnostic of diagnostics) {
    if (diagnostic.uri === problem.uri) {
      relativeIndex++;
      problem.count++;
    } else {
      relativeIndex = 1;
      problem = {
        code: '',
        columnIndex: 0,
        count: 1,
        fileName: '',
        level: 1,
        listItemType: Expanded,
        message: '',
        posInSet: relativeIndex,
        relativePath: '',
        rowIndex: 0,
        setSize: 123,
        source: '',
        type: '',
        uri: diagnostic.uri
      };
      problems.push(problem);
    }
    problems.push(toProblem(diagnostic, relativeIndex));
  }
  for (const problem of problems) {
    // TODO maybe rename property, this should be the relative path of the parent folder of the file
    // @ts-ignore
    problem.relativePath = getRelativeParentUri(problem.uri, workspaceUri);
    // @ts-ignore
    problem.fileName = getFileName(problem.uri);
    // problem.uri = problem.uri // TODO
  }
  return problems;
};

// TODO send event listener to editor, asking to send problem updates to this worker

const getProblems = async workspaceUri => {
  try {
    const diagnostics = await getProblems$1();
    // @ts-ignore
    const problems = toProblems(diagnostics, workspaceUri);
    return {
      error: '',
      problems
    };
  } catch (error) {
    return {
      error: `${error}`,
      problems: []
    };
  }
};

const getSavedViewMode = savedState => {
  if (savedState && typeof savedState.viewMode === 'number') {
    return savedState.viewMode;
  }
  return List;
};
const getSavedFilterValue = savedState => {
  if (savedState && typeof savedState.filterValue === 'string') {
    return savedState.filterValue;
  }
  return '';
};
const isString = value => {
  return typeof value === 'string';
};
const getSavedCollapsedUris = savedState => {
  if (savedState && savedState.collapsedUris && Array.isArray(savedState.collapsedUris) && savedState.collapsedUris.every(isString)) {
    return savedState.collapsedUris;
  }
  return [];
};
const loadContent = async (state, savedState) => {
  const {
    error,
    problems
  } = await getProblems(state.workspaceUri);
  if (error) {
    return {
      ...state,
      message: error
    };
  }
  const message = getMessage(problems.length);
  const viewMode = getSavedViewMode(savedState);
  const filterValue = getSavedFilterValue(savedState);
  const collapsedUris = getSavedCollapsedUris(savedState);
  return {
    ...state,
    collapsedUris,
    filteredProblems: problems,
    filterValue,
    inputSource: Script,
    listItems: [],
    message,
    problems,
    viewMode
  };
};

const Filter$2 = 'filter';

const renderFilterValue = (oldState, newState) => {
  return ['Viewlet.setValueByName', Filter$2, newState.filterValue];
};

const Button = 1;
const ProblemsFilter = 4;

const Chevron = 'Chevron';
const FileIcon = 'FileIcon';
const Filter$1 = 'Filter';
const FilterBadge = 'FilterBadge';
const Highlight = 'Highlight';
const IconButton = 'IconButton';
const InputBox = 'InputBox';
const Label = 'Label';
const Actions = 'Actions';
const LabelDetail = 'LabelDetail';
const Message = 'Message';
const MessageAction = 'MessageAction';
const Problem = 'Problem';
const ProblemAt = 'ProblemAt';
const ProblemBadge = 'ProblemBadge';
const Problems = 'Problems';
const ProblemSelected = 'ProblemSelected';
const ProblemsErrorIcon = 'ProblemsErrorIcon';
const ProblemsIcon = 'ProblemsIcon';
const ProblemsList = 'ProblemsList';
const ProblemsTable = 'ProblemsTable';
const ProblemsTableBody = 'ProblemsTableBody';
const ProblemsTableHeader = 'ProblemsTableHeader';
const ProblemsTableRow = 'ProblemsTableRow';
const ProblemsTableRowItem = 'ProblemsTableRowItem';
const ProblemsTableRowOdd = 'ProblemsTableRowOdd';
const ProblemsWarningIcon = 'ProblemsWarningIcon';
const Viewlet = 'Viewlet';

const HandleBlur = 1;
const HandleClearFilterClick = 2;
const HandleContextMenu = 3;
const HandleFilterInput = 4;
const HandlePointerDown = 5;
const HandleClickMoreFilters = 6;

const getIconVirtualDom = (icon, type = Div) => {
  return {
    childCount: 0,
    className: mergeClassNames('MaskIcon', `MaskIcon${icon}`),
    role: None$1,
    type
  };
};

const getActionButtonVirtualDom = action => {
  const {
    command,
    icon,
    id
  } = action;
  return [{
    childCount: 1,
    className: IconButton,
    'data-command': command,
    title: id,
    type: Button$1
  }, getIconVirtualDom(icon)];
};

const getFilterBadgeDom = badgeText => {
  if (!badgeText) {
    return [];
  }
  return [{
    childCount: 1,
    className: FilterBadge,
    type: Div
  }, text(badgeText)];
};

const getInputBoxVirtualDom = (name, onInput, placeholder) => {
  return {
    autocapitalize: 'off',
    autocorrect: 'off',
    className: InputBox,
    name,
    onInput,
    placeholder,
    spellcheck: false,
    type: Input
  };
};

const CollapseAll = 'CollapseAll';
const Filter = 'Filter';
const ListFlat = 'ListFlat';
const ListTree = 'ListTree';

const getChildCount = badgeText => {
  let childCount = 0;
  childCount++; // input
  if (badgeText) {
    childCount++; // badge
  }
  childCount++; // action button
  return childCount;
};
const getProblemsFilterVirtualDom = action => {
  return [{
    childCount: getChildCount(action.badgeText),
    className: Filter$1,
    type: Div
  }, getInputBoxVirtualDom(Filter$2, action.command, action.placeholder || ''), ...getFilterBadgeDom(action.badgeText), ...getActionButtonVirtualDom({
    command: 'more filters',
    icon: Filter,
    id: 'more filters'})];
};

const getNoResultsWithFilterVirtualDom = () => {
  return [{
    childCount: 3,
    className: Message,
    type: Div
  }, {
    childCount: 1,
    type: Span
  }, text(noResultsFoundWithProvidedFilterCriteria()), {
    childCount: 1,
    className: MessageAction,
    onClick: HandleClearFilterClick,
    type: A
  }, text(clearFilter()), text('.')];
};

const getBadgeVirtualDom = (className, count) => {
  return [{
    childCount: 1,
    className: mergeClassNames('Badge', className) + (''),
    type: Div
  }, text(`${count}`)];
};

const getChevronDownVirtualDom = (extraClassName = '') => {
  return {
    childCount: 0,
    className: mergeClassNames(Chevron, 'MaskIconChevronDown', extraClassName) + (extraClassName === '' ? ' ' : ''),
    type: Div
  };
};
const getChevronRightVirtualDom = (extraClassName = '') => {
  return {
    childCount: 0,
    className: mergeClassNames(Chevron, 'MaskIconChevronRight', extraClassName) + (extraClassName === '' ? ' ' : ''),
    type: Div
  };
};

const getFileIconVirtualDom = icon => {
  return {
    childCount: 0,
    className: FileIcon,
    role: None$1,
    src: icon,
    type: Img
  };
};

const Warning = 'warning';

const getProblemsIconVirtualDom = type => {
  if (type === Warning) {
    return {
      childCount: 0,
      className: mergeClassNames(ProblemsIcon, ProblemsWarningIcon),
      type: Div
    };
  }
  return {
    childCount: 0,
    className: mergeClassNames(ProblemsIcon, ProblemsErrorIcon),
    type: Div
  };
};

// TODO compute detail message in getVisibleProblems
const getProblemSourceDetail = (source, code) => {
  let message = '';
  if (source) {
    message += `${source}`;
  }
  if (code) {
    message += `(${code})`;
  }
  message += ' ';
  return message;
};

const defaultIndent = 1;
const getTreeItemIndent = depth => {
  return `${depth * defaultIndent}rem`;
};

const getProblemVirtualDom = problem => {
  const {
    code,
    columnIndex,
    fileName,
    filterValueLength,
    icon,
    isActive,
    isCollapsed,
    level,
    listItemType,
    message,
    messageMatchIndex,
    posInSet,
    relativePath,
    rowIndex,
    setSize,
    source,
    type
  } = problem;
  let className = Problem;
  if (isActive) {
    className += ' ' + ProblemSelected;
  }
  if (listItemType === Expanded || listItemType === Collapsed) {
    return [{
      ariaExpanded: !isCollapsed,
      ariaLevel: level,
      ariaPosInSet: posInSet,
      ariaSelected: isActive,
      ariaSetSize: setSize,
      childCount: 5,
      className,
      paddingLeft: getTreeItemIndent(1),
      role: TreeItem,
      type: Div
    }, listItemType === Collapsed ? getChevronRightVirtualDom() : getChevronDownVirtualDom(), getFileIconVirtualDom(icon), text(fileName), {
      childCount: 1,
      className: LabelDetail,
      type: Div
    }, text(relativePath), ...getBadgeVirtualDom(ProblemBadge, problem.count)];
  }
  const lineColumn = atLineColumn(rowIndex, columnIndex);
  const label = {
    childCount: 1,
    className: Label,
    type: Div
  };
  /**
   * @type {any}
   */
  const dom = [{
    ariaLevel: level,
    ariaPosInSet: posInSet,
    ariaSelected: isActive,
    ariaSetSize: setSize,
    childCount: 3,
    className,
    paddingLeft: getTreeItemIndent(2),
    role: TreeItem,
    type: Div
  }, getProblemsIconVirtualDom(type), label];
  if (filterValueLength) {
    const before = message.slice(0, messageMatchIndex);
    const middle = message.slice(messageMatchIndex, messageMatchIndex + filterValueLength);
    const after = message.slice(messageMatchIndex + filterValueLength);
    label.childCount += 2;
    dom.push(text(before), {
      childCount: 1,
      className: Highlight,
      type: Div
    }, text(middle), text(after));
  } else {
    dom.push(text(message));
  }
  dom.push({
    childCount: 2,
    className: ProblemAt,
    type: Span
  }, text(getProblemSourceDetail(source, code)), text(lineColumn));
  return dom;
};

const getProblemsListVirtualDom = problems => {
  const dom = [{
    ariaLabel: 'Problems Tree',
    // TODO use i18n string
    childCount: problems.length,
    className: ProblemsList,
    role: Tree,
    type: Div
  }, ...problems.flatMap(getProblemVirtualDom)];
  return dom;
};

const getProblemsNoProblemsFoundVirtualDom = () => {
  return [{
    childCount: 1,
    className: Message,
    type: Div
  }, text(noProblemsDetected())];
};

const getClassName = isEven => {
  if (isEven) {
    return ProblemsTableRow;
  }
  return mergeClassNames(ProblemsTableRow, ProblemsTableRowOdd);
};
const getProblemsTableRowVirtualDom = problem => {
  const {
    code,
    isEven,
    message,
    source,
    type,
    uri
  } = problem;
  // TODO problems are grouped by uri, depending
  // on which renderer is used the data needs to look different
  if (!message) {
    return [];
  }
  const dom = [{
    childCount: 5,
    className: getClassName(isEven),
    type: Div
  }, {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, getProblemsIconVirtualDom(type), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(getProblemSourceDetail(source, code)), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(message), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(uri), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(source)];
  return dom;
};

const getProblemsTableBodyVirtualDom = problems => {
  const dom = [{
    childCount: problems.length,
    className: ProblemsTableBody,
    type: Div
  }, ...problems.flatMap(getProblemsTableRowVirtualDom)];
  return dom;
};

const getProblemsTableHeaderVirtualDom = () => {
  const textCode = code();
  const textSource = source();
  const textMessage = message();
  const textFile = file();
  const dom = [{
    childCount: 1,
    className: ProblemsTableHeader,
    type: Div
  }, {
    childCount: 5,
    className: ProblemsTableRow,
    type: Div
  }, {
    childCount: 0,
    className: ProblemsTableRowItem,
    type: Div
  }, {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(textCode), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(textMessage), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(textFile), {
    childCount: 1,
    className: ProblemsTableRowItem,
    type: Div
  }, text(textSource)];
  return dom;
};

const getProblemsTableVirtualDom = problems => {
  const dom = [{
    childCount: 2,
    className: ProblemsTable,
    type: Div
  }, ...getProblemsTableHeaderVirtualDom(), ...getProblemsTableBodyVirtualDom(problems)];
  return dom;
};

const getProblemsVirtualDom$1 = (viewMode, problems, filterValue, message) => {
  if (problems.length === 0 && message) {
    return [{
      childCount: 1,
      className: Message,
      type: Div
    }, text(message)];
  }
  if (problems.length === 0 && filterValue) {
    return getNoResultsWithFilterVirtualDom();
  }
  if (problems.length === 0) {
    return getProblemsNoProblemsFoundVirtualDom();
  }
  if (viewMode === Table) {
    return getProblemsTableVirtualDom(problems);
  }
  return getProblemsListVirtualDom(problems);
};

const getProblemsVirtualDom = (viewMode, problems, filterValue, isSmall, message) => {
  const baseDom = {
    childCount: 1,
    className: mergeClassNames(Viewlet, Problems),
    onBlur: HandleBlur,
    onContextMenu: HandleContextMenu,
    onPointerDown: HandlePointerDown,
    tabIndex: 0,
    type: Div
  };
  const filterDom = isSmall ? getProblemsFilterVirtualDom({
    badgeText: '',
    command: HandleFilterInput,
    placeholder: filter()}) : [];
  const itemsDom = getProblemsVirtualDom$1(viewMode, problems, filterValue, message);
  return [baseDom, ...filterDom, ...itemsDom];
};

const matchesFilterValue = (string, filterValueLower) => {
  if (filterValueLower) {
    return string.toLowerCase().indexOf(filterValueLower);
  }
  return 0;
};

const getListItemType = (listItemType, isCollapsed) => {
  if (listItemType === Item) {
    return Item;
  }
  if (isCollapsed) {
    return Collapsed;
  }
  return Expanded;
};
const filterProblems = (problems, collapsedUris, filterValue) => {
  const filterValueLower = filterValue.toLowerCase();
  const filtered = [];
  for (const problem of problems) {
    const uriMatchIndex = matchesFilterValue(problem.uri, filterValueLower);
    const sourceMatchIndex = matchesFilterValue(problem.source, filterValueLower);
    const messageMatchIndex = matchesFilterValue(problem.message, filterValueLower);
    if (uriMatchIndex === -1 && sourceMatchIndex === -1 && messageMatchIndex === -1) {
      continue;
    }
    const isCollapsed = collapsedUris.includes(problem.uri);
    if (isCollapsed && problem.listItemType === Item) {
      continue;
    }
    filtered.push({
      ...problem,
      isCollapsed,
      listItemType: getListItemType(problem.listItemType, isCollapsed),
      messageMatchIndex,
      sourceMatchIndex,
      uriMatchIndex
    });
  }
  return filtered;
};

const getFileNameIcon = file => {
  return '';
};

const getIcon = uri => {
  if (!uri) {
    return '';
  }
  return getFileNameIcon();
};

const getVisibleProblems = (problems, collapsedUris, focusedIndex, filterValue) => {
  array(problems);
  array(collapsedUris);
  number(focusedIndex);
  string(filterValue);
  const visibleItems = [];
  const filterValueLength = filterValue.length;
  const filtered = filterProblems(problems, collapsedUris, filterValue);
  for (let i = 0; i < filtered.length; i++) {
    const problem = filtered[i];
    visibleItems.push({
      ...problem,
      filterValueLength,
      icon: getIcon(problem.uri),
      isActive: i === focusedIndex,
      isEven: i % 2 === 0
    });
  }
  return visibleItems;
};

const renderItems = (oldState, newState) => {
  const {
    collapsedUris,
    filterValue,
    focusedIndex,
    message,
    problems,
    smallWidthBreakPoint,
    viewMode,
    width
  } = newState;
  const visible = getVisibleProblems(problems, collapsedUris, focusedIndex, filterValue);
  const isSmall = width <= smallWidthBreakPoint;
  const dom = getProblemsVirtualDom(viewMode, visible, filterValue, isSmall, message);
  return ['Viewlet.setDom2', dom];
};

const getRenderer = diffType => {
  switch (diffType) {
    case RenderFilterValue:
      return renderFilterValue;
    case RenderItems:
      return renderItems;
    default:
      throw new Error('unknown renderer');
  }
};

const applyRender = (oldState, newState, diffResult) => {
  const commands = [];
  for (const item of diffResult) {
    const fn = getRenderer(item);
    commands.push(fn(oldState, newState));
  }
  return commands;
};

const render2 = (uid, diffResult) => {
  const {
    newState,
    oldState
  } = get(uid);
  set(uid, newState, newState);
  const commands = applyRender(oldState, newState, diffResult);
  return commands;
};

const getActionVirtualDom = action => {
  switch (action.type) {
    case Button:
      return getActionButtonVirtualDom(action);
    case ProblemsFilter:
      return getProblemsFilterVirtualDom(action);
    default:
      return [];
  }
};

const getActionsVirtualDom = actions => {
  return [{
    childCount: actions.length,
    className: Actions,
    role: ToolBar,
    type: Div
  }, ...actions.flatMap(getActionVirtualDom)];
};

const getActions = state => {
  const {
    collapsedUris,
    filterValue,
    focusedIndex,
    inputSource,
    problems,
    smallWidthBreakPoint,
    viewMode,
    width
  } = state;
  const visibleCount = getVisibleProblems(problems, collapsedUris, focusedIndex, filterValue).length;
  const problemsCount = problems.length;
  const isSmall = width <= smallWidthBreakPoint;
  const actions = [];
  if (!isSmall) {
    actions.push({
      badgeText: visibleCount === problemsCount ? '' : showingOf(visibleCount, problemsCount),
      command: HandleFilterInput,
      id: 'Filter',
      placeholder: filter(),
      type: ProblemsFilter,
      value: inputSource === Script ? filterValue : ''
    });
  }
  if (viewMode === Table) {
    actions.push({
      command: 'viewAsList',
      icon: ListTree,
      id: viewAsList$1(),
      type: Button
    });
  } else {
    actions.push({
      command: 'collapseAll',
      icon: CollapseAll,
      id: collapseAll(),
      type: Button
    }, {
      command: 'viewAsTable',
      icon: ListFlat,
      id: viewAsTable$1(),
      type: Button
    });
  }
  return actions;
};

const renderActions = uid => {
  const {
    newState
  } = get(uid);
  const actions = getActions(newState);
  const dom = getActionsVirtualDom(actions);
  return dom;
};

const renderEventListeners = () => {
  return [{
    name: HandleBlur,
    params: ['handleBlur']
  }, {
    name: HandleContextMenu,
    params: ['handleContextMenu', 'event.clientX', 'event.clientY'],
    preventDefault: true
  }, {
    name: HandleFilterInput,
    // @ts-ignore
    params: ['handleFilterInput', 'event.target.value', User]
  }, {
    name: HandleClearFilterClick,
    params: ['clearFilter']
  }, {
    name: HandlePointerDown,
    params: ['handleClickAt', 'event.clientX', 'event.clientY']
  }, {
    name: HandleClickMoreFilters,
    params: ['handleClickMoreFilters']
  }];
};

const resize = (state, dimensions) => {
  return {
    ...state,
    ...dimensions
  };
};

const saveState = state => {
  const {
    collapsedUris,
    filterValue,
    viewMode
  } = state;
  return {
    collapsedUris,
    filterValue,
    viewMode
  };
};

const viewAsList = state => {
  return {
    ...state,
    viewMode: List
  };
};

const viewAsTable = state => {
  return {
    ...state,
    viewMode: Table
  };
};

const commandMap = {
  'Problems.copyMessage': wrapCommand(copyMessage),
  'Problems.create': create,
  'Problems.diff2': diff2,
  'Problems.focusIndex': wrapCommand(focusIndex),
  'Problems.getCommandIds': getCommandIds,
  'Problems.getKeyBindings': getKeyBindings,
  'Problems.getMenuEntries2': wrapGetter(getMenuEntries2),
  'Problems.getMenuIds': getMenuIds,
  'Problems.handleArrowLeft': wrapCommand(handleArrowLeft),
  'Problems.handleArrowRight': wrapCommand(handleArrowRight),
  'Problems.handleBlur': wrapCommand(handleBlur),
  'Problems.handleClickAt': wrapCommand(handleClickAt),
  'Problems.handleClickButton': wrapCommand(handleClickButton),
  'Problems.handleClickMoreFilters': wrapCommand(handleClickMoreFilters),
  'Problems.handleContextMenu': wrapCommand(handleContextMenu),
  'Problems.handleFilterInput': wrapCommand(handleFilterInput),
  'Problems.handleIconThemeChange': wrapCommand(handleIconThemeChange),
  'Problems.initialize': initialize,
  'Problems.loadContent': wrapCommand(loadContent),
  'Problems.render2': render2,
  'Problems.renderActions': renderActions,
  'Problems.renderEventListeners': renderEventListeners,
  'Problems.resize': resize,
  'Problems.saveState': wrapGetter(saveState),
  'Problems.terminate': terminate,
  'Problems.viewAsList': wrapCommand(viewAsList),
  'Problems.viewAsTable': wrapCommand(viewAsTable)
};

const commandMapRef = {};

const send = port => {
  return sendMessagePortToEditorWorker(port, 0);
};
const initializeEditorWorkerRpc = async () => {
  try {
    const rpc = await create$3({
      commandMap: {},
      send
    });
    set$3(rpc);
    return rpc;
  } catch (error) {
    throw new VError(error, `Failed to create editor worker rpc`);
  }
};

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

const listen = async () => {
  Object.assign(commandMapRef, commandMap);
  registerCommands(commandMap);
  await Promise.all([initializeRendererWorker(), initializeEditorWorkerRpc()]);
};

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

main();
