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 object = value => {
  const type = getType(value);
  if (type !== Object$1) {
    throw new AssertionError('expected value to be of type object');
  }
};
const number = value => {
  const type = getType(value);
  if (type !== Number$1) {
    throw new AssertionError('expected value to be of type number');
  }
};

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 = 3;
const create$5 = 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,
  signal: signal$1,
  wrap: wrap$5
};

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 TypeError$1:
        return TypeError;
      case SyntaxError$1:
        return SyntaxError;
      case ReferenceError$1:
        return ReferenceError;
      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,
      message: error.message,
      data: error.stack
    };
  }
  return {
    code: Custom,
    message: prettyError.message,
    data: {
      stack: getStack(prettyError),
      codeFrame: prettyError.codeFrame,
      type: getErrorType(prettyError),
      code: prettyError.code,
      name: prettyError.name
    }
  };
};
const create$1$2 = (id, error) => {
  return {
    jsonrpc: Two$1,
    id,
    error
  };
};
const getErrorResponse = (id, error, preparePrettyError, logError) => {
  const prettyError = preparePrettyError(error);
  logError(error, prettyError);
  const errorProperty = getErrorProperty(error, prettyError);
  return create$1$2(id, errorProperty);
};
const create$3 = (message, result) => {
  return {
    jsonrpc: Two$1,
    id: message.id,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$3(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
  return {
    jsonrpc: Two$1,
    id,
    error: {
      code: Custom,
      // @ts-ignore
      message: error.message,
      data: error
    }
  };
};
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 {
      ipc: options.ipc,
      message: options.message,
      execute: options.execute,
      resolve: options.resolve || defaultResolve,
      preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
      logError: options.logError || defaultLogError,
      requiresSocket: options.requiresSocket || defaultRequiresSocket
    };
  }
  return {
    ipc: args[0],
    message: args[1],
    execute: args[2],
    resolve: args[3],
    preparePrettyError: args[4],
    logError: args[5],
    requiresSocket: args[6]
  };
};
const handleJsonRpcMessage = async (...args) => {
  const options = normalizeParams(args);
  const {
    message,
    ipc,
    execute,
    resolve,
    preparePrettyError,
    logError,
    requiresSocket
  } = 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');
};

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 = '2.0';
const create$s = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};
const create$r = (id, method, params) => {
  const message = {
    id,
    jsonrpc: Two,
    method,
    params
  };
  return message;
};
let id$1 = 0;
const create$q = () => {
  return ++id$1;
};

/* eslint-disable n/no-unsupported-features/es-syntax */

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

// @ts-ignore
const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
  const {
    id,
    promise
  } = registerPromise(callbacks);
  const message = create$r(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$s(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$4 = 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$2$1 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  const {
    port1,
    port2
  } = new MessageChannel();
  await send(port1);
  return create$4({
    commandMap,
    isMessagePortOpen,
    messagePort: port2
  });
};
const TransferMessagePortRpcParent = {
  __proto__: null,
  create: create$2$1
};
const create$1$1 = 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 WebWorkerRpcClient = {
  __proto__: null,
  create: create$1$1
};
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 None$3 = 'none';

const Button$2 = 1;
const Div = 4;
const Input$1 = 6;
const Text = 12;
const Img = 17;

const Button$1 = 'event.button';
const ClientX = 'event.clientX';
const ClientY = 'event.clientY';
const DeltaMode = 'event.deltaMode';
const DeltaY = 'event.deltaY';
const TargetValue = 'event.target.value';

const Script$1 = 2;

const ManageExtension = 8;
const ExtensionSearchFilter = 95;

const LeftClick = 0;

const ExtensionHostWorker = 44;
const ExtensionManagementWorker = 9006;
const RendererWorker = 1;

const FocusSelector = 'Viewlet.focusSelector';
const SetCss = 'Viewlet.setCss';
const SetDom2 = 'Viewlet.setDom2';
const SetPatches = 'Viewlet.setPatches';

const rpcs = Object.create(null);
const set$5 = (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$2 = 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$5(rpcId, mockRpc);
      // @ts-ignore
      mockRpc[Symbol.dispose] = () => {
        remove(rpcId);
      };
      // @ts-ignore
      return mockRpc;
    },
    set(rpc) {
      set$5(rpcId, rpc);
    }
  };
};

const {
  dispose: dispose$2,
  invoke: invoke$2,
  invokeAndTransfer: invokeAndTransfer$1,
  registerMockRpc,
  set: set$4
} = create$2(ExtensionHostWorker);
const executeReferenceProvider = async (id, offset) => {
  // @ts-ignore
  return invoke$2('ExtensionHostReference.executeReferenceProvider', id, offset);
};
const executeFileReferenceProvider = async id => {
  // @ts-ignore
  return invoke$2('ExtensionHostReference.executeFileReferenceProvider', id);
};
const getRuntimeStatus = async extensionId => {
  // @ts-ignore
  return invoke$2('ExtensionHost.getRuntimeStatus', extensionId);
};
const getEnabledOutputProviderIds = async () => {
  const channels = await invoke$2('Output.getEnabledProviders');
  return channels;
};

const ExtensionHost = {
  __proto__: null,
  dispose: dispose$2,
  executeFileReferenceProvider,
  executeReferenceProvider,
  getEnabledOutputProviderIds,
  getRuntimeStatus,
  invoke: invoke$2,
  invokeAndTransfer: invokeAndTransfer$1,
  registerMockRpc,
  set: set$4
};

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

const {
  invoke: invoke$1,
  invokeAndTransfer,
  set: set$2
} = create$2(RendererWorker);
const showContextMenu2 = async (uid, menuId, x, y, args) => {
  number(uid);
  number(menuId);
  number(x);
  number(y);
  // @ts-ignore
  await invoke$1('ContextMenu.show2', uid, menuId, x, y, args);
};
const sendMessagePortToExtensionHostWorker$1 = async (port, rpcId = 0) => {
  const command = 'HandleMessagePort.handleMessagePort2';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
};
const writeClipBoardText = async text => {
  await invoke$1('ClipBoard.writeText', /* text */text);
};
const sendMessagePortToExtensionManagementWorker = async (port, rpcId) => {
  const command = 'Extensions.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionManagementWorker', port, command, rpcId);
};
const openUri$1 = async (uri, focus, options) => {
  await invoke$1('Main.openUri', uri, focus, options);
};

const toCommandId = key => {
  const dotIndex = key.indexOf('.');
  return key.slice(dotIndex + 1);
};
const create$1 = () => {
  const states = Object.create(null);
  const commandMapRef = {};
  return {
    get(uid) {
      return states[uid];
    },
    set(uid, oldState, newState) {
      states[uid] = {
        oldState,
        newState
      };
    },
    dispose(uid) {
      delete states[uid];
    },
    getKeys() {
      return Object.keys(states).map(key => {
        return Number.parseInt(key);
      });
    },
    clear() {
      for (const key of Object.keys(states)) {
        delete states[key];
      }
    },
    wrapCommand(fn) {
      const wrapped = async (uid, ...args) => {
        const {
          oldState,
          newState
        } = states[uid];
        const newerState = await fn(newState, ...args);
        if (oldState === newerState || newState === newerState) {
          return;
        }
        const latest = states[uid];
        states[uid] = {
          oldState: latest.oldState,
          newState: newerState
        };
      };
      return wrapped;
    },
    wrapGetter(fn) {
      const wrapped = (uid, ...args) => {
        const {
          newState
        } = states[uid];
        return fn(newState, ...args);
      };
      return wrapped;
    },
    diff(uid, modules, numbers) {
      const {
        oldState,
        newState
      } = 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;
    },
    getCommandIds() {
      const keys = Object.keys(commandMapRef);
      const ids = keys.map(toCommandId);
      return ids;
    },
    registerCommands(commandMap) {
      Object.assign(commandMapRef, commandMap);
    }
  };
};
const terminate = () => {
  globalThis.close();
};

const None$2 = 0;
const Input = 1;
const List$2 = 2;

const handleError = async (error, notify = true, prefix = '') => {
  // @ts-ignore
  console.error(error);
};

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 NoExtensionsFound = 'No extensions found.';
const Filter$1 = 'Filter';
const Refresh$1 = 'Refresh';
const ClearExtensionSearchResults = 'Clear extension search results';
const Enable = 'Enable';
const Disable = 'Disable';
const EnableWorkspace = 'Enable Workspace';
const DisableWorkspace = 'Disable Workspace';
const InstallAnotherVersion = 'Install Another Version';
const SearchExtensionsInMarketplace = 'Search Extensions in Marketplace';
const ViewsAndMoreActions = 'Views and more Actions...';
const Extensions$2 = 'Extensions';
const Installed$1 = 'Installed';
const Copy = 'Copy';
const CopyExtensionId = 'Copy Extension Id';
const Name = 'Name';
const Id$1 = 'Id';
const Description = 'Description';
const Version = 'Version';
const Publisher = 'Publisher';
const MarketplaceLink = 'Marketplace Link';
const Category$1 = 'Category';
const Disabled$1 = 'Disabled';
const Featured = 'Featured';
const McpServers = 'MCP Servers';
const MostPopular = 'Most Popular';
const RecentlyPublished = 'Recently Published';
const Recommended = 'Recommended';
const Updates = 'Updates';
const BuiltIn = 'Built-in';
const Enabled$1 = 'Enabled';
const WorkspaceUnsupported = 'Workspace Unsupported';
const SortBy = 'Sort By';

