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

const 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 IpcChildWithModuleWorker$1 = {
  __proto__: null,
  listen: listen$7,
  signal: signal$8,
  wrap: wrap$f
};
const addListener = (emitter, type, callback) => {
  if ('addEventListener' in emitter) {
    emitter.addEventListener(type, callback);
  } else {
    emitter.on(type, callback);
  }
};
const removeListener = (emitter, type, callback) => {
  if ('removeEventListener' in emitter) {
    emitter.removeEventListener(type, callback);
  } else {
    emitter.off(type, callback);
  }
};
const getFirstEvent = (eventEmitter, eventMap) => {
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  const listenerMap = Object.create(null);
  const cleanup = value => {
    for (const event of Object.keys(eventMap)) {
      removeListener(eventEmitter, event, listenerMap[event]);
    }
    resolve(value);
  };
  for (const [event, type] of Object.entries(eventMap)) {
    const listener = event => {
      cleanup({
        event,
        type
      });
    };
    addListener(eventEmitter, event, listener);
    listenerMap[event] = listener;
  }
  return promise;
};
const Message$1 = 3;
const create$5$1 = async ({
  isMessagePortOpen,
  messagePort
}) => {
  if (!isMessagePort(messagePort)) {
    throw new IpcError('port must be of type MessagePort');
  }
  if (isMessagePortOpen) {
    return messagePort;
  }
  const eventPromise = getFirstEvent(messagePort, {
    message: Message$1
  });
  messagePort.start();
  const {
    event,
    type
  } = await eventPromise;
  if (type !== Message$1) {
    throw new IpcError('Failed to wait for ipc message');
  }
  if (event.data !== readyMessage) {
    throw new IpcError('unexpected first message');
  }
  return messagePort;
};
const signal$1 = messagePort => {
  messagePort.start();
};
class IpcParentWithMessagePort extends Ipc {
  getData = getData$2;
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    this._rawIpc.close();
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
  onClose(callback) {}
}
const wrap$5 = messagePort => {
  return new IpcParentWithMessagePort(messagePort);
};
const IpcParentWithMessagePort$1 = {
  __proto__: null,
  create: create$5$1,
  signal: signal$1,
  wrap: wrap$5
};

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

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

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

const Two = '2.0';

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

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

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

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

const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
  const {
    id,
    promise
  } = registerPromise(callbacks);
  const message = create$6(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$7(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$3 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  const {
    port1,
    port2
  } = new MessageChannel();
  await send(port1);
  return create$4({
    commandMap,
    isMessagePortOpen,
    messagePort: port2
  });
};

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

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

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

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

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

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

const {
  invoke: invoke$2,
  set: set$1
} = create(6040);

const Electron = 2;

const OpenerWorker = 4561;
const RendererWorker = 1;

const {
  invoke: invoke$1,
  set
} = create(OpenerWorker);

const {
  invoke,
  invokeAndTransfer} = create(RendererWorker);
const sendMessagePortToOpenerWorker$1 = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToOpenerWorker', port, command, rpcId);
};

const sendMessagePortToAuthProcess = async port => {
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToSharedProcess', port, 'HandleMessagePortForAuthProcess.handleMessagePortForAuthProcess');
};
const initializeAuthProcess = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToAuthProcess
  });
  set$1(rpc);
};

const sendMessagePortToOpenerWorker = async port => {
  await sendMessagePortToOpenerWorker$1(port, 0);
};
const initializeOpenerWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToOpenerWorker
  });
  set(rpc);
};

const handleMessagePort = async (port, rpcId) => {
  const rpc = await create$4({
    commandMap: commandMap,
    isMessagePortOpen: true,
    messagePort: port
  });
  if (rpcId) {
    set$2(rpcId, rpc);
  }
};

