text: export function supported() {
    return Boolean(
      navigator.credentials &&
        navigator.credentials.create &&
        navigator.credentials.get &&
        window.PublicKeyCredential,
    );
  }
  
  export function isHTTPS() {
    return window.location.protocol.startsWith('https');
  }
  
  export const FLOW_AUTHENTICATE = 'authenticate';
  export const FLOW_REGISTER = 'register';
  
  /**
   * Converts a base64 string to an ArrayBuffer
   *
   * @param {String} str - A base64 encoded string
   * @returns {ArrayBuffer}
   */
  export const base64ToBuffer = (str) => {
    const rawStr = atob(str);
    const buffer = new ArrayBuffer(rawStr.length);
    const arr = new Uint8Array(buffer);
    for (let i = 0; i < rawStr.length; i += 1) {
      arr[i] = rawStr.charCodeAt(i);
    }
    return arr.buffer;
  };
  
  /**
   * Converts ArrayBuffer to a base64-encoded string
   *
   * @param {ArrayBuffer, String} str -
   * @returns {String} - ArrayBuffer to a base64-encoded string.
   * When input is a string, returns the input as-is.
   */
  export const bufferToBase64 = (input) => {
    if (typeof input === 'string') {
      return input;
    }
    const arr = new Uint8Array(input);
    return btoa(String.fromCharCode(...arr));
  };
  
  /**
   * Return a URL-safe base64 string.
   *
   * RFC: https://datatracker.ietf.org/doc/html/rfc4648#section-5
   * @param {String} base64Str
   * @returns {String}
   */
  export const base64ToBase64Url = (base64Str) => {
    return base64Str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
  };
  
  /**
   * Returns a copy of the given object with the id property converted to buffer
   *
   * @param {Object} param
   */
  function convertIdToBuffer({ id, ...rest }) {
    return {
      ...rest,
      id: base64ToBuffer(id),
    };
  }
  
  /**
   * Returns a copy of the given array with all `id`s of the items converted to buffer
   *
   * @param {Array} items
   */
  function convertIdsToBuffer(items) {
    return items.map(convertIdToBuffer);
  }
  
  /**
   * Returns an object with keys of the given props, and values from the given object converted to base64
   *
   * @param {String} obj
   * @param {Array} props
   */
  function convertPropertiesToBase64(obj, props) {
    return props.reduce(
      (acc, property) => Object.assign(acc, { [property]: bufferToBase64(obj[property]) }),
      {},
    );
  }
  
  export function convertGetParams({ allowCredentials, challenge, ...rest }) {
    return {
      ...rest,
      ...(allowCredentials ? { allowCredentials: convertIdsToBuffer(allowCredentials) } : {}),
      challenge: base64ToBuffer(challenge),
    };
  }
  
  export function convertGetResponse(webauthnResponse) {
    return {
      type: webauthnResponse.type,
      id: webauthnResponse.id,
      rawId: bufferToBase64(webauthnResponse.rawId),
      response: convertPropertiesToBase64(webauthnResponse.response, [
        'clientDataJSON',
        'authenticatorData',
        'signature',
        'userHandle',
      ]),
      clientExtensionResults: webauthnResponse.getClientExtensionResults(),
    };
  }
  
  export function convertCreateParams({ challenge, user, excludeCredentials, ...rest }) {
    return {
      ...rest,
      challenge: base64ToBuffer(challenge),
      user: convertIdToBuffer(user),
      ...(excludeCredentials ? { excludeCredentials: convertIdsToBuffer(excludeCredentials) } : {}),
    };
  }
  
  export function convertCreateResponse(webauthnResponse) {
    return {
      type: webauthnResponse.type,
      id: webauthnResponse.id,
      rawId: bufferToBase64(webauthnResponse.rawId),
      clientExtensionResults: webauthnResponse.getClientExtensionResults(),
      response: convertPropertiesToBase64(webauthnResponse.response, [
        'clientDataJSON',
        'attestationObject',
      ]),
    };
  }
  