const noExtensionsFound = () => {
  return i18nString(NoExtensionsFound);
};
const name = () => {
  return i18nString(Name);
};
const id = () => {
  return i18nString(Id$1);
};
const version = () => {
  return i18nString(Version);
};
const description = () => {
  return i18nString(Description);
};
const publisher = () => {
  return i18nString(Publisher);
};
const marketplaceLink = () => {
  return i18nString(MarketplaceLink);
};
const filter = () => {
  return i18nString(Filter$1);
};
const refresh = () => {
  return i18nString(Refresh$1);
};
const extensions = () => {
  return i18nString(Extensions$2);
};
const clearExtensionSearchResults = () => {
  return i18nString(ClearExtensionSearchResults);
};
const enable = () => {
  return i18nString(Enable);
};
const disable = () => {
  return i18nString(Disable);
};
const enableWorkspace = () => {
  return i18nString(EnableWorkspace);
};
const disableWorkspace = () => {
  return i18nString(DisableWorkspace);
};
const installAnotherVersion = () => {
  return i18nString(InstallAnotherVersion);
};
const copy = () => {
  return i18nString(Copy);
};
const copyExtensionId$1 = () => {
  return i18nString(CopyExtensionId);
};
const searchExtensionsInMarketPlace = () => {
  return i18nString(SearchExtensionsInMarketplace);
};
const viewsAndMoreActions = () => {
  return i18nString(ViewsAndMoreActions);
};
const installed = () => {
  return i18nString(Installed$1);
};
const category = () => {
  return i18nString(Category$1);
};
const disabled = () => {
  return i18nString(Disabled$1);
};
const featured = () => {
  return i18nString(Featured);
};
const mcpServers = () => {
  return i18nString(McpServers);
};
const mostPopular = () => {
  return i18nString(MostPopular);
};
const recentlyPublished = () => {
  return i18nString(RecentlyPublished);
};
const recommended = () => {
  return i18nString(Recommended);
};
const updates = () => {
  return i18nString(Updates);
};
const builtIn = () => {
  return i18nString(BuiltIn);
};
const enabled = () => {
  return i18nString(Enabled$1);
};
const workspaceUnsupported = () => {
  return i18nString(WorkspaceUnsupported);
};
const sortBy = () => {
  return i18nString(SortBy);
};

const getFinalDeltaY = (height, itemHeight, itemsLength) => {
  const contentHeight = itemsLength * itemHeight;
  const finalDeltaY = Math.max(contentHeight - height, 0);
  return finalDeltaY;
};

const Button = 1;

const HandleClearSearchResults = 1;
const HandleClickFilter = 2;
const HandleContextMenu = 3;
const HandleExtensionsInput = 4;
const HandleFocus = 5;
const HandlePointerDown = 7;
const HandleScrollBarMove = 9;
const HandleScrollBarPointerCaptureLost = 10;
const HandleScrollBarPointerDown = 11;
const HandleTouchEnd = 12;
const HandleTouchMove = 13;
const HandleTouchStart = 14;
const HandleWheel = 15;
const HandleHeaderContextMenu = 16;
const HandleBlur = 17;

const ClearAll = 'ClearAll';
const Filter = 'Filter';
const Refresh = 'Refresh';
const Ellipsis = 'Ellipsis';

const getInputActions = hasValue => {
  const clearEnabled = hasValue;
  const actions = [{
    enabled: clearEnabled,
    icon: `MaskIcon${ClearAll}`,
    onClick: HandleClearSearchResults,
    title: clearExtensionSearchResults(),
    type: Button
  }, {
    enabled: true,
    icon: `MaskIcon${Filter}`,
    onClick: HandleClickFilter,
    title: filter(),
    type: Button
  }];
  return actions;
};

const getListHeight = (itemsLength, itemHeight, maxHeight) => {
  number(itemsLength);
  number(itemHeight);
  number(maxHeight);
  if (itemsLength === 0) {
    return itemHeight;
  }
  const totalHeight = itemsLength * itemHeight;
  return Math.min(totalHeight, maxHeight);
};

// TODO optimize this function to return the minimum number
// of visible items needed, e.g. when not scrolled 5 items with
// 20px fill 100px but when scrolled 6 items are needed
const getNumberOfVisibleItems$1 = (listHeight, itemHeight) => {
  return Math.ceil(listHeight / itemHeight) + 1;
};

const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
  if (size >= contentSize) {
    return 0;
  }
  return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
};

const getScrollBarY$1 = (delta, finalDelta, size, scrollBarSize) => {
  if (finalDelta === 0) {
    return 0;
  }
  const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
  return scrollBarOffset;
};

const Installed = '@installed';
const Enabled = '@enabled';
const Disabled = '@disabled';
const Builtin = '@builtin';
const Sort = '@sort';
const Id = '@id';
const Category = '@category';
const Outdated = '@outdated';

const RE_PARAM = /@\w+(?::\w+)?/g;
const deserializeCategory = value => {
  if (value.startsWith(':"') && value.endsWith('"')) {
    return value.slice(2, -1);
  }
  if (value.startsWith('"') && value.endsWith('"')) {
    return value.slice(1, -1);
  }
  if (value.startsWith(':')) {
    return value.slice(1);
  }
  return value.trim();
};

// TODO test sorting and filtering
const parseValue = value => {
  let installed = false;
  let enabled = false;
  let disabled = false;
  let builtin = false;
  let sort = '';
  let id = '';
  let outdated = false;
  let category = '';
  // TODO this is not very functional code (assignment)
  const replaced = value.replaceAll(RE_PARAM, (match, by, order) => {
    if (match.startsWith(Installed)) {
      installed = true;
    }
    if (match.startsWith(Enabled)) {
      enabled = true;
    }
    if (match.startsWith(Disabled)) {
      disabled = true;
    }
    if (match.startsWith(Builtin)) {
      builtin = true;
    }
    if (match.startsWith(Sort)) {
      // Parse sort value after @sort:
      const sortValue = match.slice(Sort.length);
      if (sortValue.startsWith(':')) {
        sort = sortValue.slice(1);
      } else if (sortValue === '') {
        // Default sort if no value specified
        sort = 'installs';
      } else {
        // Handle cases like @sort:installs or @sortsize (without colon)
        sort = sortValue.replace(/^:/, '');
      }
    }
    if (match.startsWith(Id)) {
      // TODO improve parsing
      id = value.replace('@id:', '');
    }
    if (match.startsWith(Category)) {
      const raw = value.replace('@category', '');
      category = deserializeCategory(raw);
    }
    if (match.startsWith(Outdated)) {
      outdated = true;
    }
    return '';
  });
  const isLocal = enabled || builtin || disabled || outdated || installed;
  return {
    builtin,
    category,
    disabled,
    enabled,
    id,
    installed,
    isLocal,
    outdated,
    query: replaced,
    sort
  };
};

const matchesName = (extension, query) => {
  const extensionNameLower = extension.name.toLowerCase();
  return extensionNameLower.includes(query);
};
const matchesId = (extension, query) => {
  const extensionIdLower = extension.id.toLowerCase();
  return extensionIdLower.includes(query);
};
const matchesCategory = (extension, category) => {
  return extension && typeof extension === 'object' && 'categories' in extension && Array.isArray(extension.categories) && extension.categories.some(item => {
    return item.toLowerCase() === category;
  });
};
const matchesParsedValue = (extension, parsedValue) => {
  if (parsedValue.id && extension.id) {
    return parsedValue.id === extension.id;
  }
  if (parsedValue.category && matchesCategory(extension, parsedValue.category)) {
    return true;
  }
  if (extension.name) {
    return matchesName(extension, parsedValue.query);
  }
  if (extension.id) {
    return matchesId(extension, parsedValue.query);
  }
  return false;
};

const compareExtension = (extensionA, extensionB) => {
  return extensionA.name.localeCompare(extensionB.name) || extensionA.id.localeCompare(extensionB.id);
};

const sortExtensions = extensions => {
  return extensions.toSorted(compareExtension);
};

const getExtensions = async (extensions, parsedValue) => {
  const filteredExtensions = [];
  for (const extension of extensions) {
    if (matchesParsedValue(extension, parsedValue)) {
      filteredExtensions.push(extension);
    }
  }
  const sortedExtensions = sortExtensions(filteredExtensions);
  return sortedExtensions;
};

const searchExtensions = async (extensions, value, platform, assetDir) => {
  try {
    const parsedValue = parseValue(value);
    const filteredExtensions = await getExtensions(extensions, parsedValue);
    return filteredExtensions;
  } catch (error) {
    throw new VError(error, 'Failed to search for extensions');
  }
};