let USER_AGENT;
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
  const NAME = 'oauth4webapi';
  const VERSION = 'v3.8.6';
  USER_AGENT = `${NAME}/${VERSION}`;
}
function looseInstanceOf(input, expected) {
  if (input == null) {
    return false;
  }
  try {
    return input instanceof expected || Object.getPrototypeOf(input)[Symbol.toStringTag] === expected.prototype[Symbol.toStringTag];
  } catch {
    return false;
  }
}
const ERR_INVALID_ARG_VALUE = 'ERR_INVALID_ARG_VALUE';
const ERR_INVALID_ARG_TYPE = 'ERR_INVALID_ARG_TYPE';
function CodedTypeError(message, code, cause) {
  const err = new TypeError(message, {
    cause
  });
  Object.assign(err, {
    code
  });
  return err;
}
const allowInsecureRequests = Symbol();
const clockSkew = Symbol();
const clockTolerance = Symbol();
const customFetch = Symbol();
const jweDecrypt = Symbol();
const encoder = new TextEncoder();
const decoder = new TextDecoder();
function buf(input) {
  if (typeof input === 'string') {
    return encoder.encode(input);
  }
  return decoder.decode(input);
}
let encodeBase64Url;
if (Uint8Array.prototype.toBase64) {
  encodeBase64Url = input => {
    if (input instanceof ArrayBuffer) {
      input = new Uint8Array(input);
    }
    return input.toBase64({
      alphabet: 'base64url',
      omitPadding: true
    });
  };
} else {
  const CHUNK_SIZE = 0x8000;
  encodeBase64Url = input => {
    if (input instanceof ArrayBuffer) {
      input = new Uint8Array(input);
    }
    const arr = [];
    for (let i = 0; i < input.byteLength; i += CHUNK_SIZE) {
      arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
    }
    return btoa(arr.join('')).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
  };
}
let decodeBase64Url;
if (Uint8Array.fromBase64) {
  decodeBase64Url = input => {
    try {
      return Uint8Array.fromBase64(input, {
        alphabet: 'base64url'
      });
    } catch (cause) {
      throw CodedTypeError('The input to be decoded is not correctly encoded.', ERR_INVALID_ARG_VALUE, cause);
    }
  };
} else {
  decodeBase64Url = input => {
    try {
      const binary = atob(input.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''));
      const bytes = new Uint8Array(binary.length);
      for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
      }
      return bytes;
    } catch (cause) {
      throw CodedTypeError('The input to be decoded is not correctly encoded.', ERR_INVALID_ARG_VALUE, cause);
    }
  };
}
function b64u(input) {
  if (typeof input === 'string') {
    return decodeBase64Url(input);
  }
  return encodeBase64Url(input);
}
class UnsupportedOperationError extends Error {
  code;
  constructor(message, options) {
    super(message, options);
    this.name = this.constructor.name;
    this.code = UNSUPPORTED_OPERATION;
    Error.captureStackTrace?.(this, this.constructor);
  }
}
class OperationProcessingError extends Error {
  code;
  constructor(message, options) {
    super(message, options);
    this.name = this.constructor.name;
    if (options?.code) {
      this.code = options?.code;
    }
    Error.captureStackTrace?.(this, this.constructor);
  }
}
function OPE(message, code, cause) {
  return new OperationProcessingError(message, {
    code,
    cause
  });
}
function isJsonObject(input) {
  if (input === null || typeof input !== 'object' || Array.isArray(input)) {
    return false;
  }
  return true;
}
function prepareHeaders(input) {
  if (looseInstanceOf(input, Headers)) {
    input = Object.fromEntries(input.entries());
  }
  const headers = new Headers(input ?? {});
  if (USER_AGENT && !headers.has('user-agent')) {
    headers.set('user-agent', USER_AGENT);
  }
  if (headers.has('authorization')) {
    throw CodedTypeError('"options.headers" must not include the "authorization" header name', ERR_INVALID_ARG_VALUE);
  }
  return headers;
}
function signal(url, value) {
  if (value !== undefined) {
    if (typeof value === 'function') {
      value = value(url.href);
    }
    if (!(value instanceof AbortSignal)) {
      throw CodedTypeError('"options.signal" must return or be an instance of AbortSignal', ERR_INVALID_ARG_TYPE);
    }
    return value;
  }
  return undefined;
}
function assertNumber(input, allow0, it, code, cause) {
  try {
    if (typeof input !== 'number' || !Number.isFinite(input)) {
      throw CodedTypeError(`${it} must be a number`, ERR_INVALID_ARG_TYPE, cause);
    }
    if (input > 0) return;
    if (allow0) {
      if (input !== 0) {
        throw CodedTypeError(`${it} must be a non-negative number`, ERR_INVALID_ARG_VALUE, cause);
      }
      return;
    }
    throw CodedTypeError(`${it} must be a positive number`, ERR_INVALID_ARG_VALUE, cause);
  } catch (err) {
    if (code) {
      throw OPE(err.message, code, cause);
    }
    throw err;
  }
}
function assertString(input, it, code, cause) {
  try {
    if (typeof input !== 'string') {
      throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
    }
    if (input.length === 0) {
      throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
    }
  } catch (err) {
    if (code) {
      throw OPE(err.message, code, cause);
    }
    throw err;
  }
}
function assertApplicationJson(response) {
  assertContentType(response, 'application/json');
}
function notJson(response, ...types) {
  let msg = '"response" content-type must be ';
  if (types.length > 2) {
    const last = types.pop();
    msg += `${types.join(', ')}, or ${last}`;
  } else if (types.length === 2) {
    msg += `${types[0]} or ${types[1]}`;
  } else {
    msg += types[0];
  }
  return OPE(msg, RESPONSE_IS_NOT_JSON, response);
}
function assertContentType(response, contentType) {
  if (getContentType(response) !== contentType) {
    throw notJson(response, contentType);
  }
}
function randomBytes() {
  return b64u(crypto.getRandomValues(new Uint8Array(32)));
}
function generateRandomCodeVerifier() {
  return randomBytes();
}
async function calculatePKCECodeChallenge(codeVerifier) {
  assertString(codeVerifier, 'codeVerifier');
  return b64u(await crypto.subtle.digest('SHA-256', buf(codeVerifier)));
}
function getClockSkew(client) {
  const skew = client?.[clockSkew];
  return typeof skew === 'number' && Number.isFinite(skew) ? skew : 0;
}
function getClockTolerance(client) {
  const tolerance = client?.[clockTolerance];
  return typeof tolerance === 'number' && Number.isFinite(tolerance) && Math.sign(tolerance) !== -1 ? tolerance : 30;
}
function epochTime() {
  return Math.floor(Date.now() / 1000);
}
function assertAs(as) {
  if (typeof as !== 'object' || as === null) {
    throw CodedTypeError('"as" must be an object', ERR_INVALID_ARG_TYPE);
  }
  assertString(as.issuer, '"as.issuer"');
}
function assertClient(client) {
  if (typeof client !== 'object' || client === null) {
    throw CodedTypeError('"client" must be an object', ERR_INVALID_ARG_TYPE);
  }
  assertString(client.client_id, '"client.client_id"');
}
function None() {
  return (_as, client, body, _headers) => {
    body.set('client_id', client.client_id);
  };
}
const URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
  try {
    return new URL(url, base);
  } catch {
    return null;
  }
};
function checkProtocol(url, enforceHttps) {
  if (enforceHttps && url.protocol !== 'https:') {
    throw OPE('only requests to HTTPS are allowed', HTTP_REQUEST_FORBIDDEN, url);
  }
  if (url.protocol !== 'https:' && url.protocol !== 'http:') {
    throw OPE('only HTTP and HTTPS requests are allowed', REQUEST_PROTOCOL_FORBIDDEN, url);
  }
}
function validateEndpoint(value, endpoint, useMtlsAlias, enforceHttps) {
  let url;
  if (typeof value !== 'string' || !(url = URLParse(value))) {
    throw OPE(`authorization server metadata does not contain a valid ${useMtlsAlias ? `"as.mtls_endpoint_aliases.${endpoint}"` : `"as.${endpoint}"`}`, value === undefined ? MISSING_SERVER_METADATA : INVALID_SERVER_METADATA, {
      attribute: useMtlsAlias ? `mtls_endpoint_aliases.${endpoint}` : endpoint
    });
  }
  checkProtocol(url, enforceHttps);
  return url;
}
function resolveEndpoint(as, endpoint, useMtlsAlias, enforceHttps) {
  if (useMtlsAlias && as.mtls_endpoint_aliases && endpoint in as.mtls_endpoint_aliases) {
    return validateEndpoint(as.mtls_endpoint_aliases[endpoint], endpoint, useMtlsAlias, enforceHttps);
  }
  return validateEndpoint(as[endpoint], endpoint, useMtlsAlias, enforceHttps);
}
class ResponseBodyError extends Error {
  cause;
  code;
  error;
  status;
  error_description;
  response;
  constructor(message, options) {
    super(message, options);
    this.name = this.constructor.name;
    this.code = RESPONSE_BODY_ERROR;
    this.cause = options.cause;
    this.error = options.cause.error;
    this.status = options.response.status;
    this.error_description = options.cause.error_description;
    Object.defineProperty(this, 'response', {
      enumerable: false,
      value: options.response
    });
    Error.captureStackTrace?.(this, this.constructor);
  }
}
class WWWAuthenticateChallengeError extends Error {
  cause;
  code;
  response;
  status;
  constructor(message, options) {
    super(message, options);
    this.name = this.constructor.name;
    this.code = WWW_AUTHENTICATE_CHALLENGE;
    this.cause = options.cause;
    this.status = options.response.status;
    this.response = options.response;
    Object.defineProperty(this, 'response', {
      enumerable: false
    });
    Error.captureStackTrace?.(this, this.constructor);
  }
}
const tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
const token68Match = '[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}';
const quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
const quotedParamMatcher = '(' + tokenMatch + ')\\s*=\\s*' + quotedMatch;
const paramMatcher = '(' + tokenMatch + ')\\s*=\\s*(' + tokenMatch + ')';
const schemeRE = new RegExp('^[,\\s]*(' + tokenMatch + ')');
const quotedParamRE = new RegExp('^[,\\s]*' + quotedParamMatcher + '[,\\s]*(.*)');
const unquotedParamRE = new RegExp('^[,\\s]*' + paramMatcher + '[,\\s]*(.*)');
const token68ParamRE = new RegExp('^(' + token68Match + ')(?:$|[,\\s])(.*)');
function parseWwwAuthenticateChallenges(response) {
  if (!looseInstanceOf(response, Response)) {
    throw CodedTypeError('"response" must be an instance of Response', ERR_INVALID_ARG_TYPE);
  }
  const header = response.headers.get('www-authenticate');
  if (header === null) {
    return undefined;
  }
  const challenges = [];
  let rest = header;
  while (rest) {
    let match = rest.match(schemeRE);
    const scheme = match?.['1'].toLowerCase();
    if (!scheme) {
      return undefined;
    }
    const afterScheme = rest.substring(match[0].length);
    if (afterScheme && !afterScheme.match(/^[\s,]/)) {
      return undefined;
    }
    const spaceMatch = afterScheme.match(/^\s+(.*)$/);
    const hasParameters = !!spaceMatch;
    rest = spaceMatch ? spaceMatch[1] : undefined;
    const parameters = {};
    let token68;
    if (hasParameters) {
      while (rest) {
        let key;
        let value;
        if (match = rest.match(quotedParamRE)) {
          [, key, value, rest] = match;
          if (value.includes('\\')) {
            try {
              value = JSON.parse(`"${value}"`);
            } catch {}
          }
          parameters[key.toLowerCase()] = value;
          continue;
        }
        if (match = rest.match(unquotedParamRE)) {
          [, key, value, rest] = match;
          parameters[key.toLowerCase()] = value;
          continue;
        }
        if (match = rest.match(token68ParamRE)) {
          if (Object.keys(parameters).length) {
            break;
          }
          [, token68, rest] = match;
          break;
        }
        return undefined;
      }
    } else {
      rest = afterScheme || undefined;
    }
    const challenge = {
      scheme,
      parameters
    };
    if (token68) {
      challenge.token68 = token68;
    }
    challenges.push(challenge);
  }
  if (!challenges.length) {
    return undefined;
  }
  return challenges;
}
async function parseOAuthResponseErrorBody(response) {
  if (response.status > 399 && response.status < 500) {
    assertReadableResponse(response);
    assertApplicationJson(response);
    try {
      const json = await response.clone().json();
      if (isJsonObject(json) && typeof json.error === 'string' && json.error.length) {
        return json;
      }
    } catch {}
  }
  return undefined;
}
async function checkOAuthBodyError(response, expected, label) {
  if (response.status !== expected) {
    checkAuthenticationChallenges(response);
    let err;
    if (err = await parseOAuthResponseErrorBody(response)) {
      await response.body?.cancel();
      throw new ResponseBodyError('server responded with an error in the response body', {
        cause: err,
        response
      });
    }
    throw OPE(`"response" is not a conform ${label} response (unexpected HTTP status code)`, RESPONSE_IS_NOT_CONFORM, response);
  }
}
function assertDPoP(option) {
  if (!branded.has(option)) {
    throw CodedTypeError('"options.DPoP" is not a valid DPoPHandle', ERR_INVALID_ARG_VALUE);
  }
}
function getContentType(input) {
  return input.headers.get('content-type')?.split(';')[0];
}
async function authenticatedRequest(as, client, clientAuthentication, url, body, headers, options) {
  await clientAuthentication(as, client, body, headers);
  headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
  return (options?.[customFetch] || fetch)(url.href, {
    body,
    headers: Object.fromEntries(headers.entries()),
    method: 'POST',
    redirect: 'manual',
    signal: signal(url, options?.signal)
  });
}
async function tokenEndpointRequest(as, client, clientAuthentication, grantType, parameters, options) {
  const url = resolveEndpoint(as, 'token_endpoint', client.use_mtls_endpoint_aliases, options?.[allowInsecureRequests] !== true);
  parameters.set('grant_type', grantType);
  const headers = prepareHeaders(options?.headers);
  headers.set('accept', 'application/json');
  if (options?.DPoP !== undefined) {
    assertDPoP(options.DPoP);
    await options.DPoP.addProof(url, headers, 'POST');
  }
  const response = await authenticatedRequest(as, client, clientAuthentication, url, parameters, headers, options);
  options?.DPoP?.cacheNonce(response, url);
  return response;
}
const idTokenClaims = new WeakMap();
const jwtRefs = new WeakMap();
async function processGenericAccessTokenResponse(as, client, response, additionalRequiredIdTokenClaims, decryptFn, recognizedTokenTypes) {
  assertAs(as);
  assertClient(client);
  if (!looseInstanceOf(response, Response)) {
    throw CodedTypeError('"response" must be an instance of Response', ERR_INVALID_ARG_TYPE);
  }
  await checkOAuthBodyError(response, 200, 'Token Endpoint');
  assertReadableResponse(response);
  const json = await getResponseJsonBody(response);
  assertString(json.access_token, '"response" body "access_token" property', INVALID_RESPONSE, {
    body: json
  });
  assertString(json.token_type, '"response" body "token_type" property', INVALID_RESPONSE, {
    body: json
  });
  json.token_type = json.token_type.toLowerCase();
  if (json.expires_in !== undefined) {
    let expiresIn = typeof json.expires_in !== 'number' ? parseFloat(json.expires_in) : json.expires_in;
    assertNumber(expiresIn, true, '"response" body "expires_in" property', INVALID_RESPONSE, {
      body: json
    });
    json.expires_in = expiresIn;
  }
  if (json.refresh_token !== undefined) {
    assertString(json.refresh_token, '"response" body "refresh_token" property', INVALID_RESPONSE, {
      body: json
    });
  }
  if (json.scope !== undefined && typeof json.scope !== 'string') {
    throw OPE('"response" body "scope" property must be a string', INVALID_RESPONSE, {
      body: json
    });
  }
  if (json.id_token !== undefined) {
    assertString(json.id_token, '"response" body "id_token" property', INVALID_RESPONSE, {
      body: json
    });
    const requiredClaims = ['aud', 'exp', 'iat', 'iss', 'sub'];
    if (client.require_auth_time === true) {
      requiredClaims.push('auth_time');
    }
    if (client.default_max_age !== undefined) {
      assertNumber(client.default_max_age, true, '"client.default_max_age"');
      requiredClaims.push('auth_time');
    }
    const {
      claims,
      jwt
    } = await validateJwt(json.id_token, checkSigningAlgorithm.bind(undefined, client.id_token_signed_response_alg, as.id_token_signing_alg_values_supported, 'RS256'), getClockSkew(client), getClockTolerance(client), decryptFn).then(validatePresence.bind(undefined, requiredClaims)).then(validateIssuer.bind(undefined, as)).then(validateAudience.bind(undefined, client.client_id));
    if (Array.isArray(claims.aud) && claims.aud.length !== 1) {
      if (claims.azp === undefined) {
        throw OPE('ID Token "aud" (audience) claim includes additional untrusted audiences', JWT_CLAIM_COMPARISON, {
          claims,
          claim: 'aud'
        });
      }
      if (claims.azp !== client.client_id) {
        throw OPE('unexpected ID Token "azp" (authorized party) claim value', JWT_CLAIM_COMPARISON, {
          expected: client.client_id,
          claims,
          claim: 'azp'
        });
      }
    }
    if (claims.auth_time !== undefined) {
      assertNumber(claims.auth_time, true, 'ID Token "auth_time" (authentication time)', INVALID_RESPONSE, {
        claims
      });
    }
    jwtRefs.set(response, jwt);
    idTokenClaims.set(json, claims);
  }
  if (recognizedTokenTypes?.[json.token_type] !== undefined) {
    recognizedTokenTypes[json.token_type](response, json);
  } else if (json.token_type !== 'dpop' && json.token_type !== 'bearer') {
    throw new UnsupportedOperationError('unsupported `token_type` value', {
      cause: {
        body: json
      }
    });
  }
  return json;
}
function checkAuthenticationChallenges(response) {
  let challenges;
  if (challenges = parseWwwAuthenticateChallenges(response)) {
    throw new WWWAuthenticateChallengeError('server responded with a challenge in the WWW-Authenticate HTTP Header', {
      cause: challenges,
      response
    });
  }
}
function validateAudience(expected, result) {
  if (Array.isArray(result.claims.aud)) {
    if (!result.claims.aud.includes(expected)) {
      throw OPE('unexpected JWT "aud" (audience) claim value', JWT_CLAIM_COMPARISON, {
        expected,
        claims: result.claims,
        claim: 'aud'
      });
    }
  } else if (result.claims.aud !== expected) {
    throw OPE('unexpected JWT "aud" (audience) claim value', JWT_CLAIM_COMPARISON, {
      expected,
      claims: result.claims,
      claim: 'aud'
    });
  }
  return result;
}
function validateIssuer(as, result) {
  const expected = as[_expectedIssuer]?.(result) ?? as.issuer;
  if (result.claims.iss !== expected) {
    throw OPE('unexpected JWT "iss" (issuer) claim value', JWT_CLAIM_COMPARISON, {
      expected,
      claims: result.claims,
      claim: 'iss'
    });
  }
  return result;
}
const branded = new WeakSet();
const jwtClaimNames = {
  aud: 'audience',
  c_hash: 'code hash',
  client_id: 'client id',
  exp: 'expiration time',
  iat: 'issued at',
  iss: 'issuer',
  jti: 'jwt id',
  nonce: 'nonce',
  s_hash: 'state hash',
  sub: 'subject',
  ath: 'access token hash',
  htm: 'http method',
  htu: 'http uri',
  cnf: 'confirmation',
  auth_time: 'authentication time'
};
function validatePresence(required, result) {
  for (const claim of required) {
    if (result.claims[claim] === undefined) {
      throw OPE(`JWT "${claim}" (${jwtClaimNames[claim]}) claim missing`, INVALID_RESPONSE, {
        claims: result.claims
      });
    }
  }
  return result;
}
const WWW_AUTHENTICATE_CHALLENGE = 'OAUTH_WWW_AUTHENTICATE_CHALLENGE';
const RESPONSE_BODY_ERROR = 'OAUTH_RESPONSE_BODY_ERROR';
const UNSUPPORTED_OPERATION = 'OAUTH_UNSUPPORTED_OPERATION';
const PARSE_ERROR = 'OAUTH_PARSE_ERROR';
const INVALID_RESPONSE = 'OAUTH_INVALID_RESPONSE';
const RESPONSE_IS_NOT_JSON = 'OAUTH_RESPONSE_IS_NOT_JSON';
const RESPONSE_IS_NOT_CONFORM = 'OAUTH_RESPONSE_IS_NOT_CONFORM';
const HTTP_REQUEST_FORBIDDEN = 'OAUTH_HTTP_REQUEST_FORBIDDEN';
const REQUEST_PROTOCOL_FORBIDDEN = 'OAUTH_REQUEST_PROTOCOL_FORBIDDEN';
const JWT_TIMESTAMP_CHECK = 'OAUTH_JWT_TIMESTAMP_CHECK_FAILED';
const JWT_CLAIM_COMPARISON = 'OAUTH_JWT_CLAIM_COMPARISON_FAILED';
const MISSING_SERVER_METADATA = 'OAUTH_MISSING_SERVER_METADATA';
const INVALID_SERVER_METADATA = 'OAUTH_INVALID_SERVER_METADATA';
async function genericTokenEndpointRequest(as, client, clientAuthentication, grantType, parameters, options) {
  assertAs(as);
  assertClient(client);
  assertString(grantType, '"grantType"');
  return tokenEndpointRequest(as, client, clientAuthentication, grantType, new URLSearchParams(parameters), options);
}
async function processGenericTokenEndpointResponse(as, client, response, options) {
  return processGenericAccessTokenResponse(as, client, response, undefined, options?.[jweDecrypt], options?.recognizedTokenTypes);
}
function assertReadableResponse(response) {
  if (response.bodyUsed) {
    throw CodedTypeError('"response" body has been used already', ERR_INVALID_ARG_VALUE);
  }
}
async function validateJwt(jws, checkAlg, clockSkew, clockTolerance, decryptJwt) {
  let {
    0: protectedHeader,
    1: payload,
    length
  } = jws.split('.');
  if (length === 5) {
    if (decryptJwt !== undefined) {
      jws = await decryptJwt(jws);
      ({
        0: protectedHeader,
        1: payload,
        length
      } = jws.split('.'));
    } else {
      throw new UnsupportedOperationError('JWE decryption is not configured', {
        cause: jws
      });
    }
  }
  if (length !== 3) {
    throw OPE('Invalid JWT', INVALID_RESPONSE, jws);
  }
  let header;
  try {
    header = JSON.parse(buf(b64u(protectedHeader)));
  } catch (cause) {
    throw OPE('failed to parse JWT Header body as base64url encoded JSON', PARSE_ERROR, cause);
  }
  if (!isJsonObject(header)) {
    throw OPE('JWT Header must be a top level object', INVALID_RESPONSE, jws);
  }
  checkAlg(header);
  if (header.crit !== undefined) {
    throw new UnsupportedOperationError('no JWT "crit" header parameter extensions are supported', {
      cause: {
        header
      }
    });
  }
  let claims;
  try {
    claims = JSON.parse(buf(b64u(payload)));
  } catch (cause) {
    throw OPE('failed to parse JWT Payload body as base64url encoded JSON', PARSE_ERROR, cause);
  }
  if (!isJsonObject(claims)) {
    throw OPE('JWT Payload must be a top level object', INVALID_RESPONSE, jws);
  }
  const now = epochTime() + clockSkew;
  if (claims.exp !== undefined) {
    if (typeof claims.exp !== 'number') {
      throw OPE('unexpected JWT "exp" (expiration time) claim type', INVALID_RESPONSE, {
        claims
      });
    }
    if (claims.exp <= now - clockTolerance) {
      throw OPE('unexpected JWT "exp" (expiration time) claim value, expiration is past current timestamp', JWT_TIMESTAMP_CHECK, {
        claims,
        now,
        tolerance: clockTolerance,
        claim: 'exp'
      });
    }
  }
  if (claims.iat !== undefined) {
    if (typeof claims.iat !== 'number') {
      throw OPE('unexpected JWT "iat" (issued at) claim type', INVALID_RESPONSE, {
        claims
      });
    }
  }
  if (claims.iss !== undefined) {
    if (typeof claims.iss !== 'string') {
      throw OPE('unexpected JWT "iss" (issuer) claim type', INVALID_RESPONSE, {
        claims
      });
    }
  }
  if (claims.nbf !== undefined) {
    if (typeof claims.nbf !== 'number') {
      throw OPE('unexpected JWT "nbf" (not before) claim type', INVALID_RESPONSE, {
        claims
      });
    }
    if (claims.nbf > now + clockTolerance) {
      throw OPE('unexpected JWT "nbf" (not before) claim value', JWT_TIMESTAMP_CHECK, {
        claims,
        now,
        tolerance: clockTolerance,
        claim: 'nbf'
      });
    }
  }
  if (claims.aud !== undefined) {
    if (typeof claims.aud !== 'string' && !Array.isArray(claims.aud)) {
      throw OPE('unexpected JWT "aud" (audience) claim type', INVALID_RESPONSE, {
        claims
      });
    }
  }
  return {
    header,
    claims,
    jwt: jws
  };
}
function checkSigningAlgorithm(client, issuer, fallback, header) {
  if (client !== undefined) {
    if (typeof client === 'string' ? header.alg !== client : !client.includes(header.alg)) {
      throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
        header,
        expected: client,
        reason: 'client configuration'
      });
    }
    return;
  }
  if (Array.isArray(issuer)) {
    if (!issuer.includes(header.alg)) {
      throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
        header,
        expected: issuer,
        reason: 'authorization server metadata'
      });
    }
    return;
  }
  if (fallback !== undefined) {
    if (typeof fallback === 'string' ? header.alg !== fallback : typeof fallback === 'function' ? !fallback(header.alg) : !fallback.includes(header.alg)) {
      throw OPE('unexpected JWT "alg" header parameter', INVALID_RESPONSE, {
        header,
        expected: fallback,
        reason: 'default value'
      });
    }
    return;
  }
  throw OPE('missing client or server configuration to verify used JWT "alg" header parameter', undefined, {
    client,
    issuer,
    fallback
  });
}
async function getResponseJsonBody(response, check = assertApplicationJson) {
  let json;
  try {
    json = await response.json();
  } catch (cause) {
    check(response);
    throw OPE('failed to parse "response" body as JSON', PARSE_ERROR, cause);
  }
  if (!isJsonObject(json)) {
    throw OPE('"response" body must be a top level object', INVALID_RESPONSE, {
      body: json
    });
  }
  return json;
}
const _expectedIssuer = Symbol();

