{"version":3,"sources":["../../../src/api/api-provider.tsx"],"sourcesContent":["import { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\nimport { useIsHydrated } from \"../lib/use-is-hydrated.js\";\nimport { getClaims } from \"./utils.js\";\n\nexport type WidgetType =\n  | \"admin-portal-audit-log-streaming\"\n  | \"admin-portal-domain-verification\"\n  | \"user-management\"\n  | \"organization-switcher\"\n  | \"user-sessions\"\n  | \"user-security\"\n  | \"user-profile\"\n  | \"admin-portal-sso-connection\"\n  | \"api-keys\"\n  | \"pipes\"\n  | \"directory-sync\";\n\nexport type AuthToken = string | (() => Promise<string>);\n\ninterface ElevatedAccess {\n  token: string;\n  expiresAt: string;\n}\n\ninterface ApiConfig {\n  authToken: string | undefined;\n  permissions: string[] | undefined;\n  exp: number | undefined;\n  baseUrl: string;\n  widgetType: WidgetType;\n  elevatedAccess?: ElevatedAccess;\n  setElevatedAccess: (elevatedAccess?: ElevatedAccess) => void;\n}\n\nexport interface AuthTokenQueryData {\n  authToken: string;\n  permissions: string[] | undefined;\n  exp: number | undefined;\n}\n\nexport const authTokenQueryKey = (widgetType: WidgetType) =>\n  [\"authToken\", widgetType] as const;\n\n// Refetch this many ms before the JWT expires so data queries always see a\n// fresh token.\nconst TOKEN_REFRESH_BUFFER_MS = 30_000;\n\n// Cap setTimeout delays at ~24 days to stay well below the 32-bit signed int\n// limit (~24.8 days), which some runtimes silently coerce to 1ms.\nconst MAX_TIMEOUT_MS = 2_147_483_000;\n\nconst ApiContext = React.createContext<ApiConfig | undefined>(undefined);\n\ninterface ApiProviderProps {\n  authToken: AuthToken;\n  baseUrl: string;\n  children: React.ReactNode;\n  widgetType: WidgetType;\n}\n\nexport const ApiProvider = ({\n  authToken,\n  baseUrl,\n  children,\n  widgetType,\n}: ApiProviderProps) => {\n  const queryClient = useQueryClient();\n  const authTokenQuery = useQuery<AuthTokenQueryData>({\n    initialData: () => {\n      if (typeof authToken !== \"string\") {\n        return undefined;\n      }\n\n      const claims = getClaims(authToken);\n      return {\n        authToken,\n        permissions: claims.permissions,\n        exp: claims.exp,\n      };\n    },\n    queryFn: async () => {\n      const resolvedToken =\n        typeof authToken === \"string\" ? authToken : await authToken();\n      const claims = getClaims(resolvedToken);\n\n      return {\n        authToken: resolvedToken,\n        permissions: claims.permissions,\n        exp: claims.exp,\n      };\n    },\n    queryKey: authTokenQueryKey(widgetType),\n  });\n\n  // Keep the token \"fresh\" until ~30s before it expires, so window-focus\n  // re-fetches re-mint before data queries use a stale Bearer.\n  //\n  // TODO: prefer the function-form `staleTime` on `useQuery` above once we can\n  // bump the `@tanstack/react-query` peer dep to ^5.44.0 (the version that\n  // introduced it). That lets react-query own the freshness window and avoids\n  // the extra timer + window-focus nonsense this effect introduces.\n  const exp = authTokenQuery.data?.exp;\n  React.useEffect(() => {\n    if (!exp) {\n      return;\n    }\n\n    const delay = Math.min(\n      MAX_TIMEOUT_MS,\n      exp * 1000 - Date.now() - TOKEN_REFRESH_BUFFER_MS,\n    );\n    if (delay <= 0) {\n      void queryClient.refetchQueries({\n        queryKey: authTokenQueryKey(widgetType),\n      });\n      return;\n    }\n    const timeout = window.setTimeout(() => {\n      void queryClient.refetchQueries({\n        queryKey: authTokenQueryKey(widgetType),\n      });\n    }, delay);\n    return () => window.clearTimeout(timeout);\n  }, [exp, queryClient, widgetType]);\n\n  const [elevatedAccess, setElevatedAccess] = React.useState<ElevatedAccess>();\n  const elevatedAccessTimeout = React.useRef<number | undefined>(undefined);\n\n  // This effect manages the expiration of elevated access tokens\n  // When an elevated access token is present, it checks every 30 seconds if the token has expired\n  // If the token has expired (current time > expiration time), it clears the elevated access\n  React.useEffect(() => {\n    if (elevatedAccessTimeout.current) {\n      window.clearInterval(elevatedAccessTimeout.current);\n    }\n\n    if (elevatedAccess) {\n      elevatedAccessTimeout.current = window.setInterval(() => {\n        const now = new Date();\n        const expiresAtDate = new Date(elevatedAccess.expiresAt);\n\n        //  Reset the elevated access if it has expired\n        if (now > expiresAtDate) {\n          setElevatedAccess(undefined);\n        }\n      }, 30_000); // every 30 seconds\n    }\n\n    return () => {\n      if (elevatedAccessTimeout.current) {\n        window.clearInterval(elevatedAccessTimeout.current);\n      }\n    };\n  }, [elevatedAccess]);\n\n  const value = React.useMemo(\n    () => ({\n      authToken: authTokenQuery.data?.authToken,\n      permissions: authTokenQuery.data?.permissions,\n      exp: authTokenQuery.data?.exp,\n      baseUrl,\n      elevatedAccess,\n      setElevatedAccess,\n      widgetType,\n    }),\n    [\n      authTokenQuery.data,\n      baseUrl,\n      elevatedAccess,\n      setElevatedAccess,\n      widgetType,\n    ],\n  );\n\n  return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;\n};\n\nexport const useApi = () => {\n  const context = React.useContext(ApiContext);\n\n  if (context === undefined) {\n    throw new Error(\"useApi must be used within an ApiProvider\");\n  }\n\n  return context;\n};\n\nexport const useElevatedAccessToken = () => {\n  const { elevatedAccess, setElevatedAccess } = useApi();\n\n  return { elevatedAccess, setElevatedAccess };\n};\n\nexport const useApiReady = () => {\n  const { authToken } = useApi();\n  const isHydrated = useIsHydrated();\n\n  return isHydrated && authToken !== undefined;\n};\n"],"mappings":"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","names":[]}