// TODO debounce
const handleChange = async (state, update) => {
  const fullNewState = {
    ...state,
    ...update
  };
  try {
    const value = fullNewState.searchValue.trim();
    const {
      inputSource
    } = fullNewState;
    const hasValue = value.length > 0;
    const inputActions = getInputActions(hasValue);
    const {
      allExtensions,
      assetDir,
      headerHeight,
      height,
      itemHeight,
      minimumSliderSize,
      platform
    } = state;
    // TODO cancel ongoing requests
    // TODO handle errors
    const items = await searchExtensions(allExtensions, value, platform, assetDir);
    if (items.length === 0) {
      return {
        ...state,
        allExtensions,
        deltaY: 0,
        finalDeltaY: 0,
        focus: Input,
        inputActions,
        inputSource,
        items,
        maxLineY: 0,
        message: noExtensionsFound(),
        minLineY: 0,
        placeholder: searchExtensionsInMarketPlace(),
        scrollBarHeight: 0,
        searchValue: value
      };
    }
    const total = items.length;
    const listHeight = getListHeight(total, itemHeight, height);
    const contentHeight = total * itemHeight;
    const scrollBarHeight = getScrollBarSize(height, contentHeight, minimumSliderSize);
    const numberOfVisible = getNumberOfVisibleItems$1(listHeight, itemHeight);
    const maxLineY = Math.min(numberOfVisible, total);
    const finalDeltaY = getFinalDeltaY(listHeight, itemHeight, total);
    const scrollBarY = getScrollBarY$1(0, finalDeltaY, height - headerHeight, scrollBarHeight);
    return {
      ...state,
      allExtensions,
      deltaY: 0,
      finalDeltaY,
      inputActions,
      inputSource,
      items,
      maxLineY,
      message: '',
      minLineY: 0,
      placeholder: searchExtensionsInMarketPlace(),
      scrollBarHeight,
      scrollBarY,
      searchValue: value
    };

    // TODO handle out of order responses (a bit complicated)
    // for now just assume everything comes back in order
  } catch (error) {
    // TODO send error to error worker
    await handleError(error);
    return {
      ...fullNewState,
      message: `${error}`
    };
  }
};

const clearSearchResults = state => {
  return handleChange(state, {
    ...state,
    focus: Input,
    inputSource: Script$1,
    searchValue: ''
  });
};

const closeSuggest = state => {
  return state;
};

const getFocusedItem = state => {
  const {
    focusedIndex,
    items
  } = state;
  if (focusedIndex === -1) {
    return undefined;
  }
  const item = items[focusedIndex];
  return item;
};

const copyExtensionId = async state => {
  const item = getFocusedItem(state);
  if (!item) {
    return state;
  }
  const {
    id
  } = item;
  await writeClipBoardText(id);
  return state;
};

const stringifyRow = row => {
  const {
    key,
    value
  } = row;
  return `${key}: ${value}`.trim();
};
const getExtensionInfoText = extension => {
  const {
    description: description$1,
    id: id$1,
    name: name$1,
    publisher: publisher$1
  } = extension;
  const version$1 = ''; // TODO
  const marketplaceLink$1 = ''; // TODO
  const rows = [{
    key: name(),
    value: name$1
  }, {
    key: id(),
    value: id$1
  }, {
    key: description(),
    value: description$1
  }, {
    key: version(),
    value: version$1
  }, {
    key: publisher(),
    value: publisher$1
  }, {
    key: marketplaceLink(),
    value: marketplaceLink$1
  }];
  const infoRows = rows.map(stringifyRow);
  const infoText = infoRows.join('\n');
  return infoText;
};

const copyExtensionInfo = async state => {
  const item = getFocusedItem(state);
  if (!item) {
    return state;
  }
  const text = getExtensionInfoText(item);
  await writeClipBoardText(text);
  return state;
};

const {
  diff,
  dispose: dispose$1,
  get,
  getCommandIds,
  registerCommands,
  set: set$1,
  wrapCommand,
  wrapGetter
} = create$1();

const create = (id, uri, x, y, width, height, platform, assetDir) => {
  const state = {
    allExtensions: [],
    assetDir,
    deltaY: 0,
    finalDeltaY: 0,
    focus: 0,
    focused: false,
    focusedIndex: 0,
    handleOffset: 0,
    headerHeight: 41,
    height,
    initial: true,
    inputActions: [],
    inputSource: 0,
    itemHeight: 72,
    items: [],
    maxLineY: 0,
    message: '',
    minimumSliderSize: 0,
    minLineY: 0,
    negativeMargin: 0,
    placeholder: '',
    platform,
    scrollBarActive: false,
    scrollBarHeight: 0,
    scrollBarY: 0,
    searchValue: '',
    size: 0,
    suggestOpen: false,
    uid: id,
    width,
    x,
    y
  };
  set$1(id, state, state);
};

const isEqual$3 = (oldState, newState) => {
  return newState.scrollBarHeight === newState.scrollBarHeight && oldState.scrollBarY === newState.scrollBarY && oldState.initial === newState.initial && oldState.deltaY === newState.deltaY;
};

const isEqual$2 = (oldState, newState) => {
  if (!newState.focus) {
    return true;
  }
  return oldState.focus === newState.focus && oldState.inputSource === newState.inputSource;
};

const isEqual$1 = (oldState, newState) => {
  return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex && oldState.message === newState.message && oldState.focus === newState.focus && oldState.inputActions === newState.inputActions && oldState.placeholder === newState.placeholder;
};

const User = 1;
const Script = 2;

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

const RenderHeader = 1;
const RenderFocus = 2;
const RenderItems = 4;
const RenderScrollBar = 5;
const RenderMessage = 6;
const RenderSearchValue = 7;
const RenderFocusContext = 8;
const RenderIncremental = 9;
const RenderCss = 10;

const modules = [isEqual$1, isEqual$2, isEqual, isEqual$2, isEqual$3];
const numbers = [RenderIncremental, RenderFocus, RenderSearchValue, RenderFocusContext, RenderCss];

const diff2 = uid => {
  return diff(uid, modules, numbers);
};

const dispose = uid => {
  dispose$1(uid);
};

// TODO optimize this function to return the minimum number
// of visible items needed, e.g. when not scrolled 5 items with
// 20px fill 100px but when scrolled 6 items are needed
const getNumberOfVisibleItems = (listHeight, itemHeight) => {
  return Math.ceil(listHeight / itemHeight) + 1;
};
const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
  const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
  return scrollBarOffset;
};
const getScrollBarY = getScrollBarOffset;
const focusIndexScrollDown = (state, index, listHeight, itemHeight, itemsLength) => {
  const {
    finalDeltaY,
    height,
    headerHeight,
    scrollBarHeight
  } = state;
  const newMaxLineY = Math.min(index + 1, itemsLength);
  const fittingItems = getNumberOfVisibleItems(listHeight, itemHeight);
  const newMinLineY = Math.max(newMaxLineY - fittingItems, 0);
  const newDeltaY = itemsLength < fittingItems ? 0 : newMinLineY * itemHeight - listHeight % itemHeight + itemHeight;
  const scrollBarY = getScrollBarY(newDeltaY, finalDeltaY, height - headerHeight, scrollBarHeight);
  return {
    ...state,
    focusedIndex: index,
    listFocusedIndex: index,
    minLineY: newMinLineY,
    maxLineY: newMaxLineY,
    focused: true,
    deltaY: newDeltaY,
    scrollBarY
  };
};
const focusIndexScrollUp = (state, index, listHeight, itemHeight, itemsLength) => {
  const {
    finalDeltaY,
    height,
    headerHeight,
    scrollBarHeight
  } = state;
  const newMinLineY = index;
  const fittingItems = getNumberOfVisibleItems(listHeight, itemHeight);
  const newMaxLineY = Math.min(newMinLineY + fittingItems, itemsLength);
  const newDeltaY = newMinLineY * itemHeight;
  const scrollBarY = getScrollBarY(newDeltaY, finalDeltaY, height - headerHeight, scrollBarHeight);
  return {
    ...state,
    focusedIndex: index,
    listFocusedIndex: index,
    minLineY: newMinLineY,
    maxLineY: newMaxLineY,
    focused: true,
    deltaY: newDeltaY,
    scrollBarY
  };
};
const focusIndex = (state, index) => {
  const {
    itemHeight,
    minLineY,
    maxLineY,
    headerHeight,
    height,
    items
  } = state;
  const itemsLength = items.length;
  if (itemsLength === 0) {
    return state;
  }
  number(itemHeight);
  if (index === -1) {
    return {
      ...state,
      focusedIndex: -1,
      listFocusedIndex: -1,
      focused: true
    };
  }
  const listHeight = height - headerHeight;
  if (index < minLineY + 1) {
    return focusIndexScrollUp(state, index, listHeight, itemHeight, itemsLength);
  }
  if (index >= maxLineY - 1) {
    return focusIndexScrollDown(state, index, listHeight, itemHeight, itemsLength);
  }
  return {
    ...state,
    focusedIndex: index,
    listFocusedIndex: index,
    focused: true
  };
};
const first = () => {
  return 0;
};
const last = items => {
  return items.length - 1;
};
const next = (items, index) => {
  return (index + 1) % items.length;
};
const previous = (items, index) => {
  return index === 0 ? items.length - 1 : index - 1;
};
const focusFirst = state => {
  const firstIndex = first();
  return focusIndex(state, firstIndex);
};
const focusLast = state => {
  const {
    items
  } = state;
  const lastIndex = last(items);
  return focusIndex(state, lastIndex);
};
const focusNext = state => {
  const {
    focusedIndex,
    items
  } = state;
  const nextIndex = next(items, focusedIndex);
  return focusIndex(state, nextIndex);
};

// based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/common/arrays.ts#L625 (License MIT)

const isLastIndex = (array, index) => {
  return index === array.length - 1;
};
const lastIndex = array => {
  return array.length - 1;
};
const focusNextPage = state => {
  const {
    focusedIndex,
    items,
    maxLineY,
    minLineY
  } = state;
  if (isLastIndex(items, focusedIndex)) {
    return state;
  }
  const indexNextPage = Math.min(maxLineY + (maxLineY - minLineY) - 2, lastIndex(items));
  return focusIndex(state, indexNextPage);
};
const focusPrevious = state => {
  const {
    focusedIndex,
    items
  } = state;
  if (focusedIndex === 0 || focusedIndex === -1) {
    return state;
  }
  const previousIndex = previous(items, focusedIndex);
  return focusIndex(state, previousIndex);
};
const focusPreviousPage = state => {
  const {
    focusedIndex,
    minLineY,
    maxLineY
  } = state;
  if (focusedIndex === 0 || focusedIndex === -1) {
    return state;
  }
  const indexPreviousPage = Math.max(minLineY - (maxLineY - minLineY) + 1, 0);
  return focusIndex(state, indexPreviousPage);
};