const trailingSlashesRegex = /\/+$/;
const trimTrailingSlashes = value => {
  return value.replace(trailingSlashesRegex, '');
};

const getBackendAuthUrl = (backendUrl, path) => {
  return `${trimTrailingSlashes(backendUrl)}${path}`;
};

const getBackendOidcTokenUrl = backendUrl => {
  return getBackendAuthUrl(backendUrl, '/oidc/token');
};

const getAuthorizationServer$1 = backendUrl => {
  return {
    issuer: getBackendAuthUrl(backendUrl, '/oidc'),
    jwks_uri: getBackendAuthUrl(backendUrl, '/oidc/jwks'),
    token_endpoint: getBackendOidcTokenUrl(backendUrl)
  };
};
const getClient$1 = clientId => {
  return {
    client_id: clientId
  };
};
const exchangeAuthorizationCode = async (backendUrl, clientId, code, redirectUri, codeVerifier, requestTokenEndpoint = genericTokenEndpointRequest, processTokenEndpointResponse = processGenericTokenEndpointResponse) => {
  const authorizationServer = getAuthorizationServer$1(backendUrl);
  const client = getClient$1(clientId);
  const response = await requestTokenEndpoint(authorizationServer, client, None(), 'authorization_code', new URLSearchParams({
    code,
    code_verifier: codeVerifier,
    redirect_uri: redirectUri
  }));
  const tokenResponse = await processTokenEndpointResponse(authorizationServer, client, response);
  return {
    accessToken: tokenResponse.access_token,
    refreshToken: typeof tokenResponse.refresh_token === 'string' ? tokenResponse.refresh_token : ''
  };
};

