{"version":3,"file":"LoginWithAuth0-D92HnzHq.mjs","names":[],"sources":["../src/lib/AutoReconnectingWebsocket.ts","../src/lib/availableStorage.ts","../src/lib/errorHandling.ts","../src/lib/LoginWithAuth0.ts"],"sourcesContent":["import ReconnectingWebSocket, { type ErrorEvent } from \"reconnecting-websocket\"\nimport type { MockNovaInstance } from \"./mock/MockNovaInstance.ts\"\n\nexport class AutoReconnectingWebsocket extends ReconnectingWebSocket {\n  receivedFirstMessage?: MessageEvent\n  targetUrl: string\n  disposed = false\n  readonly opts: {\n    mock?: MockNovaInstance\n    onDispose?: () => void\n  }\n\n  constructor(\n    targetUrl: string,\n    opts: {\n      mock?: MockNovaInstance\n      onDispose?: () => void\n    } = {},\n  ) {\n    console.log(\"Opening websocket to\", targetUrl)\n\n    super(() => this.targetUrl || targetUrl, undefined, {\n      startClosed: true,\n    })\n\n    // Reconnecting websocket doesn't set this properly with startClosed\n    Object.defineProperty(this, \"url\", {\n      get() {\n        return this.targetUrl\n      },\n    })\n\n    this.targetUrl = targetUrl\n    this.opts = opts\n\n    this.addEventListener(\"open\", () => {\n      console.log(`Websocket to ${this.url} opened`)\n    })\n\n    this.addEventListener(\"message\", (ev) => {\n      if (!this.receivedFirstMessage) {\n        this.receivedFirstMessage = ev\n      }\n    })\n\n    this.addEventListener(\"close\", () => {\n      console.log(`Websocket to ${this.url} closed`)\n    })\n\n    const origReconnect = this.reconnect\n    this.reconnect = () => {\n      if (this.opts.mock) {\n        this.opts.mock.handleWebsocketConnection(this)\n      } else {\n        origReconnect.apply(this)\n      }\n    }\n\n    this.reconnect()\n  }\n\n  changeUrl(targetUrl: string) {\n    this.receivedFirstMessage = undefined\n    this.targetUrl = targetUrl\n    this.reconnect()\n  }\n\n  sendJson(data: unknown) {\n    if (this.opts.mock) {\n      this.opts.mock.handleWebsocketMessage(this, JSON.stringify(data))\n    } else {\n      this.send(JSON.stringify(data))\n    }\n  }\n\n  /**\n   * Permanently close this websocket and indicate that\n   * this object should not be used again.\n   **/\n  dispose() {\n    this.close()\n    this.disposed = true\n    if (this.opts.onDispose) {\n      this.opts.onDispose()\n    }\n  }\n\n  /**\n   * Returns a promise that resolves once the websocket\n   * is in the OPEN state. */\n  async opened() {\n    return new Promise<void>((resolve, reject) => {\n      if (this.readyState === WebSocket.OPEN) {\n        resolve()\n      } else {\n        this.addEventListener(\"open\", () => resolve())\n        this.addEventListener(\"error\", reject)\n      }\n    })\n  }\n\n  /**\n   * Returns a promise that resolves once the websocket\n   * is in the CLOSED state. */\n  async closed() {\n    return new Promise<void>((resolve, reject) => {\n      if (this.readyState === WebSocket.CLOSED) {\n        resolve()\n      } else {\n        this.addEventListener(\"close\", () => resolve())\n        this.addEventListener(\"error\", reject)\n      }\n    })\n  }\n\n  /**\n   * Returns a promise that resolves when the first message\n   * is received from the websocket. Resolves immediately if\n   * the first message has already been received.\n   */\n  async firstMessage() {\n    if (this.receivedFirstMessage) {\n      return this.receivedFirstMessage\n    }\n\n    return new Promise<MessageEvent>((resolve, reject) => {\n      const onMessage = (ev: MessageEvent) => {\n        this.receivedFirstMessage = ev\n        this.removeEventListener(\"message\", onMessage)\n        this.removeEventListener(\"error\", onError)\n        resolve(ev)\n      }\n\n      const onError = (ev: ErrorEvent) => {\n        this.removeEventListener(\"message\", onMessage)\n        this.removeEventListener(\"error\", onError)\n        reject(ev)\n      }\n\n      this.addEventListener(\"message\", onMessage)\n      this.addEventListener(\"error\", onError)\n    })\n  }\n\n  /**\n   * Returns a promise that resolves when the next message\n   * is received from the websocket.\n   */\n  async nextMessage() {\n    return new Promise<MessageEvent>((resolve, reject) => {\n      const onMessage = (ev: MessageEvent) => {\n        this.removeEventListener(\"message\", onMessage)\n        this.removeEventListener(\"error\", onError)\n        resolve(ev)\n      }\n\n      const onError = (ev: ErrorEvent) => {\n        this.removeEventListener(\"message\", onMessage)\n        this.removeEventListener(\"error\", onError)\n        reject(ev)\n      }\n\n      this.addEventListener(\"message\", onMessage)\n      this.addEventListener(\"error\", onError)\n    })\n  }\n}\n","/**\n * Safety wrapper around browser localStorage providing context availability\n * checks and JSON parsing\n */\nclass AvailableStorage {\n  available = typeof window !== \"undefined\" && !!window.localStorage\n\n  getJSON<T>(key: string): Partial<T> | null {\n    if (!this.available) return null\n\n    const result = window.localStorage.getItem(key)\n    if (result === null) return null\n\n    try {\n      return JSON.parse(result)\n    } catch (err) {\n      return null\n    }\n  }\n\n  setJSON(key: string, obj: unknown) {\n    if (!this.available) return null\n\n    window.localStorage.setItem(key, JSON.stringify(obj))\n  }\n\n  delete(key: string) {\n    if (!this.available) return null\n\n    window.localStorage.removeItem(key)\n  }\n\n  setString(key: string, value: string) {\n    if (!this.available) return null\n\n    window.localStorage.setItem(key, value)\n  }\n\n  getString(key: string): string | null {\n    if (!this.available) return null\n\n    return window.localStorage.getItem(key)\n  }\n}\n\nexport const availableStorage = new AvailableStorage()\n","import { isAxiosError } from \"axios\"\nimport { tryStringifyJson } from \"./converters.ts\"\n\nexport function delay(ms: number) {\n  return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Attempts to make a helpful error message from an unknown thrown error\n * or promise rejection.\n *\n * This function is mainly to aid debugging and good bug reports. For\n * expected errors encountered by end users, it's more ideal to catch\n * the specific error code and provide a localized app-specific error message.\n */\nexport function makeErrorMessage(err: unknown): string {\n  if (isAxiosError(err)) {\n    if (err.response) {\n      return `${err.response?.status} ${err.response?.statusText} from ${err.response?.config.method?.toUpperCase() || \"accessing\"} ${err.response?.config.url}: ${JSON.stringify(err.response?.data)}`\n    } else if (err.config) {\n      if (err.code === \"ERR_NETWORK\") {\n        return `${err.message} from ${err.config.method?.toUpperCase() || \"accessing\"} ${err.config.url}. This error can happen because of either connection issues or server CORS policy.`\n      } else {\n        return `${err.message} from ${err.config.method?.toUpperCase() || \"accessing\"} ${err.config.url}`\n      }\n    }\n  } else if (err instanceof Error) {\n    return err.message\n  } else if (typeof err === \"string\") {\n    return err\n  } else if (typeof err === \"object\") {\n    return tryStringifyJson(err) || `Unserializable object ${err}`\n  }\n\n  return `${err}`\n}\n\n/**\n * Reloads the page with protection against loops.\n * Allows at most one reload per 10 seconds; otherwise throws\n * an error.\n */\nexport function guardedPageReload(key: string): Promise<never> {\n  const RELOAD_KEY = `novajs_reload_guard:${key}`\n  const RELOAD_COOLDOWN_MS = 10_000\n  const lastReloadAt = Number(window.sessionStorage.getItem(RELOAD_KEY) ?? \"0\")\n\n  if (Date.now() - lastReloadAt < RELOAD_COOLDOWN_MS) {\n    throw new Error(\n      `Unhandled error caused a reload (${key}), but a reload was already attempted recently. Aborting to prevent reload loop.`,\n    )\n  }\n\n  window.sessionStorage.setItem(RELOAD_KEY, String(Date.now()))\n  window.location.reload()\n\n  return new Promise(() => {}) // never settles, the page should be reloading\n}\n","import { guardedPageReload } from \"./errorHandling.ts\"\n\n/**\n * Mapping of stages to Auth0 configurations.\n * The client ids are public identifiers for a specific auth0 application\n * and are safe to include in client-side code.\n * https://auth0.com/docs/get-started/applications/application-settings\n */\nconst auth0ConfigMap = {\n  dev: {\n    domain: `https://auth.portal.dev.wandelbots.io`,\n    clientId: \"fLbJD0RLp5r2Dpucm5j8BjwMR6Hunfha\",\n  },\n  stg: {\n    domain: `https://auth.portal.stg.wandelbots.io`,\n    clientId: \"joVDeD9e786WzFNSGCqoVq7HNkWt5j6s\",\n  },\n  prod: {\n    domain: `https://auth.portal.wandelbots.io`,\n    clientId: \"J7WJUi38xVQdJAEBNRT9Xw1b0fXDb4J2\",\n  },\n}\n\n/** Determine which Auth0 configuration to use based on instance URL */\nexport const getAuth0Config = (instanceUrl: URL) => {\n  if (instanceUrl.host.endsWith(\".dev.wandelbots.io\")) return auth0ConfigMap.dev\n  if (instanceUrl.host.endsWith(\".stg.wandelbots.io\")) return auth0ConfigMap.stg\n  if (instanceUrl.host.endsWith(\".wandelbots.io\")) return auth0ConfigMap.prod\n  throw new Error(\n    `Unable to authenticate with NOVA instance \"${instanceUrl}\". Auth0 login is only supported for cloud instances with hosts of the form \"**.wandelbots.io\".`,\n  )\n}\n\n/**\n * Initializes Auth0 login process using redirect if necessary and retrieves an access token.\n * Returns null when an access token should not be needed to authenticate (i.e. cookie auth\n * when deployed on the instance domain)\n */\nexport const loginWithAuth0 = async (\n  instanceUrl: URL,\n): Promise<string | null> => {\n  if (typeof window === \"undefined\") {\n    throw new Error(\n      `Access token must be set to use Nova when not in a browser environment.`,\n    )\n  }\n\n  if (instanceUrl.origin === window.location.origin) {\n    // When deployed on the instance itself, our auth is handled by cookies\n    // and no access token is needed-- just need to reload the page and it'll\n    // login again / set cookie as needed\n    return guardedPageReload(\"cloud_instance_auth\")\n  }\n\n  // If we're on localhost or another domain, we need to do the full oauth flow\n  // Note this will ONLY work for origins which are whitelisted as a redirect_uri\n  // in the auth0 config, currently\n  const { Auth0Client } = await import(\"@auth0/auth0-spa-js\")\n\n  const auth0Config = getAuth0Config(instanceUrl)\n\n  const auth0Client = new Auth0Client({\n    domain: auth0Config.domain,\n    clientId: auth0Config.clientId ?? \"\",\n    useRefreshTokens: false,\n    authorizationParams: {\n      audience: \"nova-api\",\n      redirect_uri: window.location.origin,\n    },\n  })\n\n  // If the URL includes a redirect result, handle it\n  if (\n    window.location.search.includes(\"code=\") &&\n    window.location.search.includes(\"state=\")\n  ) {\n    const { appState } = await auth0Client.handleRedirectCallback()\n    // Return to the URL the user was originally on before the redirect\n    window.history.replaceState(\n      {},\n      document.title,\n      appState?.returnTo || window.location.pathname,\n    )\n  } else {\n    // Initiate login with redirect\n    await auth0Client.loginWithRedirect()\n  }\n\n  // Once logged in, retrieve the access token silently\n  const accessToken = await auth0Client.getTokenSilently()\n  return accessToken\n}\n"],"mappings":"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"}