const getActions = () => {
  return [{
    command: '',
    icon: Refresh,
    id: refresh(),
    type: Button
  }, {
    command: '',
    icon: Ellipsis,
    id: viewsAndMoreActions(),
    type: Button
  }];
};

const Enter = 3;
const Space = 9;
const PageUp = 10;
const PageDown = 11;
const End = 255;
const Home = 12;
const UpArrow = 14;
const DownArrow = 16;

const CtrlCmd = 1 << 11 >>> 0;

const FocusExtensions = 15;
const FocusExtensionsInput = 7000;

const getKeyBindings = () => {
  return [{
    command: 'Extensions.focusFirst',
    key: Home,
    when: FocusExtensions
  }, {
    command: 'Extensions.focusLast',
    key: End,
    when: FocusExtensions
  }, {
    command: 'Extensions.focusPreviousPage',
    key: PageUp,
    when: FocusExtensions
  }, {
    command: 'Extensions.focusNextPage',
    key: PageDown,
    when: FocusExtensions
  }, {
    command: 'Extensions.focusPrevious',
    key: UpArrow,
    when: FocusExtensions
  }, {
    command: 'Extensions.focusNext',
    key: DownArrow,
    when: FocusExtensions
  }, {
    command: 'Extensions.handleClickCurrentButKeepFocus',
    key: Space,
    when: FocusExtensions
  }, {
    command: 'Extensions.handleClickCurrent',
    key: Enter,
    when: FocusExtensions
  }, {
    command: 'Extensions.toggleSuggest',
    key: CtrlCmd | Space,
    when: FocusExtensions
  }, {
    command: 'Extensions.scrollDown',
    key: CtrlCmd | DownArrow,
    when: FocusExtensions
  }];
};

const Separator = 1;
const None$1 = 0;
const SubMenu = 4;

const getMenuEntries = () => {
  return [{
    command: 'SearchExtensions.enable',
    flags: None$1,
    id: 'enable',
    label: enable()
  }, {
    command: 'SearchExtensions.enableWorkspace',
    flags: None$1,
    id: 'enableWorkspace',
    label: enableWorkspace()
  }, {
    command: '',
    flags: Separator,
    id: 'separator1',
    label: ''
  }, {
    command: 'SearchExtensions.disable',
    flags: None$1,
    id: 'disable',
    label: disable()
  }, {
    command: 'SearchExtensions.disableWorkspace',
    flags: None$1,
    id: 'disableWorkspace',
    label: disableWorkspace()
  }, {
    command: '',
    flags: Separator,
    id: 'separator2',
    label: ''
  }, {
    command: 'SearchExtensions.installAnotherVersion',
    flags: None$1,
    id: 'installAnotherVersion',
    label: installAnotherVersion()
  }, {
    command: 'Extensions.copyExtensionInfo',
    flags: None$1,
    id: 'copy',
    label: copy()
  }, {
    command: 'Extensions.copyExtensionId',
    flags: None$1,
    id: 'copyExtensionId',
    label: copyExtensionId$1()
  }];
};

const getMenuEntriesFilter = () => {
  return [{
    command: 'SearchExtensions.filterByFeatured',
    flags: None$1,
    id: 'filterByFeatured',
    label: featured()
  }, {
    command: 'SearchExtensions.filterByMcpServers',
    flags: None$1,
    id: 'filterByMcpServers',
    label: mcpServers()
  }, {
    command: 'SearchExtensions.filterByMostPopular',
    flags: None$1,
    id: 'filterByMostPopular',
    label: mostPopular()
  }, {
    command: 'SearchExtensions.filterByRecentlyPublished',
    flags: None$1,
    id: 'filterByRecentlyPublished',
    label: recentlyPublished()
  }, {
    command: 'SearchExtensions.filterByRecommended',
    flags: None$1,
    id: 'filterByRecommended',
    label: recommended()
  }, {
    command: '',
    flags: Separator,
    id: 'separator1',
    label: ''
  }, {
    command: 'SearchExtensions.filterByCategory',
    flags: SubMenu,
    id: 'filterByCategory',
    label: category()
  }, {
    command: 'SearchExtensions.filterByInstalled',
    flags: None$1,
    id: 'filterByInstalled',
    label: installed()
  }, {
    command: 'SearchExtensions.filterByUpdates',
    flags: None$1,
    id: 'filterByUpdates',
    label: updates()
  }, {
    command: 'SearchExtensions.filterByBuiltin',
    flags: None$1,
    id: 'filterByBuiltin',
    label: builtIn()
  }, {
    command: 'SearchExtensions.filterByEnabled',
    flags: None$1,
    id: 'filterByEnabled',
    label: enabled()
  }, {
    command: 'SearchExtensions.filterByDisabled',
    flags: None$1,
    id: 'filterByDisabled',
    label: disabled()
  }, {
    command: 'SearchExtensions.filterByWorkspaceUnsupported',
    flags: None$1,
    id: 'filterByWorkspaceUnsupported',
    label: workspaceUnsupported()
  }, {
    command: '',
    flags: Separator,
    id: 'separator2',
    label: ''
  }, {
    command: 'SearchExtensions.filterBySortBy',
    flags: SubMenu,
    id: 'filterBySortBy',
    label: sortBy()
  }];
};

const getMenuEntries2 = (state, props) => {
  const {
    menuId
  } = props;
  switch (menuId) {
    case ExtensionSearchFilter:
      return getMenuEntriesFilter();
    default:
      return getMenuEntries();
  }
};

const getMenuIds = () => {
  return [ManageExtension, ExtensionSearchFilter];
};

const handleBlur = state => {
  return {
    ...state,
    focus: None$2
  };
};

const getExtensionDetailUri = extensionId => {
  return `extension-detail://${extensionId}`;
};

const openUri = async uri => {
  return openUri$1(uri);
};

const handleClick = async (state, index) => {
  const {
    items,
    minLineY
  } = state;
  const actualIndex = index + minLineY;
  if (actualIndex < 0 || actualIndex >= items.length) {
    return {
      ...state,
      focus: List$2,
      focusedIndex: -1
    };
  }
  const extension = items[actualIndex];
  const uri = getExtensionDetailUri(extension.id);
  await openUri(uri);
  const partialNewState = focusIndex(state, actualIndex);
  const newState = {
    ...partialNewState,
    focus: List$2
  };
  return newState;
};

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

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

const SetText = 1;
const Replace = 2;
const SetAttribute = 3;
const RemoveAttribute = 4;
const Add = 6;
const NavigateChild = 7;
const NavigateParent = 8;
const RemoveChild = 9;
const NavigateSibling = 10;

const isKey = key => {
  return key !== 'type' && key !== 'childCount';
};

const getKeys = node => {
  const keys = Object.keys(node).filter(isKey);
  return keys;
};

const arrayToTree = nodes => {
  const result = [];
  let i = 0;
  while (i < nodes.length) {
    const node = nodes[i];
    const {
      children,
      nodesConsumed
    } = getChildrenWithCount(nodes, i + 1, node.childCount || 0);
    result.push({
      node,
      children
    });
    i += 1 + nodesConsumed;
  }
  return result;
};
const getChildrenWithCount = (nodes, startIndex, childCount) => {
  if (childCount === 0) {
    return {
      children: [],
      nodesConsumed: 0
    };
  }
  const children = [];
  let i = startIndex;
  let remaining = childCount;
  let totalConsumed = 0;
  while (remaining > 0 && i < nodes.length) {
    const node = nodes[i];
    const nodeChildCount = node.childCount || 0;
    const {
      children: nodeChildren,
      nodesConsumed
    } = getChildrenWithCount(nodes, i + 1, nodeChildCount);
    children.push({
      node,
      children: nodeChildren
    });
    const nodeSize = 1 + nodesConsumed;
    i += nodeSize;
    totalConsumed += nodeSize;
    remaining--;
  }
  return {
    children,
    nodesConsumed: totalConsumed
  };
};

const compareNodes = (oldNode, newNode) => {
  const patches = [];
  // Check if node type changed - return null to signal incompatible nodes
  // (caller should handle this with a Replace operation)
  if (oldNode.type !== newNode.type) {
    return null;
  }
  // Handle text nodes
  if (oldNode.type === Text && newNode.type === Text) {
    if (oldNode.text !== newNode.text) {
      patches.push({
        type: SetText,
        value: newNode.text
      });
    }
    return patches;
  }
  // Compare attributes
  const oldKeys = getKeys(oldNode);
  const newKeys = getKeys(newNode);
  // Check for attribute changes
  for (const key of newKeys) {
    if (oldNode[key] !== newNode[key]) {
      patches.push({
        type: SetAttribute,
        key,
        value: newNode[key]
      });
    }
  }
  // Check for removed attributes
  for (const key of oldKeys) {
    if (!(key in newNode)) {
      patches.push({
        type: RemoveAttribute,
        key
      });
    }
  }
  return patches;
};

const treeToArray = node => {
  const result = [node.node];
  for (const child of node.children) {
    result.push(...treeToArray(child));
  }
  return result;
};