const getCurrentHref = async () => {
  try {
    return await invoke('Layout.getHref');
  } catch {
    // ignore
  }
  if (!globalThis.location || typeof globalThis.location.href !== 'string' || !globalThis.location.href) {
    return '';
  }
  return globalThis.location.href;
};

const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
  return {
    authAccessToken: '',
    authErrorMessage,
    userState: 'loggedOut'
  };
};

const getPayload$1 = async response => {
  try {
    return await response.json();
  } catch {
    return undefined;
  }
};
const getOidcUserName = async (backendUrl, accessToken, fetchFn = fetch) => {
  if (!backendUrl || !accessToken) {
    return '';
  }
  const response = await fetchFn(new URL('/account/me', backendUrl), {
    headers: {
      Accept: 'application/json',
      Authorization: `Bearer ${accessToken}`
    }
  });
  if (!response.ok) {
    return '';
  }
  const payload = await getPayload$1(response);
  if (!payload || typeof payload !== 'object') {
    return '';
  }
  const displayName = Reflect.get(payload, 'displayName');
  return typeof displayName === 'string' ? displayName : '';
};

const databaseName = 'auth-worker';
const objectStoreName = 'auth';
const memoryStorage = new Map();
let databasePromise;

// IndexedDB request objects are mutable browser primitives and cannot satisfy the readonly rule structurally.
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
const requestToPromise = request => {
  return new Promise((resolve, reject) => {
    request.addEventListener('success', () => {
      resolve(request.result);
    });
    request.addEventListener('error', () => {
      reject(request.error ?? new Error('Persistent storage request failed.'));
    });
  });
};

// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
const transactionToPromise = transaction => {
  return new Promise((resolve, reject) => {
    transaction.addEventListener('complete', () => {
      resolve();
    });
    transaction.addEventListener('abort', () => {
      reject(transaction.error ?? new Error('Persistent storage transaction failed.'));
    });
    transaction.addEventListener('error', () => {
      reject(transaction.error ?? new Error('Persistent storage transaction failed.'));
    });
  });
};
const getDatabase = async () => {
  if (typeof indexedDB === 'undefined') {
    return undefined;
  }
  if (!databasePromise) {
    databasePromise = new Promise((resolve, reject) => {
      const request = indexedDB.open(databaseName, 1);
      request.addEventListener('upgradeneeded', () => {
        const database = request.result;
        if (!database.objectStoreNames.contains(objectStoreName)) {
          database.createObjectStore(objectStoreName);
        }
      });
      request.addEventListener('success', () => {
        resolve(request.result);
      });
      request.addEventListener('error', () => {
        reject(request.error ?? new Error('Failed to open persistent auth storage.'));
      });
    });
  }
  return databasePromise;
};
const getPersistentAuthValue = async key => {
  const database = await getDatabase();
  if (!database) {
    return memoryStorage.get(key) ?? '';
  }
  const transaction = database.transaction(objectStoreName, 'readonly');
  const objectStore = transaction.objectStore(objectStoreName);
  const value = await requestToPromise(objectStore.get(key));
  return typeof value === 'string' ? value : '';
};
const setPersistentAuthValue = async (key, value) => {
  memoryStorage.set(key, value);
  const database = await getDatabase();
  if (!database) {
    return;
  }
  const transaction = database.transaction(objectStoreName, 'readwrite');
  const objectStore = transaction.objectStore(objectStoreName);
  objectStore.put(value, key);
  await transactionToPromise(transaction);
};
const clearPersistentAuthValue = async key => {
  memoryStorage.delete(key);
  const database = await getDatabase();
  if (!database) {
    return;
  }
  const transaction = database.transaction(objectStoreName, 'readwrite');
  const objectStore = transaction.objectStore(objectStoreName);
  objectStore.delete(key);
  await transactionToPromise(transaction);
};

const callbackUrlKey = 'oidcCallbackUrl';
const clientIdKey = 'oidcClientId';
const pendingClientIdKey = 'pendingOidcClientId';
const pendingCodeVerifierKey = 'pendingOidcCodeVerifier';
const pendingRedirectUriKey = 'pendingOidcRedirectUri';
const pendingStateKey = 'pendingOidcState';
const clearOidcCallbackUrl = async () => {
  await clearPersistentAuthValue(callbackUrlKey);
};
const clearStoredOidcClientId = async () => {
  await clearPersistentAuthValue(clientIdKey);
};
const clearPendingOidcAuthState = async () => {
  await Promise.all([clearPersistentAuthValue(pendingClientIdKey), clearPersistentAuthValue(pendingCodeVerifierKey), clearPersistentAuthValue(pendingRedirectUriKey), clearPersistentAuthValue(pendingStateKey)]);
};
const getOidcCallbackUrl = async () => {
  return getPersistentAuthValue(callbackUrlKey);
};
const getStoredOidcClientId = async () => {
  return getPersistentAuthValue(clientIdKey);
};
const loadPendingOidcAuthState = async () => {
  const [clientId, codeVerifier, redirectUri, state] = await Promise.all([getPersistentAuthValue(pendingClientIdKey), getPersistentAuthValue(pendingCodeVerifierKey), getPersistentAuthValue(pendingRedirectUriKey), getPersistentAuthValue(pendingStateKey)]);
  if (!clientId || !codeVerifier || !redirectUri || !state) {
    return undefined;
  }
  return {
    clientId,
    codeVerifier,
    redirectUri,
    state
  };
};
const saveOidcClientId = async clientId => {
  await setPersistentAuthValue(clientIdKey, clientId);
};
const savePendingOidcAuthState = async value => {
  await Promise.all([setPersistentAuthValue(pendingClientIdKey, value.clientId), setPersistentAuthValue(pendingCodeVerifierKey, value.codeVerifier), setPersistentAuthValue(pendingRedirectUriKey, value.redirectUri), setPersistentAuthValue(pendingStateKey, value.state)]);
};

const normalizeRedirectUri = value => {
  const url = new URL(value);
  url.hash = '';
  url.search = '';
  return url.toString();
};
const getCallbackHref = async () => {
  const storedCallbackUrl = await getOidcCallbackUrl();
  if (storedCallbackUrl) {
    await clearOidcCallbackUrl();
    return storedCallbackUrl;
  }
  return getCurrentHref();
};
const completeBrowserOidcLogin = async backendUrl => {
  const href = await getCallbackHref();
  if (!href) {
    return undefined;
  }
  let url;
  try {
    url = new URL(href);
  } catch {
    return undefined;
  }
  const code = url.searchParams.get('code') || '';
  const error = url.searchParams.get('error') || '';
  const errorDescription = url.searchParams.get('error_description') || '';
  if (!code && !error) {
    return undefined;
  }
  const pendingAuthState = await loadPendingOidcAuthState();
  if (!pendingAuthState) {
    await clearPendingOidcAuthState();
    return getLoggedOutBackendAuthState('Authentication state is missing.');
  }
  if (normalizeRedirectUri(href) !== normalizeRedirectUri(pendingAuthState.redirectUri)) {
    await clearPendingOidcAuthState();
    return getLoggedOutBackendAuthState('Authentication returned to an unexpected redirect URI.');
  }
  if (error) {
    await clearPendingOidcAuthState();
    return getLoggedOutBackendAuthState(errorDescription || error);
  }
  const returnedState = url.searchParams.get('state') || '';
  if (!returnedState || returnedState !== pendingAuthState.state) {
    await clearPendingOidcAuthState();
    return getLoggedOutBackendAuthState('Authentication state mismatch.');
  }
  const exchanged = await exchangeAuthorizationCode(backendUrl, pendingAuthState.clientId, code, pendingAuthState.redirectUri, pendingAuthState.codeVerifier);
  const userName = await getOidcUserName(backendUrl, exchanged.accessToken);
  await clearPendingOidcAuthState();
  return {
    authAccessToken: exchanged.accessToken,
    authClientId: pendingAuthState.clientId,
    authErrorMessage: '',
    authRefreshToken: exchanged.refreshToken,
    userName,
    userState: exchanged.accessToken ? 'loggedIn' : 'loggedOut'
  };
};

