Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import { useEffect, useState } from "react"
import { z } from "zod"
const CACHE_DURATION_MS = 30 * 1000 // 30 seconds
const OpenRouterBaseURL = "https://openrouter.ai/api/v1"
// Define schema for OpenRouter key response
const openRouterKeyInfoSchema = z.object({
data: z.object({
label: z.string().nullable(),
usage: z.number(),
is_free_tier: z.boolean(),
is_provisioning_key: z.boolean(),
rate_limit: z.object({
requests: z.number(),
interval: z.string(),
}),
limit: z.number().nullable(),
}),
})
export type OpenRouterKeyInfo = z.infer<typeof openRouterKeyInfoSchema>["data"]
// Module-level cache variables
let moduleCachedData: OpenRouterKeyInfo | null = null
let moduleLastFetchTime: number | null = null
async function getOpenRouterKeyInfo(apiKey: string, signal: AbortSignal): Promise<OpenRouterKeyInfo | null> {
try {
const keyEndpoint = `${OpenRouterBaseURL}/key`
const response = await fetch(keyEndpoint, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
signal,
})
if (!response.ok) {
if (response.status === 401) {
console.warn("OpenRouter API key is invalid or unauthorized.")
} else {
console.error(`Error fetching OpenRouter key info: HTTP ${response.status}`)
}
return null
}
const responseData = await response.json()
const result = openRouterKeyInfoSchema.safeParse(responseData)
if (!result.success) {
console.error("OpenRouter API key info validation failed:", result.error.flatten().fieldErrors)
return null
}
return result.data.data
} catch (error: any) {
if (error.name !== "AbortError") {
console.error("Error fetching OpenRouter key info:", error)
}
return null
}
}
/**
* Custom hook to fetch OpenRouter API key information.
* Implements stale-while-revalidate caching using module-level variables.
* @param apiKey The OpenRouter API key.
* @returns An object containing the key info data, loading state, and error state.
*/
export const useOpenRouterKeyInfo = (apiKey?: string) => {
// State reflects the currently displayed data, initialized from cache
const [data, setData] = useState<OpenRouterKeyInfo | null>(moduleCachedData)
// Loading is true only if there's no initial cache and a key is provided
const [isLoading, setIsLoading] = useState<boolean>(!moduleCachedData && !!apiKey)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
const controller = new AbortController()
const signal = controller.signal
if (!apiKey) {
// Clear state and cache if API key is removed
setData(null)
moduleCachedData = null
moduleLastFetchTime = null
setIsLoading(false)
setError(null)
return () => controller.abort()
}
const now = Date.now()
const cacheAge = moduleLastFetchTime ? now - moduleLastFetchTime : Infinity
const isCacheStale = cacheAge >= CACHE_DURATION_MS
const hasCache = !!moduleCachedData
// Use cached data immediately if available
if (hasCache) {
// Ensure local state matches module cache if it hasn't updated yet
// This handles cases where the hook re-renders before the effect runs
if (data !== moduleCachedData) {
setData(moduleCachedData)
}
setIsLoading(false)
} else {
setIsLoading(true)
setError(null)
}
// Fetch if cache is stale or doesn't exist
if (isCacheStale || !hasCache) {
const isBackgroundFetch = hasCache && isCacheStale // Fetching while showing stale data
// Don't set loading true for background fetches
if (!isBackgroundFetch) {
setIsLoading(true)
}
getOpenRouterKeyInfo(apiKey, signal)
.then((result) => {
if (!signal.aborted) {
moduleCachedData = result // Update module cache
moduleLastFetchTime = Date.now() // Update module fetch time
setData(result) // Update state
setError(null) // Clear error on success
}
})
.catch((err) => {
if (!signal.aborted) {
console.error("[useOpenRouterKeyInfo] Fetch error:", err)
setError(err instanceof Error ? err : new Error("An unknown error occurred"))
if (!isBackgroundFetch) {
setData(null)
moduleCachedData = null
moduleLastFetchTime = null
}
}
})
.finally(() => {
if (!signal.aborted) {
setIsLoading(false) // Stop loading indicator
}
})
}
return () => {
controller.abort()
}
}, [apiKey]) // Re-run effect only when apiKey changes
return { data, isLoading, error }
}
|