const diffChildren = (oldChildren, newChildren, patches) => {
  const maxLength = Math.max(oldChildren.length, newChildren.length);
  // Track where we are: -1 means at parent, >= 0 means at child index
  let currentChildIndex = -1;
  // Collect indices of children to remove (we'll add these patches at the end in reverse order)
  const indicesToRemove = [];
  for (let i = 0; i < maxLength; i++) {
    const oldNode = oldChildren[i];
    const newNode = newChildren[i];
    if (!oldNode && !newNode) {
      continue;
    }
    if (!oldNode) {
      // Add new node - we should be at the parent
      if (currentChildIndex >= 0) {
        // Navigate back to parent
        patches.push({
          type: NavigateParent
        });
        currentChildIndex = -1;
      }
      // Flatten the entire subtree so renderInternal can handle it
      const flatNodes = treeToArray(newNode);
      patches.push({
        type: Add,
        nodes: flatNodes
      });
    } else if (newNode) {
      // Compare nodes to see if we need any patches
      const nodePatches = compareNodes(oldNode.node, newNode.node);
      // If nodePatches is null, the node types are incompatible - need to replace
      if (nodePatches === null) {
        // Navigate to this child
        if (currentChildIndex === -1) {
          patches.push({
            type: NavigateChild,
            index: i
          });
          currentChildIndex = i;
        } else if (currentChildIndex !== i) {
          patches.push({
            type: NavigateSibling,
            index: i
          });
          currentChildIndex = i;
        }
        // Replace the entire subtree
        const flatNodes = treeToArray(newNode);
        patches.push({
          type: Replace,
          nodes: flatNodes
        });
        // After replace, we're at the new element (same position)
        continue;
      }
      // Check if we need to recurse into children
      const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
      // Only navigate to this element if we need to do something
      if (nodePatches.length > 0 || hasChildrenToCompare) {
        // Navigate to this child if not already there
        if (currentChildIndex === -1) {
          patches.push({
            type: NavigateChild,
            index: i
          });
          currentChildIndex = i;
        } else if (currentChildIndex !== i) {
          patches.push({
            type: NavigateSibling,
            index: i
          });
          currentChildIndex = i;
        }
        // Apply node patches (these apply to the current element, not children)
        if (nodePatches.length > 0) {
          patches.push(...nodePatches);
        }
        // Compare children recursively
        if (hasChildrenToCompare) {
          diffChildren(oldNode.children, newNode.children, patches);
        }
      }
    } else {
      // Remove old node - collect the index for later removal
      indicesToRemove.push(i);
    }
  }
  // Navigate back to parent if we ended at a child
  if (currentChildIndex >= 0) {
    patches.push({
      type: NavigateParent
    });
    currentChildIndex = -1;
  }
  // Add remove patches in reverse order (highest index first)
  // This ensures indices remain valid as we remove
  for (let j = indicesToRemove.length - 1; j >= 0; j--) {
    patches.push({
      type: RemoveChild,
      index: indicesToRemove[j]
    });
  }
};
const diffTrees = (oldTree, newTree, patches, path) => {
  // At the root level (path.length === 0), we're already AT the element
  // So we compare the root node directly, then compare its children
  if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
    const oldNode = oldTree[0];
    const newNode = newTree[0];
    // Compare root nodes
    const nodePatches = compareNodes(oldNode.node, newNode.node);
    // If nodePatches is null, the root node types are incompatible - need to replace
    if (nodePatches === null) {
      const flatNodes = treeToArray(newNode);
      patches.push({
        type: Replace,
        nodes: flatNodes
      });
      return;
    }
    if (nodePatches.length > 0) {
      patches.push(...nodePatches);
    }
    // Compare children
    if (oldNode.children.length > 0 || newNode.children.length > 0) {
      diffChildren(oldNode.children, newNode.children, patches);
    }
  } else {
    // Non-root level or multiple root elements - use the regular comparison
    diffChildren(oldTree, newTree, patches);
  }
};

const removeTrailingNavigationPatches = patches => {
  // Find the last non-navigation patch
  let lastNonNavigationIndex = -1;
  for (let i = patches.length - 1; i >= 0; i--) {
    const patch = patches[i];
    if (patch.type !== NavigateChild && patch.type !== NavigateParent && patch.type !== NavigateSibling) {
      lastNonNavigationIndex = i;
      break;
    }
  }
  // Return patches up to and including the last non-navigation patch
  return lastNonNavigationIndex === -1 ? [] : patches.slice(0, lastNonNavigationIndex + 1);
};

const diffTree = (oldNodes, newNodes) => {
  // Step 1: Convert flat arrays to tree structures
  const oldTree = arrayToTree(oldNodes);
  const newTree = arrayToTree(newNodes);
  // Step 3: Compare the trees
  const patches = [];
  diffTrees(oldTree, newTree, patches, []);
  // Remove trailing navigation patches since they serve no purpose
  return removeTrailingNavigationPatches(patches);
};

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

const handleClickAt = async (state, button, eventX, eventY) => {
  if (button !== LeftClick) {
    return state;
  }
  const {
    deltaY,
    headerHeight,
    itemHeight,
    x,
    y
  } = state;
  const index = getListIndex(eventX, eventY, x, y, deltaY, itemHeight, headerHeight);
  return handleClick(state, index);
};

const handleClickCurrent = state => {
  const {
    focusedIndex
  } = state;
  return handleClick(state, focusedIndex);
};

const handleClickCurrentButKeepFocus = state => {
  const {
    focusedIndex
  } = state;
  return handleClick(state, focusedIndex);
};

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

const handleClickFilter = async state => {
  const {
    headerHeight,
    uid,
    width,
    x,
    y
  } = state;
  const menuX = x + width + 80;
  const menuY = y + headerHeight + 100;
  await show2(uid, ExtensionSearchFilter, menuX, menuY, {
    menuId: ExtensionSearchFilter
  });
  return state;
};

const handleContextMenu = async (state, button, eventX, eventY) => {
  // TODO use focused index when when context menu button is -1 (keyboard)
  const {
    deltaY,
    headerHeight,
    itemHeight,
    items,
    uid,
    x,
    y
  } = state;
  const index = getListIndex(eventX, eventY, x, y, deltaY, itemHeight, headerHeight);
  if (index < 0 || index > items.length) {
    return state;
  }
  await show2(uid, ManageExtension, eventX, eventY, {
    menuId: ManageExtension
  });
  return state;
};

const handleDisable = (state, id) => {
  return state;
  // TODO
};

const handleDisableWorkspace = (state, id) => {
  return state;
};

const handleEnable = (state, id) => {
  return state;
};

const handleEnableWorkspace = (state, id) => {
  return state;
};

const handleFocus = async state => {
  return {
    ...state,
    focus: List$2
  };
};

const handleHeaderContextMenu = async state => {
  return state;
};

const handleInput = async (state, value, inputSource = User) => {
  return handleChange(state, {
    inputSource,
    searchValue: value
  });
};

// TODO show error / warning  when installment fails / times out
const handleInstall = async (state, id) => {
  return state;
  // TODO
};

const handleScrollBarCaptureLost = state => {
  return {
    ...state,
    scrollBarActive: false
  };
};

const getNewDeltaPercent = (height, scrollBarHeight, relativeY) => {
  const halfScrollBarHeight = scrollBarHeight / 2;
  if (relativeY <= halfScrollBarHeight) {
    // clicked at top
    return {
      handleOffset: relativeY,
      percent: 0
    };
  }
  if (relativeY <= height - halfScrollBarHeight) {
    // clicked in middle
    return {
      handleOffset: halfScrollBarHeight,
      percent: (relativeY - halfScrollBarHeight) / (height - scrollBarHeight)
    };
  }
  // clicked at bottom
  return {
    handleOffset: scrollBarHeight - height + relativeY,
    percent: 1
  };
};

const clamp = (num, min, max) => {
  number(num);
  number(min);
  number(max);
  return Math.min(Math.max(num, min), max);
};

const setDeltaY = (state, value) => {
  object(state);
  number(value);
  const {
    deltaY,
    finalDeltaY,
    headerHeight,
    height,
    itemHeight,
    items,
    minimumSliderSize
  } = state;
  const listHeight = height - headerHeight;
  const newDeltaY = clamp(value, 0, finalDeltaY);
  if (deltaY === newDeltaY) {
    return state;
  }
  // TODO when it only moves by one px, extensions don't need to be rerendered, only negative margin
  const minLineY = Math.floor(newDeltaY / itemHeight);
  const total = items.length;
  const maxLineY = Math.min(minLineY + getNumberOfVisibleItems$1(listHeight, itemHeight), total);
  const contentHeight = total * itemHeight;
  const scrollBarHeight = getScrollBarSize(listHeight, contentHeight, minimumSliderSize);
  const scrollBarY = getScrollBarY$1(newDeltaY, finalDeltaY, height - headerHeight, scrollBarHeight);
  return {
    ...state,
    deltaY: newDeltaY,
    maxLineY,
    minLineY,
    scrollBarY
  };
};

const handleScrollBarClick = (state, eventY) => {
  // TODO move this to list
  const {
    deltaY,
    finalDeltaY,
    headerHeight,
    height,
    scrollBarHeight,
    y
  } = state;
  const contentHeight = height - headerHeight;
  const relativeY = eventY - y - headerHeight;
  const currentScrollBarY = getScrollBarY$1(deltaY, finalDeltaY, contentHeight, scrollBarHeight);
  const diff = relativeY - currentScrollBarY;
  if (diff >= 0 && diff < scrollBarHeight) {
    return {
      ...state,
      handleOffset: diff,
      scrollBarActive: true
    };
  }
  const {
    handleOffset,
    percent
  } = getNewDeltaPercent(contentHeight, scrollBarHeight, relativeY);
  const newDeltaY = percent * finalDeltaY;
  return {
    ...setDeltaY(state, newDeltaY),
    handleOffset,
    scrollBarActive: true
  };
};