const accessTokenKey = 'accessToken';
const refreshTokenKey = 'refreshToken';
const userNameKey = 'userName';
const userSubscriptionPlanKey = 'userSubscriptionPlan';
const userSubscriptionStatusKey = 'userSubscriptionStatus';
const userUsedTokensKey = 'userUsedTokens';
const toOptionalString = value => {
  return value || undefined;
};
const toOptionalNumber = value => {
  if (!value) {
    return undefined;
  }
  const numberValue = Number(value);
  return Number.isFinite(numberValue) ? numberValue : undefined;
};
const clearPersistedAuthSession = async () => {
  await Promise.all([clearPersistentAuthValue(accessTokenKey), clearPersistentAuthValue(refreshTokenKey), clearStoredOidcClientId(), clearPersistentAuthValue(userNameKey), clearPersistentAuthValue(userSubscriptionPlanKey), clearPersistentAuthValue(userSubscriptionStatusKey), clearPersistentAuthValue(userUsedTokensKey)]);
};
const getPersistedAuthSession = async () => {
  const [accessToken, refreshToken, authClientId, userName, userSubscriptionPlan, userSubscriptionStatus, userUsedTokens] = await Promise.all([getPersistentAuthValue(accessTokenKey), getPersistentAuthValue(refreshTokenKey), getStoredOidcClientId(), getPersistentAuthValue(userNameKey), getPersistentAuthValue(userSubscriptionPlanKey), getPersistentAuthValue(userSubscriptionStatusKey), getPersistentAuthValue(userUsedTokensKey)]);
  if (!accessToken && !refreshToken) {
    return undefined;
  }
  const optionalAuthClientId = toOptionalString(authClientId);
  const optionalRefreshToken = toOptionalString(refreshToken);
  const optionalUserName = toOptionalString(userName);
  const optionalSubscriptionPlan = toOptionalString(userSubscriptionPlan);
  const optionalSubscriptionStatus = toOptionalString(userSubscriptionStatus);
  const optionalUsedTokens = toOptionalNumber(userUsedTokens);
  return {
    authAccessToken: accessToken,
    authErrorMessage: '',
    userState: 'loggedIn',
    ...(optionalAuthClientId ? {
      authClientId
    } : {}),
    ...(optionalRefreshToken ? {
      authRefreshToken: refreshToken
    } : {}),
    ...(optionalUserName ? {
      userName
    } : {}),
    ...(optionalSubscriptionPlan ? {
      userSubscriptionPlan
    } : {}),
    ...(optionalSubscriptionStatus ? {
      userSubscriptionStatus
    } : {}),
    ...(typeof optionalUsedTokens === 'number' ? {
      userUsedTokens: optionalUsedTokens
    } : {})
  };
};
const persistAuthSession = async loginResult => {
  await Promise.all([setPersistentAuthValue(accessTokenKey, loginResult.authAccessToken ?? ''), setPersistentAuthValue(refreshTokenKey, loginResult.authRefreshToken ?? ''), loginResult.authClientId ? saveOidcClientId(loginResult.authClientId) : clearStoredOidcClientId(), setPersistentAuthValue(userNameKey, loginResult.userName ?? ''), setPersistentAuthValue(userSubscriptionPlanKey, loginResult.userSubscriptionPlan ?? ''), setPersistentAuthValue(userSubscriptionStatusKey, loginResult.userSubscriptionStatus ?? ''), setPersistentAuthValue(userUsedTokensKey, typeof loginResult.userUsedTokens === 'number' ? String(loginResult.userUsedTokens) : '')]);
};

const persistLoginResult = async loginResult => {
  if (loginResult.userState !== 'loggedIn') {
    await clearPersistedAuthSession();
    return loginResult;
  }
  await persistAuthSession(loginResult);
  return loginResult;
};

const getBackendUrl = options => {
  if (typeof options === 'number') {
    return '';
  }
  return options.backendUrl || '';
};
const initialize = async options => {
  const backendUrl = getBackendUrl(options);
  try {
    if (backendUrl) {
      const completedBrowserLogin = await completeBrowserOidcLogin(backendUrl);
      if (completedBrowserLogin) {
        return persistLoginResult(completedBrowserLogin);
      }
    }
    const persistedAuthSession = await getPersistedAuthSession();
    if (persistedAuthSession) {
      return persistedAuthSession;
    }
    return getLoggedOutBackendAuthState();
  } catch (error) {
    const authErrorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
    return getLoggedOutBackendAuthState(authErrorMessage);
  }
};

const getBackendLogoutUrl = backendUrl => {
  return getBackendAuthUrl(backendUrl, '/auth/logout');
};

const logoutFromBackend = async backendUrl => {
  if (!backendUrl) {
    return;
  }
  try {
    await fetch(getBackendLogoutUrl(backendUrl), {
      credentials: 'include',
      headers: {
        Accept: 'application/json'
      },
      method: 'POST'
    });
  } catch {
    // Ignore logout failures and still clear local auth state.
  }
};

const getBackendRefreshUrl = backendUrl => {
  return getBackendAuthUrl(backendUrl, '/auth/refresh');
};

const delay = async ms => {
  await new Promise(resolve => setTimeout(resolve, ms));
};

let nextLoginResponse;
let nextRefreshResponse;
const setNextLoginResponse = response => {
  nextLoginResponse = response;
};
const setNextRefreshResponse = response => {
  nextRefreshResponse = response;
};
const clear = () => {
  nextLoginResponse = undefined;
  nextRefreshResponse = undefined;
};
const hasPendingMockLoginResponse = () => {
  return !!nextLoginResponse;
};
const hasPendingMockRefreshResponse = () => {
  return !!nextRefreshResponse;
};
const consumeNextLoginResponse = async () => {
  if (!nextLoginResponse) {
    return undefined;
  }
  const response = nextLoginResponse;
  nextLoginResponse = undefined;
  if (response.delay > 0) {
    await delay(response.delay);
  }
  if (response.type === 'error') {
    throw new Error(response.message);
  }
  return response.response;
};
const consumeNextRefreshResponse = async () => {
  if (!nextRefreshResponse) {
    return undefined;
  }
  const response = nextRefreshResponse;
  nextRefreshResponse = undefined;
  if (response.delay > 0) {
    await new Promise(resolve => setTimeout(resolve, response.delay));
  }
  if (response.type === 'error') {
    throw new Error(response.message);
  }
  return response.response;
};

const isObject = value => {
  return !!value && typeof value === 'object';
};

const isBackendAuthResponse = value => {
  return isObject(value);
};

const getNumber = (value, fallback = 0) => {
  return typeof value === 'number' ? value : fallback;
};

const getString = (value, fallback = '') => {
  return typeof value === 'string' ? value : fallback;
};

const getUserName = value => {
  if (typeof value.userName === 'string') {
    return value.userName;
  }
  return getString(value.user?.displayName);
};
const toBackendAuthState = value => {
  return {
    authAccessToken: getString(value.accessToken),
    authErrorMessage: getString(value.error),
    authRefreshToken: getString(value.refreshToken),
    userName: getUserName(value),
    userState: value.accessToken ? 'loggedIn' : 'loggedOut',
    userSubscriptionPlan: getString(value.subscriptionPlan),
    userSubscriptionStatus: getString(value.subscriptionStatus),
    userUsedTokens: getNumber(value.usedTokens)
  };
};

const parseBackendAuthResponse = value => {
  if (!isBackendAuthResponse(value)) {
    return getLoggedOutBackendAuthState('Backend returned an invalid authentication response.');
  }
  return toBackendAuthState(value);
};

const getAuthorizationServer = backendUrl => {
  return {
    issuer: getBackendAuthUrl(backendUrl, '/oidc'),
    jwks_uri: getBackendAuthUrl(backendUrl, '/oidc/jwks'),
    token_endpoint: getBackendOidcTokenUrl(backendUrl)
  };
};
const getClient = clientId => {
  return {
    client_id: clientId
  };
};
const refreshOidcTokens = async (backendUrl, clientId, refreshToken, requestTokenEndpoint = genericTokenEndpointRequest, processTokenEndpointResponse = processGenericTokenEndpointResponse) => {
  const authorizationServer = getAuthorizationServer(backendUrl);
  const client = getClient(clientId);
  const response = await requestTokenEndpoint(authorizationServer, client, None(), 'refresh_token', new URLSearchParams({
    refresh_token: refreshToken
  }));
  const tokenResponse = await processTokenEndpointResponse(authorizationServer, client, response);
  return {
    accessToken: tokenResponse.access_token,
    refreshToken: typeof tokenResponse.refresh_token === 'string' ? tokenResponse.refresh_token : refreshToken
  };
};

const clearStoredOidcAuth = async () => {
  await clearPersistedAuthSession();
};
const toLoginResult = (accessToken, refreshToken, clientId, userName) => {
  return {
    authAccessToken: accessToken,
    authClientId: clientId,
    authErrorMessage: '',
    authRefreshToken: refreshToken,
    userName,
    userState: accessToken ? 'loggedIn' : 'loggedOut'
  };
};
const restoreOidcAuth = async backendUrl => {
  const [accessToken, refreshToken, clientId] = await Promise.all([getPersistentAuthValue('accessToken'), getPersistentAuthValue('refreshToken'), getStoredOidcClientId()]);
  if (!refreshToken || !clientId) {
    return undefined;
  }
  try {
    const refreshedTokens = await refreshOidcTokens(backendUrl, clientId, refreshToken);
    const userName = await getOidcUserName(backendUrl, refreshedTokens.accessToken);
    return toLoginResult(refreshedTokens.accessToken, refreshedTokens.refreshToken, clientId, userName);
  } catch {
    if (accessToken) {
      const userName = await getOidcUserName(backendUrl, accessToken);
      if (userName) {
        return toLoginResult(accessToken, refreshToken, clientId, userName);
      }
    }
    await clearStoredOidcAuth();
    return undefined;
  }
};

const getPayload = async response => {
  try {
    return await response.json();
  } catch {
    return undefined;
  }
};
const syncBackendAuth = async backendUrl => {
  if (!backendUrl) {
    return getLoggedOutBackendAuthState('Backend URL is missing.');
  }
  try {
    const completedBrowserLogin = await completeBrowserOidcLogin(backendUrl);
    if (completedBrowserLogin) {
      return persistLoginResult(completedBrowserLogin);
    }
    const restoredOidcAuth = await restoreOidcAuth(backendUrl);
    if (restoredOidcAuth) {
      return persistLoginResult(restoredOidcAuth);
    }
    if (hasPendingMockRefreshResponse()) {
      const mockResponse = await consumeNextRefreshResponse();
      return parseBackendAuthResponse(mockResponse);
    }
    const response = await fetch(getBackendRefreshUrl(backendUrl), {
      credentials: 'include',
      headers: {
        Accept: 'application/json'
      },
      method: 'POST'
    });
    if (response.status === 401 || response.status === 403) {
      return getLoggedOutBackendAuthState();
    }
    const payload = await getPayload(response);
    if (!response.ok) {
      const parsed = parseBackendAuthResponse(payload);
      return getLoggedOutBackendAuthState(parsed.authErrorMessage || 'Backend authentication failed.');
    }
    const parsed = parseBackendAuthResponse(payload);
    if (parsed.authErrorMessage) {
      return getLoggedOutBackendAuthState(parsed.authErrorMessage);
    }
    if (!parsed.authAccessToken) {
      return getLoggedOutBackendAuthState();
    }
    return parsed;
  } catch (error) {
    const authErrorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
    return getLoggedOutBackendAuthState(authErrorMessage);
  }
};

const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalMs = 1000) => {
  const deadline = Date.now() + timeoutMs;
  let lastErrorMessage = '';
  while (Date.now() < deadline) {
    const authState = await syncBackendAuth(backendUrl);
    if (authState.userState === 'loggedIn') {
      return authState;
    }
    if (authState.authErrorMessage) {
      lastErrorMessage = authState.authErrorMessage;
    }
    await delay(pollIntervalMs);
  }
  return getLoggedOutBackendAuthState(lastErrorMessage);
};

// cspell:ignore pkce

const createPkceValues = async () => {
  const codeVerifier = generateRandomCodeVerifier();
  const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
  return {
    codeChallenge,
    codeVerifier
  };
};

const successHtml = `<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Authentication Complete</title>
    <style>
      :root {
        color-scheme: light;
        --background: linear-gradient(180deg, #f4f7fb 0%, #e9eef8 100%);
        --panel: rgba(255, 255, 255, 0.92);
        --panel-border: rgba(33, 52, 88, 0.08);
        --text: #132238;
        --muted: #5f6f86;
        --accent: #1f7a5a;
        --accent-soft: #e7f6ef;
        --shadow: 0 24px 60px rgba(44, 65, 98, 0.16);
      }

      * {
        box-sizing: border-box;
      }

      html,
      body {
        margin: 0;
        min-height: 100%;
        font-family: Inter, sans-serif;
        background: var(--background);
        color: var(--text);
      }

      body {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 24px;
      }

      .card {
        width: min(100%, 460px);
        padding: 32px 28px;
        border: 1px solid var(--panel-border);
        border-radius: 20px;
        background: var(--panel);
        box-shadow: var(--shadow);
        text-align: center;
        backdrop-filter: blur(12px);
      }

      .badge {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 64px;
        height: 64px;
        margin-bottom: 20px;
        border-radius: 999px;
        background: var(--accent-soft);
        color: var(--accent);
      }

      h1 {
        margin: 0;
        font-size: 28px;
        line-height: 1.15;
        letter-spacing: -0.03em;
      }

      p {
        margin: 14px 0 0;
        font-size: 15px;
        line-height: 1.6;
        color: var(--muted);
      }

      .hint {
        margin-top: 22px;
        padding: 12px 14px;
        border-radius: 12px;
        background: rgba(19, 34, 56, 0.04);
        font-size: 14px;
      }

      .button {
        margin-top: 20px;
        border: 0;
        border-radius: 999px;
        background: var(--text);
        color: #fff;
        padding: 10px 18px;
        font: inherit;
        cursor: pointer;
      }

      .button:hover {
        background: #0d182a;
      }
    </style>
  </head>
  <body>
    <main class="card">
      <div class="badge" aria-hidden="true">
        <svg width="28" height="28" viewBox="0 0 24 24" fill="none" role="presentation">
          <path d="M20 7L10 17L5 12" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
        </svg>
      </div>
      <h1>Authentication complete</h1>
      <p>Your sign-in finished successfully. You can return to the app now.</p>
      <p class="hint">This window is no longer needed and can be closed.</p>
      <button class="button" type="button" id="close-button">Close Window</button>
    </main>
    <script>
      const closeWindow = () => {
        window.close()
      }

      document.getElementById('close-button')?.addEventListener('click', closeWindow)
      window.setTimeout(closeWindow, 1200)
    </script>
  </body>
</html>`;
const errorHtml = `<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Authentication Failed</title>
    <style>
      :root {
        color-scheme: light;
        --background: linear-gradient(180deg, #fbf4f2 0%, #f7e5df 100%);
        --panel: rgba(255, 255, 255, 0.94);
        --panel-border: rgba(120, 43, 24, 0.12);
        --text: #3a1d16;
        --muted: #7f5a4c;
        --accent: #b4492d;
        --accent-soft: #fde9e2;
        --shadow: 0 24px 60px rgba(96, 48, 32, 0.14);
      }

      * {
        box-sizing: border-box;
      }

      html,
      body {
        margin: 0;
        min-height: 100%;
        font-family: Inter, sans-serif;
        background: var(--background);
        color: var(--text);
      }

      body {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 24px;
      }

      .card {
        width: min(100%, 460px);
        padding: 32px 28px;
        border: 1px solid var(--panel-border);
        border-radius: 20px;
        background: var(--panel);
        box-shadow: var(--shadow);
        text-align: center;
        backdrop-filter: blur(12px);
      }

      .badge {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 64px;
        height: 64px;
        margin-bottom: 20px;
        border-radius: 999px;
        background: var(--accent-soft);
        color: var(--accent);
      }

      h1 {
        margin: 0;
        font-size: 28px;
        line-height: 1.15;
        letter-spacing: -0.03em;
      }

      p {
        margin: 14px 0 0;
        font-size: 15px;
        line-height: 1.6;
        color: var(--muted);
      }

      .hint {
        margin-top: 22px;
        padding: 12px 14px;
        border-radius: 12px;
        background: rgba(58, 29, 22, 0.05);
        font-size: 14px;
      }

      .button {
        margin-top: 20px;
        border: 0;
        border-radius: 999px;
        background: var(--text);
        color: #fff;
        padding: 10px 18px;
        font: inherit;
        cursor: pointer;
      }

      .button:hover {
        background: #24110d;
      }
    </style>
  </head>
  <body>
    <main class="card">
      <div class="badge" aria-hidden="true">
        <svg width="28" height="28" viewBox="0 0 24 24" fill="none" role="presentation">
          <path d="M12 8V12" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"></path>
          <path d="M12 16H12.01" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"></path>
          <path d="M10.29 3.86L1.82 18A2 2 0 0 0 3.53 21H20.47A2 2 0 0 0 22.18 18L13.71 3.86A2 2 0 0 0 10.29 3.86Z" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
        </svg>
      </div>
      <h1>Authentication failed</h1>
      <p>The sign-in flow did not return an authorization code.</p>
      <p class="hint">You can close this window and try again from the app.</p>
      <button class="button" type="button" id="close-button">Close Window</button>
    </main>
    <script>
      const closeWindow = () => {
        window.close()
      }

      document.getElementById('close-button')?.addEventListener('click', closeWindow)
    </script>
  </body>
</html>`;

const getElectronRedirectUri = async uid => {
  const localOauthServerPort = await invoke$2('OAuthServer.create', String(uid), successHtml, errorHtml);
  return `http://localhost:${localOauthServerPort}/callback`;
};