const getNewPercent = (contentHeight, scrollBarHeight, relativeY) => {
  if (relativeY <= contentHeight - scrollBarHeight / 2) {
    // clicked in middle
    return relativeY / (contentHeight - scrollBarHeight);
  }
  // clicked at bottom
  return 1;
};
const handleScrollBarMove = (state, eventY) => {
  const {
    finalDeltaY,
    handleOffset,
    headerHeight,
    height,
    scrollBarActive,
    scrollBarHeight,
    y
  } = state;
  if (!scrollBarActive) {
    return state;
  }
  const relativeY = eventY - y - headerHeight - handleOffset;
  const contentHeight = height - headerHeight;
  const newPercent = getNewPercent(contentHeight, scrollBarHeight, relativeY);
  const newDeltaY = newPercent * finalDeltaY;
  return setDeltaY(state, newDeltaY);
};

// TODO show error / warning  when installment fails / times out
const handleUninstall = async (state, id) => {
  return state;
};

const handleWheel = (state, deltaMode, deltaY) => {
  number(deltaMode);
  number(deltaY);
  return setDeltaY(state, state.deltaY + deltaY);
};

const sendMessagePortToExtensionHostWorker = async port => {
  await sendMessagePortToExtensionHostWorker$1(port, 0);
};

const createExtensionHostWorkerRpc = async () => {
  try {
    const rpc = await TransferMessagePortRpcParent.create({
      commandMap: {},
      send: sendMessagePortToExtensionHostWorker
    });
    return rpc;
  } catch (error) {
    throw new VError(error, `Failed to create extension host rpc`);
  }
};

const {
  invoke,
  set
} = ExtensionHost;

const initializeExtensionHostWorker = async () => {
  const rpc = await createExtensionHostWorkerRpc();
  set(rpc);
};

const createExtensionManagementWorkerRpc = async () => {
  try {
    const rpc = await TransferMessagePortRpcParent.create({
      commandMap: {},
      send: port => sendMessagePortToExtensionManagementWorker(port, 0)
    });
    return rpc;
  } catch (error) {
    throw new VError(error, `Failed to create extension management rpc`);
  }
};

const initializeExtensionManagementWorker = async () => {
  try {
    const rpc = await createExtensionManagementWorkerRpc();
    set$3(rpc);
  } catch {
    // ignore
  }
};

const initialize = async () => {
  await Promise.all([initializeExtensionHostWorker(), initializeExtensionManagementWorker()]);
};

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

const getAllExtensions$1 = async platform => {
  if (platform === Web) {
    try {
      const extensions = await invoke('Extensions.getExtensions');
      return extensions;
    } catch {
      return [];
    }
  }
  // @ts-ignore todo
  return invoke$1('ExtensionManagement.getAllExtensions');
};

const getAllExtensions = platform => {
  return getAllExtensions$1(platform);
};

const Small = 1;
const Normal = 2;
const Large = 3;

const getViewletSize = width => {
  if (width < 180) {
    return Small;
  }
  if (width < 768) {
    return Normal;
  }
  return Large;
};

const getRemoteUrl = (extension, platform, assetDir) => {
  if (platform === Remote || platform === Electron) {
    if (extension.builtin) {
      return `${assetDir}/extensions/${extension.id}/${extension.icon}`;
    }
    return `/remote/${extension.path}/${extension.icon}`; // TODO support windows paths
  }
  if (platform === Web) {
    return `${extension.path}/${extension.icon}`;
  }
  return '';
};

const getExtensionDefaultIcon = assetDir => {
  return `${assetDir}/icons/extensionDefaultIcon.png`;
};
const getExtensionLanguageBasicsIcon = assetDir => {
  return `${assetDir}/icons/language-icon.svg`;
};
const getExtensionThemeIcon = assetDir => {
  return `${assetDir}/icons/theme-icon.png`;
};

const isLanguageBasicsExtension = extension => {
  return extension.name && extension.name.startsWith('Language Basics');
};
const isThemeExtension = extension => {
  return extension.name && extension.name.endsWith(' Theme');
};
const getExtensionIcon = (extension, platform, assetDir) => {
  if (!extension) {
    return getExtensionDefaultIcon(assetDir);
  }
  if (!extension.path || !extension.icon) {
    if (isLanguageBasicsExtension(extension)) {
      return getExtensionLanguageBasicsIcon(assetDir);
    }
    if (isThemeExtension(extension)) {
      return getExtensionThemeIcon(assetDir);
    }
    return getExtensionDefaultIcon(assetDir);
  }
  return getRemoteUrl(extension, platform, assetDir);
};

const getIcon = (extension, platform, assetDir) => {
  return getExtensionIcon(extension, platform, assetDir);
};
const getName = extension => {
  if (extension && extension.name) {
    return extension.name;
  }
  if (extension && extension.id) {
    return extension.id;
  }
  return 'n/a';
};
const getDescription = extension => {
  if (!extension || !extension.description) {
    return 'n/a';
  }
  return extension.description;
};
const getId$1 = extension => {
  if (!extension || !extension.id) {
    return 'n/a';
  }
  return extension.id;
};
const isString = item => {
  return typeof item === 'string';
};
const getCategories = extension => {
  if (!extension || !extension.categories || !Array.isArray(extension.categories)) {
    return [];
  }
  return extension.categories.filter(isString);
};

const RE_PUBLISHER = /^[a-z\d-]+/;

// TODO handle case when extension is of type number|array|null|string
const getPublisher = extension => {
  if (!extension || !extension.id) {
    return 'n/a';
  }
  // TODO handle case when id is not of type string -> should not crash application
  const match = extension.id.match(RE_PUBLISHER);
  if (!match) {
    return 'n/a';
  }
  return match[0];
};

const normalizeExtension$1 = (extension, platform, assetDir) => {
  return {
    categories: getCategories(extension),
    description: getDescription(extension),
    icon: getIcon(extension, platform, assetDir),
    id: getId$1(extension),
    name: getName(extension),
    publisher: getPublisher(extension),
    uri: ''
  };
};

const normalizeExtension = (extensions, platform, assetDir) => {
  const normalized = [];
  for (const extension of extensions) {
    normalized.push(normalizeExtension$1(extension, platform, assetDir));
  }
  return normalized;
};

const getSavedValue = savedState => {
  if (savedState && typeof savedState === 'object' && 'searchValue' in savedState && typeof savedState.searchValue === 'string') {
    return savedState.searchValue;
  }
  return '';
};
const getSavedDeltaY = savedState => {
  if (savedState && typeof savedState === 'object' && 'deltaY' in savedState && typeof savedState.deltaY === 'number' && !Number.isNaN(savedState.deltaY)) {
    return savedState.deltaY;
  }
  return 0;
};

const restoreState = savedState => {
  const searchValue = getSavedValue(savedState);
  const savedDeltaY = getSavedDeltaY(savedState);
  return {
    deltaY: savedDeltaY,
    searchValue
  };
};

const loadContent = async (state, savedState) => {
  const {
    assetDir,
    platform,
    width
  } = state;
  const {
    deltaY,
    searchValue
  } = restoreState(savedState);
  // TODO just get local extensions on demand (not when query string is already different)
  const allExtensions = await getAllExtensions(platform);
  const size = getViewletSize(width);
  const normalized = normalizeExtension(allExtensions, platform, assetDir);
  const updatedState = await handleInput({
    ...state,
    allExtensions: normalized,
    deltaY,
    initial: false,
    inputSource: Script,
    size
  }, searchValue, Script);
  return updatedState;
};

const openSuggest = state => {
  return state;
};

const getCss = state => {
  const {
    deltaY,
    itemHeight,
    scrollBarHeight,
    scrollBarY
  } = state;
  const relative = -(deltaY % itemHeight);
  const roundedScrollBarY = Math.round(scrollBarY);
  return `.Extensions .ScrollBarThumb {
  height: ${scrollBarHeight}px;
  translate: 0 ${roundedScrollBarY}px;
}

.Extensions .ListItems {
  translate: 0 ${relative}px;

.ExtensionListItem {
  position: relative !important;
}
`;
};

const renderCss = newState => {
  const {
    uid
  } = newState;
  const css = getCss(newState);
  return [SetCss, uid, css];
};

const Extensions$1 = 'extensions';

const getSelector = focus => {
  switch (focus) {
    case Input:
      return `[name="${Extensions$1}"]`;
    case List$2:
      return '.ListItems';
    default:
      return '';
  }
};
const renderFocus = newState => {
  const {
    focus,
    uid
  } = newState;
  if (!focus) {
    return [];
  }
  const selector = getSelector(focus);
  return [FocusSelector, uid, selector];
};

const renderFocusContext = newState => {
  const {
    uid
  } = newState;
  if (newState.focus === Input) {
    return ['Viewlet.setFocusContext', uid, FocusExtensionsInput];
  }
  if (newState.focus === List$2) {
    return ['Viewlet.setFocusContext', uid, FocusExtensions];
  }
  return [];
};