const getGithubPagesBasePath = url => {
  if (!url.hostname.endsWith('.github.io')) {
    return '';
  }
  const firstSegment = url.pathname.split('/').find(Boolean);
  if (!firstSegment) {
    return '';
  }
  return `/${firstSegment}`;
};
const getWebRedirectUri = async () => {
  const href = await getCurrentHref();
  if (!href) {
    return '';
  }
  try {
    const url = new URL(href);
    return `${url.origin}${getGithubPagesBasePath(url)}/auth/callback`;
  } catch {
    return '';
  }
};
const getEffectiveRedirectUri = async (platform, uid, redirectUri) => {
  if (redirectUri) {
    return redirectUri;
  }
  if (platform === Electron) {
    return getElectronRedirectUri(uid);
  }
  return getWebRedirectUri();
};

const nativeOidcClientId = 'lvce-editor-native';
const webOidcClientId = 'lvce-editor-web';
const oidcScope = 'openid offline_access profile email';
const getOidcClientId = platform => {
  return platform === Electron ? nativeOidcClientId : webOidcClientId;
};

const getBackendLoginRequest = async (backendUrl, platform = 0, uid = 0, redirectUri = '', createPkceValuesFn = createPkceValues, createRandomUuid = () => globalThis.crypto.randomUUID()) => {
  const effectiveRedirectUri = await getEffectiveRedirectUri(platform, uid, redirectUri);
  const {
    codeChallenge,
    codeVerifier
  } = await createPkceValuesFn();
  const clientId = getOidcClientId(platform);
  const nonce = createRandomUuid();
  const state = createRandomUuid();
  const loginUrl = new URL(getBackendAuthUrl(backendUrl, '/oidc/auth'));
  loginUrl.searchParams.set('client_id', clientId);
  loginUrl.searchParams.set('code_challenge', codeChallenge);
  loginUrl.searchParams.set('code_challenge_method', 'S256');
  loginUrl.searchParams.set('nonce', nonce);
  loginUrl.searchParams.set('prompt', 'consent');
  loginUrl.searchParams.set('response_type', 'code');
  loginUrl.searchParams.set('scope', oidcScope);
  loginUrl.searchParams.set('state', state);
  if (effectiveRedirectUri) {
    loginUrl.searchParams.set('redirect_uri', effectiveRedirectUri);
  }
  return {
    clientId,
    codeVerifier,
    loginUrl: loginUrl.toString(),
    nonce,
    redirectUri: effectiveRedirectUri,
    state
  };
};

const getLoggedInState = (state, response) => {
  const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
  const refreshToken = typeof response.refreshToken === 'string' ? response.refreshToken : '';
  return {
    ...state,
    authAccessToken: accessToken,
    authErrorMessage: '',
    authRefreshToken: refreshToken,
    userName: typeof response.userName === 'string' ? response.userName : state.userName,
    userState: accessToken ? 'loggedIn' : 'loggedOut',
    userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
    userSubscriptionStatus: typeof response.subscriptionStatus === 'string' ? response.subscriptionStatus : state.userSubscriptionStatus,
    userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
  };
};

const isLoginResponse = value => {
  return !!value && typeof value === 'object';
};

const exchangeElectronAuthorizationCode = async (backendUrl, code, redirectUri, codeVerifier, requestTokenEndpoint = genericTokenEndpointRequest, processTokenEndpointResponse = processGenericTokenEndpointResponse) => {
  return exchangeAuthorizationCode(backendUrl, nativeOidcClientId, code, redirectUri, codeVerifier, requestTokenEndpoint, processTokenEndpointResponse);
};

const hasAuthorizationCode = value => {
  return typeof value === 'string' && value.length > 0;
};
const getElectronAuthorizationCode = async uid => {
  return invoke$2('OAuthServer.getCode', String(uid));
};
const waitForElectronBackendLogin = async (backendUrl, uid, redirectUri, codeVerifier, timeoutMs = 30_000, pollIntervalMs = 1000, getAuthorizationCode = getElectronAuthorizationCode, exchangeAuthorizationCode = exchangeElectronAuthorizationCode) => {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const authorizationCode = await getAuthorizationCode(uid);
    if (hasAuthorizationCode(authorizationCode)) {
      const tokenResponse = await exchangeAuthorizationCode(backendUrl, authorizationCode, redirectUri, codeVerifier);
      return {
        authAccessToken: tokenResponse.accessToken,
        authErrorMessage: '',
        authRefreshToken: tokenResponse.refreshToken,
        userState: tokenResponse.accessToken ? 'loggedIn' : 'loggedOut'
      };
    }
    await delay(pollIntervalMs);
  }
  return getLoggedOutBackendAuthState('Timed out waiting for backend login.');
};

const getMockLoginResult = async signingInState => {
  if (!hasPendingMockLoginResponse()) {
    return undefined;
  }
  const response = await consumeNextLoginResponse();
  if (!isLoginResponse(response)) {
    return {
      authErrorMessage: 'Backend returned an invalid login response.',
      userState: 'loggedOut'
    };
  }
  if (typeof response.error === 'string' && response.error) {
    return {
      authErrorMessage: response.error,
      userState: 'loggedOut'
    };
  }
  return persistLoginResult(getLoggedInState(signingInState, response));
};
const getInteractiveLoginResult = async (backendUrl, platform, authUseRedirect, signingInState) => {
  const uid = 0;
  const {
    clientId,
    codeVerifier,
    loginUrl,
    redirectUri,
    state
  } = await getBackendLoginRequest(backendUrl, platform, uid);
  if (platform !== Electron) {
    await savePendingOidcAuthState({
      clientId,
      codeVerifier,
      redirectUri,
      state
    });
  }
  await invoke$1('Open.openUrl', loginUrl, platform, authUseRedirect);
  if (platform !== Electron && authUseRedirect) {
    return signingInState;
  }
  const authState = platform === Electron ? await waitForElectronBackendLogin(backendUrl, uid, redirectUri, codeVerifier) : await waitForBackendLogin(backendUrl);
  if (platform !== Electron) {
    await clearPendingOidcAuthState();
  }
  return persistLoginResult({
    ...authState,
    authClientId: clientId
  });
};
const handleClickLogin = async options => {
  const {
    authUseRedirect,
    backendUrl,
    platform
  } = options;
  if (!backendUrl) {
    return {
      authErrorMessage: 'Backend URL is missing.',
      userState: 'loggedOut'
    };
  }
  const signingInState = {
    authErrorMessage: '',
    userState: 'loggingIn'
  };
  try {
    const mockLoginResult = await getMockLoginResult(signingInState);
    if (mockLoginResult) {
      return mockLoginResult;
    }
    return getInteractiveLoginResult(backendUrl, platform, authUseRedirect, signingInState);
  } catch (error) {
    await clearPendingOidcAuthState();
    const errorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
    return {
      ...signingInState,
      ...getLoggedOutBackendAuthState(errorMessage)
    };
  }
};

const logout = async state => {
  const loggingOutState = {
    authErrorMessage: '',
    userState: 'loggingOut'
  };
  await logoutFromBackend(state.backendUrl);
  await Promise.all([clearOidcCallbackUrl(), clearPendingOidcAuthState(), clearPersistedAuthSession()]);
  return {
    ...loggingOutState,
    ...getLoggedOutBackendAuthState()
  };
};

const commandMap = {
  'Auth.clearMocks': clear,
  'Auth.consumeNextLoginResponse': consumeNextLoginResponse,
  'Auth.consumeNextRefreshResponse': consumeNextRefreshResponse,
  'Auth.hasPendingMockLoginResponse': hasPendingMockLoginResponse,
  'Auth.hasPendingMockRefreshResponse': hasPendingMockRefreshResponse,
  'Auth.initialize': initialize,
  'Auth.login': handleClickLogin,
  'Auth.logout': logout,
  'Auth.setNextLoginResponse': setNextLoginResponse,
  'Auth.setNextRefreshResponse': setNextRefreshResponse,
  'Auth.syncBackendAuth': syncBackendAuth,
  'HandleMessagePort.handleMessagePort': handleMessagePort,
  initialize: (_, port) => handleMessagePort(port, RendererWorker)
};

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

const listen = async () => {
  await Promise.all([initializeRendererWorker(), initializeOpenerWorker(), initializeAuthProcess()]);
};

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

main();