const Actions = 'Actions';
const ExtensionActions = 'ExtensionActions';
const ExtensionActive = 'ExtensionActive';
const ExtensionHeader = 'ExtensionHeader';
const ExtensionListItem = 'ExtensionListItem';
const ExtensionListItemAuthorName = 'ExtensionListItemAuthorName';
const ExtensionListItemDescription = 'ExtensionListItemDescription';
const ExtensionListItemDetail = 'ExtensionListItemDetail';
const ExtensionListItemFooter = 'ExtensionListItemFooter';
const ExtensionListItemIcon = 'ExtensionListItemIcon';
const ExtensionListItemName = 'ExtensionListItemName';
const Extensions = 'Extensions';
const FocusOutline = 'FocusOutline';
const IconButton = 'IconButton';
const List$1 = 'List';
const ListItems = 'ListItems';
const MaskIcon = 'MaskIcon';
const MultilineInputBox = 'MultilineInputBox';
const NoExtensionsFoundMessage = 'NoExtensionsFoundMessage';
const ScrollBar = 'ScrollBar';
const ScrollBarSmall = 'ScrollBarSmall';
const ScrollBarThumb = 'ScrollBarThumb';
const ScrollBarThumbActive = 'ScrollBarThumbActive';
const SearchField = 'SearchField';
const SearchFieldButton = 'SearchFieldButton';
const SearchFieldButtonDisabled = 'SearchFieldButtonDisabled';
const SearchFieldButtons = 'SearchFieldButtons';
const SearchFieldContainer = 'SearchFieldContainer';
const Viewlet = 'Viewlet';

const List = 'list';
const ListItem = 'listitem';
const None = 'none';
const ToolBar = 'toolbar';

const disabledClassName = mergeClassNames(SearchFieldButton, SearchFieldButtonDisabled);
const getClassName$1 = enabled => {
  if (enabled) {
    return SearchFieldButton;
  }
  return disabledClassName;
};
const getSearchFieldButtonVirtualDom = button => {
  const {
    enabled,
    icon,
    onClick,
    title
  } = button;
  return [{
    childCount: 1,
    className: getClassName$1(enabled),
    onClick,
    tabIndex: 0,
    title,
    type: Button$2
  }, {
    childCount: 0,
    className: mergeClassNames(MaskIcon, icon),
    type: Div
  }];
};

const getSearchFieldVirtualDom = (name, placeholder, onInput, insideButtons, outsideButtons, onFocus = '') => {
  // TODO avoid mutation
  const dom = [{
    childCount: 2,
    className: SearchField,
    role: None,
    type: Div
  }, {
    autocapitalize: 'off',
    autocomplete: 'off',
    autocorrect: 'off',
    childCount: 0,
    className: MultilineInputBox,
    inputType: 'search',
    name,
    onFocus,
    onInput,
    placeholder,
    spellcheck: false,
    type: Input$1
  }, {
    childCount: insideButtons.length,
    className: SearchFieldButtons,
    type: Div
  }, ...insideButtons.flatMap(getSearchFieldButtonVirtualDom)];
  if (outsideButtons.length > 0) {
    dom.unshift({
      childCount: 1 + outsideButtons.length,
      className: SearchFieldContainer,
      role: None,
      type: Div
    });
    dom.push(...outsideButtons.flatMap(getSearchFieldButtonVirtualDom));
  }
  return dom;
};

const parentNode = {
  childCount: 1,
  className: ExtensionHeader,
  onContextMenu: HandleHeaderContextMenu,
  type: Div
};
const getExtensionHeaderVirtualDom = (placeholder, actions) => {
  return [parentNode, ...getSearchFieldVirtualDom(Extensions$1, placeholder, HandleExtensionsInput, actions, [])];
};

const renderHeader = newState => {
  const actions = getInputActions(newState.searchValue.length > 0);
  const dom = getExtensionHeaderVirtualDom(newState.placeholder, actions);
  return ['setHeaderDom', dom];
};

const Extension = 'Extension';

const listItemDetail = {
  childCount: 3,
  className: ExtensionListItemDetail,
  type: Div
};
const listItemName = {
  childCount: 1,
  className: ExtensionListItemName,
  type: Div
};
const listItemDescription = {
  childCount: 1,
  className: ExtensionListItemDescription,
  type: Div
};
const listItemFooter = {
  childCount: 2,
  className: ExtensionListItemFooter,
  type: Div
};
const listItemAuthorName = {
  childCount: 1,
  className: ExtensionListItemAuthorName,
  type: Div
};
const getClassName = focused => {
  if (focused) {
    return mergeClassNames(ExtensionListItem, ExtensionActive);
  }
  return ExtensionListItem;
};
const getId = focused => {
  if (focused) {
    return `ExtensionActive`;
  }
  return undefined;
};
const getExtensionListItemVirtualDom = extension => {
  const {
    description,
    focused,
    icon,
    name,
    posInSet,
    publisher,
    setSize
  } = extension;
  const dom = [{
    ariaPosInSet: posInSet,
    ariaRoleDescription: Extension,
    ariaSetSize: setSize,
    childCount: 2,
    className: getClassName(focused),
    id: getId(focused),
    role: ListItem,
    type: Div
  }, {
    childCount: 0,
    className: ExtensionListItemIcon,
    role: None,
    src: icon,
    type: Img
  }, listItemDetail, listItemName, text(name), listItemDescription, text(description), listItemFooter, listItemAuthorName, text(publisher), {
    childCount: 0,
    className: ExtensionActions,
    type: Div
  }];
  return dom;
};

const getExtensionsListVirtualDom = (visibleExtensions, focusOutline) => {
  const className = focusOutline ? mergeClassNames(ListItems, FocusOutline) : ListItems;
  const dom = [{
    ariaLabel: extensions(),
    childCount: visibleExtensions.length,
    className,
    onBlur: HandleBlur,
    onContextmenu: HandleContextMenu,
    onContextMenu: HandleContextMenu,
    onFocus: HandleFocus,
    onPointerDown: HandlePointerDown,
    onTouchEnd: HandleTouchEnd,
    onTouchMove: HandleTouchMove,
    onTouchStart: HandleTouchStart,
    onWheel: HandleWheel,
    role: List,
    tabIndex: 0,
    type: Div
  }, ...visibleExtensions.flatMap(getExtensionListItemVirtualDom)];
  return dom;
};

const getExtensionsVirtualDom = (visibleExtensions, focusOutline) => {
  const dom = getExtensionsListVirtualDom(visibleExtensions, focusOutline);
  // TODO
  return dom;
};

const getNoExtensionsFoundVirtualDom = message => {
  return [{
    childCount: 1,
    className: NoExtensionsFoundMessage,
    type: Div
  }, text(message)];
};

const getScrollBarVirtualDom = (scrollBarHeight, scrollBarTop) => {
  const shouldShowScrollbar = scrollBarHeight > 0;
  if (!shouldShowScrollbar) {
    return [];
  }
  // TODO move the dynamic css into a css adopted stylesheet
  // const heightString = Px.px(scrollBarHeight)
  // const translateString = Px.position(0, scrollBarTop)
  return [{
    childCount: 1,
    className: mergeClassNames(ScrollBar, ScrollBarSmall),
    onPointerDown: HandleScrollBarPointerDown,
    // TODO support pointercapture event
    type: Div
  }, {
    childCount: 0,
    className: ScrollBarThumb,
    // height: heightString,
    // translate: translateString,
    type: Div
  }];
};

const getVisibleItem = (item, setSize, itemHeight, minLineY, relative, i, focusedIndex) => {
  // TODO use normal parameters
  const {
    description,
    icon,
    id,
    name,
    publisher
  } = item;
  return {
    description,
    focused: i === focusedIndex,
    icon,
    id,
    name,
    posInSet: i + 1,
    publisher,
    setSize,
    top: (i - minLineY) * itemHeight - relative
  };
};

const getVisible = state => {
  const {
    deltaY,
    focusedIndex,
    itemHeight,
    items,
    maxLineY,
    minLineY
  } = state;
  const setSize = items.length;
  const visible = [];
  const relative = deltaY % itemHeight;
  for (let i = minLineY; i < maxLineY; i++) {
    const item = items[i];
    visible.push(getVisibleItem(item, setSize, itemHeight, minLineY, relative, i, focusedIndex));
  }
  return visible;
};

const getContentVirtualDom = (visibleExtensions, message, scrollBarHeight, scrollBarY, focusOutline) => {
  if (message) {
    return getNoExtensionsFoundVirtualDom(message);
  }
  return [{
    childCount: 2,
    className: mergeClassNames(Viewlet, List$1),
    type: Div
  }, ...getExtensionsVirtualDom(visibleExtensions, focusOutline), ...getScrollBarVirtualDom(scrollBarHeight)];
};
const getExtensionsViewVirtualDom = state => {
  const visibleExtensions = getVisible(state);
  const {
    focusedIndex,
    inputActions,
    message,
    placeholder,
    scrollBarHeight,
    scrollBarY
  } = state;
  const focusOutline = focusedIndex === -1 && state.focus === List$2;
  return [{
    ariaBusy: false,
    ariaLive: 'polite',
    childCount: 2,
    className: mergeClassNames(Viewlet, Extensions),
    role: None$3,
    type: Div
  }, ...getExtensionHeaderVirtualDom(placeholder, inputActions), ...getContentVirtualDom(visibleExtensions, message, scrollBarHeight, scrollBarY, focusOutline)];
};

const renderItems2 = newState => {
  const {
    initial,
    uid
  } = newState;
  if (initial) {
    return [SetDom2, uid, []];
  }
  const dom = getExtensionsViewVirtualDom(newState);
  return [SetDom2, uid, dom];
};

// TODO cache rendered dom so that it can be used for dom diffing
const renderIncremental = (newState, oldState) => {
  const oldDom = renderItems2(oldState)[2];
  const newDom = renderItems2(newState)[2];
  const patches = diffTree(oldDom, newDom);
  return [SetPatches, newState.uid, patches];
};

const SetMessage = 'setMessage';
const SetScrollBar = 'setScrollBar';

const renderMessage = newState => {
  return [/* method */SetMessage, /* message */newState.message];
};

const px = value => {
  return `${value}px`;
};
const position = (x, y) => {
  return `${x}px ${y}px`;
};

const renderScrollBar = newState => {
  const listHeight = getListHeight(newState.items.length, newState.itemHeight, newState.height);
  const total = newState.items.length;
  const contentHeight = total * newState.itemHeight;
  const scrollBarHeight = getScrollBarSize(listHeight, contentHeight, newState.minimumSliderSize);
  const scrollBarY = getScrollBarY$1(newState.deltaY, newState.finalDeltaY, newState.height - newState.headerHeight, scrollBarHeight);
  const roundedScrollBarY = Math.round(scrollBarY);
  const heightString = px(scrollBarHeight);
  const translateString = position(0, roundedScrollBarY);
  let className = ScrollBarThumb;
  if (newState.scrollBarActive) {
    className += ' ' + ScrollBarThumbActive;
  }
  return [/* method */SetScrollBar, translateString, heightString, className];
};

const renderSearchValue = newState => {
  return [/* method */'Viewlet.setValueByName', newState.uid, Extensions$1, newState.searchValue];
};

const getRenderer = diffType => {
  switch (diffType) {
    case RenderCss:
      return renderCss;
    case RenderFocus:
      return renderFocus;
    case RenderFocusContext:
      return renderFocusContext;
    case RenderHeader:
      return renderHeader;
    case RenderIncremental:
      return renderIncremental;
    case RenderItems:
      return renderItems2;
    case RenderMessage:
      return renderMessage;
    case RenderScrollBar:
      return renderScrollBar;
    case RenderSearchValue:
      return renderSearchValue;
    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(newState, oldState));
  }
  return commands;
};

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

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

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

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

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

const renderActions = uid => {
  const {
    newState,
    oldState
  } = get(uid);
  if (oldState === newState) {
    return [];
  }
  const actions = [];
  const dom = getActionsVirtualDom(actions);
  return dom;
};

const renderEventListeners = () => {
  return [{
    name: HandleContextMenu,
    params: ['handleContextMenu', Button$1, ClientX, ClientY],
    preventDefault: true
  }, {
    name: HandleHeaderContextMenu,
    params: ['handleHeaderContextMenu'],
    preventDefault: true
  }, {
    name: HandlePointerDown,
    params: ['handleClickAt', Button$1, ClientX, ClientY],
    preventDefault: true
  }, {
    name: HandleScrollBarPointerDown,
    params: ['handleScrollBarClick', ClientY],
    preventDefault: true,
    trackPointerEvents: [HandleScrollBarMove, HandleScrollBarPointerCaptureLost]
  }, {
    name: HandleScrollBarMove,
    params: ['handleScrollBarMove', ClientY]
  }, {
    name: HandleScrollBarPointerCaptureLost,
    params: ['handleScrollBarCaptureLost']
  }, {
    name: HandleWheel,
    params: ['handleWheel', DeltaMode, DeltaY],
    passive: true
  }, {
    name: HandleExtensionsInput,
    params: ['handleInput', TargetValue, User]
  }, {
    name: HandleClearSearchResults,
    params: ['clearSearchResults']
  }, {
    name: HandleClickFilter,
    params: ['handleClickFilter']
  }, {
    name: HandleFocus,
    params: ['handleFocus']
  }, {
    name: HandleBlur,
    params: ['handleBlur']
  }];
};

const resize = async (state, dimensions) => {
  const {
    headerHeight,
    itemHeight,
    items,
    minimumSliderSize
  } = state;
  const {
    height,
    width,
    x,
    y
  } = dimensions;
  const total = items.length;
  const listHeight = getListHeight(total, itemHeight, height);
  const contentHeight = total * itemHeight;
  const scrollBarHeight = getScrollBarSize(height, contentHeight, minimumSliderSize);
  const numberOfVisible = getNumberOfVisibleItems$1(listHeight, itemHeight);
  const maxLineY = Math.min(numberOfVisible, total);
  const finalDeltaY = getFinalDeltaY(listHeight, itemHeight, total);
  const scrollBarY = getScrollBarY$1(0, finalDeltaY, height - headerHeight, scrollBarHeight);
  return {
    ...state,
    finalDeltaY,
    height,
    maxLineY,
    scrollBarHeight,
    scrollBarY,
    width,
    x,
    y
  };
};

const saveState = state => {
  const {
    deltaY,
    searchValue
  } = state;
  return {
    deltaY,
    searchValue
  };
};

const scrollDown = state => {
  const {
    deltaY,
    finalDeltaY
  } = state;
  const newDeltaY = Math.min(deltaY + 20, finalDeltaY);
  return setDeltaY(state, newDeltaY);
};

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

const toggleSuggest = state => {
  return {
    ...state,
    suggestOpen: !state.suggestOpen
  };
};

const commandMap = {
  'SearchExtensions.clearSearchResults': wrapCommand(clearSearchResults),
  'SearchExtensions.closeSuggest': wrapCommand(closeSuggest),
  'SearchExtensions.copyExtensionId': wrapCommand(copyExtensionId),
  'SearchExtensions.copyExtensionInfo': wrapCommand(copyExtensionInfo),
  'SearchExtensions.create': create,
  'SearchExtensions.diff2': diff2,
  'SearchExtensions.disable': wrapCommand(handleDisable),
  'SearchExtensions.disableWorkspace': wrapCommand(handleDisableWorkspace),
  'SearchExtensions.dispose': dispose,
  'SearchExtensions.enable': wrapCommand(handleEnable),
  'SearchExtensions.enableWorkspace': wrapCommand(handleEnableWorkspace),
  'SearchExtensions.focusFirst': wrapCommand(focusFirst),
  'SearchExtensions.focusIndex': wrapCommand(focusIndex),
  'SearchExtensions.focusLast': wrapCommand(focusLast),
  'SearchExtensions.focusNext': wrapCommand(focusNext),
  'SearchExtensions.focusNextPage': wrapCommand(focusNextPage),
  'SearchExtensions.focusPrevious': wrapCommand(focusPrevious),
  'SearchExtensions.focusPreviousPage': wrapCommand(focusPreviousPage),
  'SearchExtensions.getActions': getActions,
  'SearchExtensions.getCommandIds': getCommandIds,
  'SearchExtensions.getKeyBindings': getKeyBindings,
  'SearchExtensions.getMenuEntries': getMenuEntries,
  'SearchExtensions.getMenuEntries2': wrapGetter(getMenuEntries2),
  'SearchExtensions.getMenuEntriesFilter': getMenuEntriesFilter,
  'SearchExtensions.getMenuIds': getMenuIds,
  'SearchExtensions.handleBlur': wrapCommand(handleBlur),
  'SearchExtensions.handleClick': wrapCommand(handleClick),
  'SearchExtensions.handleClickAt': wrapCommand(handleClickAt),
  'SearchExtensions.handleClickCurrent': wrapCommand(handleClickCurrent),
  'SearchExtensions.handleClickCurrentButKeepFocus': wrapCommand(handleClickCurrentButKeepFocus),
  'SearchExtensions.handleClickFilter': wrapCommand(handleClickFilter),
  'SearchExtensions.handleContextMenu': wrapCommand(handleContextMenu),
  'SearchExtensions.handleDisable': wrapCommand(handleDisable),
  'SearchExtensions.handleDisableWorkspace': wrapCommand(handleDisableWorkspace),
  'SearchExtensions.handleEnable': wrapCommand(handleEnable),
  'SearchExtensions.handleEnableWorkspace': wrapCommand(handleEnableWorkspace),
  'SearchExtensions.handleFocus': wrapCommand(handleFocus),
  'SearchExtensions.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
  'SearchExtensions.handleInput': wrapCommand(handleInput),
  'SearchExtensions.handleInstall': wrapCommand(handleInstall),
  'SearchExtensions.handleScrollBarCaptureLost': wrapCommand(handleScrollBarCaptureLost),
  'SearchExtensions.handleScrollBarClick': wrapCommand(handleScrollBarClick),
  'SearchExtensions.handleScrollBarMove': wrapCommand(handleScrollBarMove),
  'SearchExtensions.handleScrollBarThumbPointerMove': wrapCommand(handleScrollBarMove),
  'SearchExtensions.handleUninstall': wrapCommand(handleUninstall),
  'SearchExtensions.handleWheel': wrapCommand(handleWheel),
  'SearchExtensions.initialize': initialize,
  'SearchExtensions.loadContent': wrapCommand(loadContent),
  'SearchExtensions.openSuggest': wrapCommand(openSuggest),
  'SearchExtensions.render3': render3,
  'SearchExtensions.renderActions': renderActions,
  'SearchExtensions.renderEventListeners': renderEventListeners,
  'SearchExtensions.resize': wrapCommand(resize),
  'SearchExtensions.restoreState': restoreState,
  'SearchExtensions.saveState': wrapGetter(saveState),
  'SearchExtensions.scrollDown': wrapCommand(scrollDown),
  'SearchExtensions.searchExtensions': searchExtensions,
  'SearchExtensions.selectIndex': wrapCommand(selectIndex),
  'SearchExtensions.setDeltaY': wrapCommand(setDeltaY),
  'SearchExtensions.terminate': terminate,
  'SearchExtensions.toggleSuggest': wrapCommand(toggleSuggest)
};

const listen = async () => {
  registerCommands(commandMap);
  const rpc = await WebWorkerRpcClient.create({
    commandMap: commandMap
  });
  set$2(rpc);
};

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

main();
