{"version":3,"sources":["../../src/react/index.ts","../../src/react/hooks.ts","../../src/types.ts","../../src/react/contexts.ts","../../src/react/PolliProvider.tsx","../../src/react/oauth.ts","../../src/react/storage.ts"],"sourcesContent":["export type {\n    AuthActionsValue,\n    AuthContextValue,\n    AuthStateValue,\n} from \"./contexts.js\";\nexport {\n    type AccountResourceValue,\n    type UseAccountBalanceValue,\n    type UseAccountKeyUsageOptions,\n    type UseAccountKeyUsageValue,\n    type UseAccountKeyValue,\n    type UseAccountProfileValue,\n    useAccountBalance,\n    useAccountKey,\n    useAccountKeyUsage,\n    useAccountProfile,\n    useAuth,\n    useAuthActions,\n    useAuthState,\n} from \"./hooks.js\";\nexport {\n    DEFAULT_ENTER_URL,\n    PolliProvider,\n    type PolliProviderProps,\n} from \"./PolliProvider.js\";\nexport type { StorageAdapter, StorageOption } from \"./storage.js\";\n","import {\n    useCallback,\n    useContext,\n    useEffect,\n    useMemo,\n    useRef,\n    useState,\n} from \"react\";\nimport type {\n    AccountBalance,\n    AccountProfile,\n    KeyInfo,\n    KeyUsageOptions,\n    UsageResponse,\n} from \"../types.js\";\nimport { PollinationsError } from \"../types.js\";\nimport {\n    type AuthActionsValue,\n    AuthContext,\n    type AuthContextValue,\n    type AuthStateValue,\n} from \"./contexts.js\";\n\nexport interface AccountResourceValue<T> {\n    data: T | null;\n    isLoading: boolean;\n    error: Error | null;\n    refresh: () => Promise<void>;\n}\n\nexport interface UseAccountKeyUsageOptions extends KeyUsageOptions {\n    enabled?: boolean;\n}\n\nexport type UseAccountProfileValue = AccountResourceValue<AccountProfile>;\nexport type UseAccountBalanceValue = AccountResourceValue<AccountBalance>;\nexport type UseAccountKeyValue = AccountResourceValue<KeyInfo>;\nexport type UseAccountKeyUsageValue = AccountResourceValue<UsageResponse>;\n\ntype AccountFetcher<T> = (apiBaseUrl: string, apiKey: string) => Promise<T>;\n\nfunction useRequiredAuth(): AuthContextValue {\n    const ctx = useContext(AuthContext);\n    if (!ctx) {\n        throw new Error(\n            \"Pollinations auth hooks must be used within a <PolliProvider>\",\n        );\n    }\n    return ctx;\n}\n\n/** Current auth session state only. Does not fetch account data. */\nexport function useAuthState(): AuthStateValue {\n    const { apiKey, isLoggedIn, isHydrated, error } = useRequiredAuth();\n    return useMemo(\n        () => ({ apiKey, isLoggedIn, isHydrated, error }),\n        [apiKey, isLoggedIn, isHydrated, error],\n    );\n}\n\n/** Stable login/logout refs and provider config. */\nexport function useAuthActions(): AuthActionsValue {\n    const { login, logout, setApiKey, enterUrl, apiBaseUrl } =\n        useRequiredAuth();\n    return useMemo(\n        () => ({ login, logout, setApiKey, enterUrl, apiBaseUrl }),\n        [login, logout, setApiKey, enterUrl, apiBaseUrl],\n    );\n}\n\n/** Combined thin auth hook. Account data is available through opt-in hooks. */\nexport function useAuth(): AuthContextValue {\n    return useRequiredAuth();\n}\n\nfunction accountUrl(\n    apiBaseUrl: string,\n    path: string,\n    params?: URLSearchParams,\n): string {\n    const qs = params?.toString();\n    return `${apiBaseUrl.replace(/\\/+$/, \"\")}${path}${qs ? `?${qs}` : \"\"}`;\n}\n\nasync function pollinationsErrorFromResponse(\n    response: Response,\n): Promise<PollinationsError> {\n    let payload: unknown;\n    try {\n        payload = await response.json();\n    } catch {\n        payload = null;\n    }\n\n    const record =\n        payload && typeof payload === \"object\"\n            ? (payload as Record<string, unknown>)\n            : {};\n    const nested =\n        record.error && typeof record.error === \"object\"\n            ? (record.error as Record<string, unknown>)\n            : record;\n\n    const message =\n        typeof nested.message === \"string\"\n            ? nested.message\n            : response.statusText || `Request failed with ${response.status}`;\n    const code =\n        typeof nested.code === \"string\"\n            ? nested.code\n            : response.status === 401\n              ? \"UNAUTHORIZED\"\n              : \"HTTP_ERROR\";\n    const details =\n        nested.details && typeof nested.details === \"object\"\n            ? (nested.details as Record<string, unknown>)\n            : undefined;\n    const requestId =\n        typeof nested.requestId === \"string\" ? nested.requestId : undefined;\n    const retryAfterHeader = response.headers.get(\"retry-after\");\n    const retryAfter = retryAfterHeader\n        ? Number.parseInt(retryAfterHeader, 10)\n        : undefined;\n\n    return new PollinationsError(\n        message,\n        code,\n        response.status,\n        details,\n        requestId,\n        Number.isFinite(retryAfter) ? retryAfter : undefined,\n    );\n}\n\nasync function fetchAccountJson<T>(\n    apiBaseUrl: string,\n    apiKey: string,\n    path: string,\n    params?: URLSearchParams,\n): Promise<T> {\n    const response = await fetch(accountUrl(apiBaseUrl, path, params), {\n        headers: { Authorization: `Bearer ${apiKey}` },\n    });\n\n    if (!response.ok) {\n        throw await pollinationsErrorFromResponse(response);\n    }\n\n    return response.json() as Promise<T>;\n}\n\nfunction useAccountResource<T>(\n    fetcher: AccountFetcher<T>,\n    enabled = true,\n): AccountResourceValue<T> {\n    const { apiKey, apiBaseUrl, logout } = useRequiredAuth();\n    const [data, setData] = useState<T | null>(null);\n    const [isLoading, setIsLoading] = useState(false);\n    const [error, setError] = useState<Error | null>(null);\n    const reqIdRef = useRef(0);\n\n    const refresh = useCallback(async () => {\n        const reqId = ++reqIdRef.current;\n        if (!enabled || !apiKey) {\n            setData(null);\n            setIsLoading(false);\n            setError(null);\n            return;\n        }\n\n        setIsLoading(true);\n        setError(null);\n        try {\n            const result = await fetcher(apiBaseUrl, apiKey);\n            if (reqId !== reqIdRef.current) return;\n            setData(result);\n        } catch (err) {\n            if (reqId !== reqIdRef.current) return;\n            if (err instanceof PollinationsError && err.status === 401) {\n                logout();\n                return;\n            }\n            setData(null);\n            setError(err instanceof Error ? err : new Error(String(err)));\n        } finally {\n            if (reqId === reqIdRef.current) setIsLoading(false);\n        }\n    }, [apiKey, apiBaseUrl, enabled, fetcher, logout]);\n\n    useEffect(() => {\n        void refresh();\n    }, [refresh]);\n\n    return useMemo(\n        () => ({ data, isLoading, error, refresh }),\n        [data, isLoading, error, refresh],\n    );\n}\n\n/** Current account profile for the delegated key. */\nexport function useAccountProfile(options: { enabled?: boolean } = {}) {\n    const { enabled = true } = options;\n    const fetcher = useCallback(\n        (apiBaseUrl: string, apiKey: string) =>\n            fetchAccountJson<AccountProfile>(\n                apiBaseUrl,\n                apiKey,\n                \"/account/profile\",\n            ),\n        [],\n    );\n    return useAccountResource(fetcher, enabled);\n}\n\n/** Current visible balance for the delegated key. */\nexport function useAccountBalance(options: { enabled?: boolean } = {}) {\n    const { enabled = true } = options;\n    const fetcher = useCallback(\n        (apiBaseUrl: string, apiKey: string) =>\n            fetchAccountJson<AccountBalance>(\n                apiBaseUrl,\n                apiKey,\n                \"/account/balance\",\n            ),\n        [],\n    );\n    return useAccountResource(fetcher, enabled);\n}\n\n/** Current delegated key metadata. */\nexport function useAccountKey(options: { enabled?: boolean } = {}) {\n    const { enabled = true } = options;\n    const fetcher = useCallback(\n        (apiBaseUrl: string, apiKey: string) =>\n            fetchAccountJson<KeyInfo>(apiBaseUrl, apiKey, \"/account/key\"),\n        [],\n    );\n    return useAccountResource(fetcher, enabled);\n}\n\n/** Usage for the current delegated key only. */\nexport function useAccountKeyUsage(\n    options: UseAccountKeyUsageOptions = {},\n): UseAccountKeyUsageValue {\n    const {\n        enabled = true,\n        days,\n        limit,\n        before,\n        granularity,\n        period,\n    } = options;\n\n    const fetcher = useCallback(\n        (apiBaseUrl: string, apiKey: string) => {\n            const params = new URLSearchParams();\n            if (days) params.set(\"days\", String(days));\n            if (limit) params.set(\"limit\", String(limit));\n            if (before) params.set(\"before\", before);\n            if (granularity) params.set(\"granularity\", granularity);\n            if (period) params.set(\"period\", period);\n            return fetchAccountJson<UsageResponse>(\n                apiBaseUrl,\n                apiKey,\n                \"/account/key/usage\",\n                params,\n            );\n        },\n        [days, limit, before, granularity, period],\n    );\n    return useAccountResource(fetcher, enabled);\n}\n","export interface PollinationsConfig {\n    /** API key for authentication (get one at https://enter.pollinations.ai) */\n    apiKey?: string;\n    /** Base URL for the API (defaults to https://gen.pollinations.ai) */\n    baseUrl?: string;\n    /** Maximum number of retry attempts (default: 3) */\n    maxRetries?: number;\n    /** Default timeout in ms for all requests (default: 300000 = 5min) */\n    timeout?: number;\n    /** Timeout in ms for text requests (default: 300000 = 5min) */\n    textTimeout?: number;\n    /** Timeout in ms for image requests (default: 600000 = 10min) */\n    imageTimeout?: number;\n    /** Timeout in ms for video requests (default: 600000 = 10min) */\n    videoTimeout?: number;\n}\n\n/** Common options for all request methods */\nexport interface RequestOptions {\n    /** AbortSignal for request cancellation */\n    signal?: AbortSignal;\n}\n\n// ============================================================================\n// Image Generation\n// ============================================================================\n\n/** Image model (use getImageModels() to fetch available models) */\nexport type ImageModel = string;\n\n/** Available video models */\nexport type VideoModel = string;\n\n/** Image quality options */\nexport type ImageQuality = \"low\" | \"medium\" | \"high\" | \"hd\";\n\n/** Reasoning depth for supported image models */\nexport type ImageReasoningMode = \"fast\" | \"balanced\" | \"pro\";\n\n/** Options for image generation */\nexport interface ImageGenerateOptions extends RequestOptions {\n    /** Image model to use (default: 'zimage') */\n    model?: ImageModel;\n    /** Image width in pixels (default: 1024) */\n    width?: number;\n    /** Image height in pixels (default: 1024) */\n    height?: number;\n    /** Seed for reproducible generation (default: random) */\n    seed?: number;\n    /** Let AI enhance/improve your prompt (default: false) */\n    enhance?: boolean;\n    /** Negative prompt - what to avoid in the image */\n    negativePrompt?: string;\n    /** Keep generation private (default: false) */\n    private?: boolean;\n    /** Remove watermark logo (default: false) */\n    nologo?: boolean;\n    /** Don't show in public feed (default: false) */\n    nofeed?: boolean;\n    /** Enable safety content filters (default: false) */\n    safe?: boolean;\n    /** Output quality (default: 'medium') */\n    quality?: ImageQuality;\n    /** Reference image URL(s) for image-to-image generation */\n    referenceImage?: string | string[];\n    /** Enable transparent background - outputs PNG (default: false) */\n    transparent?: boolean;\n    /** How closely to follow prompt, 1-20 (higher = stricter) */\n    guidanceScale?: number;\n    /**\n     * Reasoning mode for supported image models.\n     * Booleans are accepted for backward compatibility: true = \"pro\",\n     * false = \"balanced\".\n     */\n    reasoning?: boolean | ImageReasoningMode;\n}\n\n/** Options for image editing (POST /v1/images/edits) */\nexport interface ImageEditOptions extends RequestOptions {\n    /** Image model to use (default: 'flux') */\n    model?: ImageModel;\n    /** Source image URL(s) for editing */\n    image?: string | string[];\n}\n\n/** Response from image generation */\nexport interface ImageResponse {\n    /** The generated image as a Buffer (Node.js) or ArrayBuffer (browser) */\n    buffer: ArrayBuffer;\n    /** Content type (image/jpeg or image/png) */\n    contentType: string;\n    /** The URL that was used to generate the image */\n    url: string;\n}\n\n// ============================================================================\n// Video Generation\n// ============================================================================\n\n/** Options for video generation */\nexport interface VideoGenerateOptions extends RequestOptions {\n    /** Video model to use (default: 'veo') */\n    model?: VideoModel;\n    /** Duration in seconds (1-30, varies by model) */\n    duration?: number;\n    /** Aspect ratio (e.g., '16:9', '9:16', '1:1') */\n    aspectRatio?: string;\n    /** Seed for reproducible generation */\n    seed?: number;\n    /** Enable audio generation where supported by the selected video model */\n    audio?: boolean;\n    /** Reference image URL(s) for image-to-video. For video, image[0] is the start frame and image[1] is the end frame when supported. */\n    referenceImage?: string | string[];\n    /** Keep generation private (default: false) */\n    private?: boolean;\n    /** Remove watermark logo (default: false) */\n    nologo?: boolean;\n    /** Enable safety content filters (default: false) */\n    safe?: boolean;\n}\n\n/** Response from video generation */\nexport interface VideoResponse {\n    /** The generated video as a Buffer (Node.js) or ArrayBuffer (browser) */\n    buffer: ArrayBuffer;\n    /** Content type (video/mp4) */\n    contentType: string;\n    /** The URL that was used to generate the video */\n    url: string;\n}\n\n// ============================================================================\n// Text Generation\n// ============================================================================\n\n/** Text model (use getTextModels() to fetch available models) */\nexport type TextModel = string;\n\n/** Message roles for chat */\nexport type MessageRole =\n    | \"system\"\n    | \"developer\"\n    | \"user\"\n    | \"assistant\"\n    | \"tool\"\n    | \"function\";\n\n/** Text content part */\nexport interface TextContentPart {\n    type: \"text\";\n    text: string;\n}\n\n/** Image content part for vision */\nexport interface ImageContentPart {\n    type: \"image_url\";\n    image_url: {\n        url: string;\n        detail?: \"auto\" | \"low\" | \"high\";\n        mime_type?: string;\n    };\n}\n\n/** Video content part */\nexport interface VideoContentPart {\n    type: \"video_url\";\n    video_url: {\n        url: string;\n        mime_type?: string;\n    };\n}\n\n/** Audio content part */\nexport interface AudioContentPart {\n    type: \"input_audio\";\n    input_audio: {\n        /** Base64 encoded audio data */\n        data: string;\n        format: \"wav\" | \"mp3\" | \"flac\" | \"opus\" | \"pcm16\";\n    };\n}\n\n/** File content part */\nexport interface FileContentPart {\n    type: \"file\";\n    file: {\n        file_data?: string;\n        file_id?: string;\n        file_name?: string;\n        file_url?: string;\n        mime_type?: string;\n    };\n}\n\n/** All possible content parts */\nexport type MessageContentPart =\n    | TextContentPart\n    | ImageContentPart\n    | VideoContentPart\n    | AudioContentPart\n    | FileContentPart;\n\n/** Message content - either a string or array of content parts */\nexport type MessageContent = string | MessageContentPart[];\n\n/** A chat message */\nexport interface Message {\n    role: MessageRole;\n    content: MessageContent;\n    name?: string;\n    /** For tool messages */\n    tool_call_id?: string;\n}\n\n/** Options for simple text generation */\nexport interface TextGenerateOptions extends RequestOptions {\n    /** Text model to use (default: 'openai') */\n    model?: TextModel;\n    /** System prompt to set context */\n    systemPrompt?: string;\n    /** Seed for reproducible generation */\n    seed?: number;\n    /** Return response as JSON (default: false) */\n    json?: boolean;\n    /** Temperature 0-2, higher = more creative (default: 1) */\n    temperature?: number;\n    /** Maximum tokens to generate */\n    maxTokens?: number;\n    /** Frequency penalty -2 to 2 */\n    frequencyPenalty?: number;\n    /** Presence penalty -2 to 2 */\n    presencePenalty?: number;\n    /** Enable streaming response (default: false) */\n    stream?: boolean;\n    /** Keep generation private (default: false) */\n    private?: boolean;\n}\n\n/** Response format options */\nexport type ResponseFormat =\n    | { type: \"text\" }\n    | { type: \"json_object\" }\n    | { type: \"json_schema\"; json_schema: JsonSchema };\n\n/** JSON schema for structured output */\nexport interface JsonSchema {\n    name: string;\n    description?: string;\n    schema: Record<string, unknown>;\n    strict?: boolean;\n}\n\n/** Tool definition */\nexport interface Tool {\n    type: \"function\";\n    function: {\n        name: string;\n        description?: string;\n        parameters?: Record<string, unknown>;\n        strict?: boolean;\n    };\n}\n\n/** Built-in tool types */\nexport type BuiltInToolType =\n    | \"code_execution\"\n    | \"google_search\"\n    | \"google_maps\"\n    | \"url_context\"\n    | \"computer_use\"\n    | \"file_search\";\n\n/** Thinking/reasoning options */\nexport interface ThinkingOptions {\n    type: \"enabled\" | \"disabled\";\n    budget_tokens?: number;\n}\n\n/** Options for chat completions (POST endpoint) */\nexport interface ChatOptions extends RequestOptions {\n    /** Text model to use (default: 'openai') */\n    model?: TextModel;\n    /** Temperature 0-2 (default: 1) */\n    temperature?: number;\n    /** Top P sampling 0-1 (default: 1) */\n    topP?: number;\n    /** Maximum tokens to generate */\n    maxTokens?: number;\n    /** Frequency penalty -2 to 2 */\n    frequencyPenalty?: number;\n    /** Presence penalty -2 to 2 */\n    presencePenalty?: number;\n    /** Repetition penalty */\n    repetitionPenalty?: number;\n    /** Stop sequences (max 4) */\n    stop?: string | string[];\n    /** Seed for reproducible generation */\n    seed?: number;\n    /** Enable streaming response (default: false) */\n    stream?: boolean;\n    /** Include usage stats in streaming response */\n    streamOptions?: { include_usage?: boolean };\n    /** Response format */\n    responseFormat?: ResponseFormat;\n    /** Tools available to the model */\n    tools?: (Tool | { type: BuiltInToolType })[];\n    /** Tool choice: 'none', 'auto', 'required', or specific function */\n    toolChoice?:\n        | \"none\"\n        | \"auto\"\n        | \"required\"\n        | { type: \"function\"; function: { name: string } };\n    /** Allow parallel tool calls (default: true) */\n    parallelToolCalls?: boolean;\n    /** Enable thinking/reasoning for supported models */\n    thinking?: ThinkingOptions | null;\n    /** Reasoning effort for thinking models */\n    reasoningEffort?: \"low\" | \"medium\" | \"high\";\n    /** Thinking budget in tokens */\n    thinkingBudget?: number;\n    /** Output modalities */\n    modalities?: (\"text\" | \"audio\")[];\n    /** Audio output options */\n    audio?: {\n        voice?: AudioVoice;\n        format?: AudioFormat;\n    };\n    /** User identifier for tracking */\n    user?: string;\n    /** Logit bias for token probabilities */\n    logitBias?: Record<string, number>;\n    /** Return log probabilities */\n    logprobs?: boolean;\n    /** Number of top log probabilities to return (0-20) */\n    topLogprobs?: number;\n    /** Legacy function calling (use tools instead) */\n    functions?: FunctionDefinition[];\n    /** Legacy function call control (use toolChoice instead) */\n    functionCall?: \"none\" | \"auto\" | { name: string };\n}\n\n/** Legacy function definition (use Tool instead) */\nexport interface FunctionDefinition {\n    name: string;\n    description?: string;\n    parameters?: Record<string, unknown>;\n}\n\n/** A tool call in the response */\nexport interface ToolCall {\n    id: string;\n    type: \"function\";\n    function: {\n        name: string;\n        arguments: string;\n    };\n}\n\n/** Token usage information */\nexport interface CompletionUsage {\n    prompt_tokens: number;\n    completion_tokens: number;\n    total_tokens: number;\n    prompt_tokens_details?: {\n        cached_tokens?: number;\n        audio_tokens?: number;\n    };\n    completion_tokens_details?: {\n        reasoning_tokens?: number;\n        audio_tokens?: number;\n    };\n}\n\n/** A choice in the chat response */\nexport interface ChatChoice {\n    index: number;\n    message: {\n        role: \"assistant\";\n        content: string | null;\n        tool_calls?: ToolCall[];\n        audio?: {\n            transcript: string;\n            data: string;\n            id: string;\n            expires_at: number;\n        } | null;\n        reasoning_content?: string | null;\n    };\n    finish_reason: \"stop\" | \"length\" | \"tool_calls\" | \"content_filter\" | null;\n    logprobs?: {\n        content?: Array<{\n            token: string;\n            logprob: number;\n            bytes?: number[];\n            top_logprobs?: Array<{\n                token: string;\n                logprob: number;\n                bytes?: number[];\n            }>;\n        }>;\n    } | null;\n}\n\n/** Chat completion response */\nexport interface ChatResponse {\n    id: string;\n    object: \"chat.completion\";\n    created: number;\n    model: string;\n    choices: ChatChoice[];\n    usage?: CompletionUsage;\n    system_fingerprint?: string;\n    citations?: string[];\n}\n\n/** A chunk in streaming response */\nexport interface ChatStreamChunk {\n    id: string;\n    object: \"chat.completion.chunk\";\n    created: number;\n    model: string;\n    choices: Array<{\n        index: number;\n        delta: {\n            role?: \"assistant\";\n            content?: string;\n            tool_calls?: Array<{\n                index: number;\n                id?: string;\n                type?: \"function\";\n                function?: {\n                    name?: string;\n                    arguments?: string;\n                };\n            }>;\n        };\n        finish_reason:\n            | \"stop\"\n            | \"length\"\n            | \"tool_calls\"\n            | \"content_filter\"\n            | null;\n    }>;\n    usage?: CompletionUsage;\n}\n\n// ============================================================================\n// Audio Generation\n// ============================================================================\n\n/** Voice option for audio generation */\nexport type AudioVoice = string;\n\n/** Audio format options */\nexport type AudioFormat = \"wav\" | \"mp3\" | \"flac\" | \"opus\" | \"pcm16\";\n\n/** Dedicated audio/music model */\nexport type AudioModel = \"elevenlabs\" | \"elevenmusic\" | \"acestep\" | string;\n\n/** Options for text-to-speech generation (GET /audio/{text} or POST /v1/audio/speech) */\nexport interface AudioGenerateOptions extends RequestOptions {\n    /** Voice to use (default: 'alloy') */\n    voice?: AudioVoice;\n    /** Audio model to use (default: 'elevenlabs') */\n    model?: AudioModel;\n    /** Duration in seconds (for music models like elevenmusic, acestep) */\n    duration?: number;\n    /** Seed for reproducibility */\n    seed?: number;\n}\n\n/** Response from dedicated audio endpoints (binary audio data) */\nexport interface AudioBinaryResponse {\n    /** The generated audio as a Buffer (Node.js) or ArrayBuffer (browser) */\n    buffer: ArrayBuffer;\n    /** Content type (audio/mpeg) */\n    contentType: string;\n}\n\n/** Audio response from chat with audio modality */\nexport interface AudioResponse {\n    /** Transcript of the audio */\n    transcript: string;\n    /** Base64 encoded audio data */\n    data: string;\n    /** Audio ID */\n    id: string;\n    /** Expiration timestamp */\n    expiresAt: number;\n}\n\n// ============================================================================\n// Speech-to-Text (Transcription)\n// ============================================================================\n\n/** STT model options */\nexport type TranscriptionModel =\n    | \"whisper-large-v3\"\n    | \"whisper-1\"\n    | \"scribe\"\n    | \"universal-2\"\n    | \"universal-3-pro\"\n    | string;\n\n/** Response format for transcription */\nexport type TranscriptionResponseFormat =\n    | \"json\"\n    | \"text\"\n    | \"verbose_json\"\n    | \"srt\"\n    | \"vtt\";\n\n/** Options for speech-to-text transcription */\nexport interface TranscribeOptions extends RequestOptions {\n    /** Model to use (default: 'whisper-large-v3') */\n    model?: TranscriptionModel;\n    /** Language code (ISO-639-1, e.g. 'en', 'fr') */\n    language?: string;\n    /** Response format (default: 'json') */\n    responseFormat?: TranscriptionResponseFormat;\n    /** Optional prompt to guide transcription style */\n    prompt?: string;\n    /** Temperature 0-1 for sampling */\n    temperature?: number;\n}\n\n/** Transcription response */\nexport interface TranscriptionResponse {\n    /** Transcribed text */\n    text: string;\n}\n\n/** Verbose transcription response with word/segment details */\nexport interface TranscriptionVerboseResponse extends TranscriptionResponse {\n    task: string;\n    language: string;\n    duration: number;\n    words?: Array<{ word: string; start: number; end: number }>;\n    segments?: Array<{ id: number; start: number; end: number; text: string }>;\n}\n\n// ============================================================================\n// Media Upload\n// ============================================================================\n\n/** Options for media upload */\nexport interface UploadOptions extends RequestOptions {\n    /** Original filename (optional) */\n    name?: string;\n    /** Content type (auto-detected if omitted) */\n    contentType?: string;\n}\n\n/** Response from media upload */\nexport interface UploadResponse {\n    /** Content-addressed hash ID */\n    id: string;\n    /** Public URL for the uploaded media */\n    url: string;\n    /** Content type of the uploaded file */\n    contentType: string;\n    /** File size in bytes */\n    size: number;\n    /** Whether the file was already uploaded (dedup) */\n    duplicate: boolean;\n}\n\n// ============================================================================\n// BYOP (Bring Your Own Pollen)\n// ============================================================================\n\n/** Account permission scopes */\nexport type AccountPermission = \"profile\" | \"usage\" | \"keys\";\n\n/** Options for building a BYOP authorization URL */\nexport interface AuthorizeOptions {\n    /** URL to redirect back to after authorization */\n    redirectUrl: string;\n    /** Your app's publishable key (shows app name to user) */\n    appKey?: string;\n    /** OAuth state value echoed back to the redirect URL */\n    state?: string;\n    /** Authorization host (defaults to https://enter.pollinations.ai) */\n    authBaseUrl?: string;\n    /** Restrict to specific models */\n    models?: string[];\n    /** Numeric pollen cap. Omit for the default cap. */\n    budget?: number;\n    /** Key lifetime in days (default: 30) */\n    expiry?: number;\n    /** Account permissions to request */\n    permissions?: AccountPermission[];\n}\n\n// ============================================================================\n// Account\n// ============================================================================\n\n/** User profile information */\nexport interface AccountProfile {\n    githubUsername: string | null;\n    image: string | null;\n    /** Current tier level (e.g. `\"spore\"`, `\"seed\"`, `\"flower\"`, `\"nectar\"`). */\n    tier: string;\n    /** ISO 8601 timestamp of the next pollen refill. `null` for tiers with no refill. */\n    nextResetAt: string | null;\n    /** Only returned when the API key has the `profile` permission */\n    name?: string | null;\n    /** Only returned when the API key has the `profile` permission */\n    email?: string | null;\n}\n\n/** Account balance */\nexport interface AccountBalance {\n    balance: number;\n}\n\n/** Usage record */\nexport interface UsageRecord {\n    timestamp: string;\n    type: string;\n    model: string;\n    api_key: string;\n    api_key_type: string;\n    meter_source: string;\n    input_text_tokens: number;\n    input_cached_tokens: number;\n    input_audio_tokens: number;\n    input_audio_seconds: number;\n    input_image_tokens: number;\n    output_text_tokens: number;\n    output_reasoning_tokens: number;\n    output_audio_tokens: number;\n    output_audio_seconds: number;\n    output_image_tokens: number;\n    output_video_seconds: number;\n    cost_usd: number;\n    response_time_ms: number;\n}\n\n/** Usage response */\nexport interface UsageResponse {\n    usage: UsageRecord[];\n    count: number;\n}\n\n/** Options for fetching usage */\nexport interface UsageOptions {\n    /** Response format (default: 'json') */\n    format?: \"json\" | \"csv\";\n    /** Number of days to include, max 90 (default: 30) */\n    days?: number;\n    /** Max records to return, 1-50000 (default: 100). CSV exports are capped at 50000. */\n    limit?: number;\n    /** ISO timestamp cursor for pagination */\n    before?: string;\n    /** Exact period granularity */\n    granularity?: \"day\" | \"week\" | \"month\";\n    /** Exact period, e.g. YYYY-MM-DD, YYYY-WNN, or YYYY-MM */\n    period?: string;\n}\n\n/** Options for fetching current-key usage. CSV export is not supported here. */\nexport type KeyUsageOptions = Omit<UsageOptions, \"format\">;\n\n/** Options for fetching daily usage */\nexport interface DailyUsageOptions {\n    /** Response format (default: 'json') */\n    format?: \"json\" | \"csv\";\n    /** Number of days to include, max 90 (default: 90) */\n    days?: number;\n    /** Exact period granularity */\n    granularity?: \"day\" | \"week\" | \"month\";\n    /** Exact period, e.g. YYYY-MM-DD, YYYY-WNN, or YYYY-MM */\n    period?: string;\n    /** Filter to one or more API keys by id */\n    api_key_ids?: string[];\n}\n\n/** Daily usage summary */\nexport interface DailyUsageRecord {\n    date: string;\n    model: string;\n    meter_source: string;\n    requests: number;\n    cost_usd: number;\n}\n\n/** Daily usage response */\nexport interface DailyUsageResponse {\n    usage: DailyUsageRecord[];\n    count: number;\n}\n\n/** API key validation response */\nexport interface KeyInfo {\n    valid: boolean;\n    type: string;\n    name?: string | null;\n    expiresAt?: string | null;\n    expiresIn?: number | null;\n    permissions?: {\n        models?: string[] | null;\n        account?: string[] | null;\n    };\n    pollenBudget?: number | null;\n    rateLimitEnabled?: boolean;\n}\n\n/** Detailed key record returned by GET /account/keys (list) */\nexport interface AccountKey {\n    id: string;\n    name: string;\n    /** First few characters of the key (for identification) */\n    start?: string;\n    prefix: string;\n    type?: string;\n    createdAt: string;\n    expiresAt: string | null;\n    lastRequest: string | null;\n    permissions: {\n        tier?: string[];\n        models?: string[];\n        account?: string[];\n    } | null;\n    metadata: Record<string, unknown> | null;\n    pollenBalance: number | null;\n    enabled: boolean;\n}\n\n/** Key-scope permissions that can be granted on created keys. */\nexport type KeyAccountPermission = \"profile\" | \"usage\" | \"keys\" | string;\n\n/** Options for POST /account/keys */\nexport interface CreateKeyOptions {\n    /** Human-readable name */\n    name: string;\n    /** Key type (default: \"secret\") */\n    type?: \"secret\" | \"publishable\";\n    /** Expiry in seconds from creation */\n    expiresIn?: number;\n    /** Restrict to specific model IDs */\n    allowedModels?: string[];\n    /** Pollen budget cap */\n    pollenBudget?: number;\n    /**\n     * Account permissions to grant (e.g. `[\"profile\", \"usage\"]`).\n     * Without this, scoped keys cannot read account state beyond their\n     * own key metadata, budget, and per-key usage.\n     * `\"keys\"` is auto-stripped server-side on the BYOP flow.\n     */\n    accountPermissions?: KeyAccountPermission[];\n    /**\n     * Allowed OAuth redirect URIs for publishable app keys. Required when\n     * creating a `publishable` key that drives the `/authorize` BYOP flow.\n     * Matching pins scheme, host, port, and path; one trailing slash is\n     * ignored; loopback ports are matched port-agnostically.\n     */\n    redirectUris?: string[];\n    /**\n     * Opt the publishable app key into developer earnings. Defaults to\n     * `false` server-side; only meaningful on `type: \"publishable\"` keys.\n     */\n    earningsEnabled?: boolean;\n}\n\n/**\n * Response from POST /account/keys — includes the raw `key` value which\n * is ONLY shown at creation time. Store it immediately.\n */\nexport interface CreatedKey {\n    id: string;\n    /** The secret key value. Only returned once at creation. */\n    key: string;\n    name: string;\n    type: string;\n    prefix: string;\n    expiresAt: string | null;\n    permissions: {\n        models?: string[];\n        account?: string[];\n    } | null;\n    pollenBudget: number | null;\n}\n\n// ============================================================================\n// Model Information\n// ============================================================================\n\n/** Model tier levels */\nexport type ModelTier = \"anonymous\" | \"seed\" | \"flower\" | \"nectar\";\n\n/** Per-model video frame-control capabilities (video models only) */\nexport type VideoCapability =\n    | \"start_frame\"\n    | \"end_frame\"\n    | \"keyframes\"\n    | \"audio_output\";\n\n/** Model information */\nexport interface ModelInfo {\n    name: string;\n    description?: string;\n    aliases?: string[];\n    tier?: ModelTier;\n    community?: boolean;\n    input_modalities?: string[];\n    output_modalities?: string[];\n    video_capabilities?: VideoCapability[];\n    tools?: boolean;\n    vision?: boolean;\n    audio?: boolean;\n    reasoning?: boolean;\n    uncensored?: boolean;\n    voices?: string[];\n    maxInputChars?: number;\n    context_length?: number;\n    supportsSystemMessages?: boolean;\n    is_specialized?: boolean;\n    pricing?: {\n        currency: \"pollen\";\n        input_token_price?: number;\n        output_token_price?: number;\n        cached_token_price?: number;\n        image_price?: number;\n        audio_input_price?: number;\n        audio_output_price?: number;\n    };\n}\n\n// ============================================================================\n// Device Flow (OAuth)\n// ============================================================================\n\n/** Raw response from POST /api/device/code */\nexport interface DeviceCodeResponse {\n    device_code: string;\n    user_code: string;\n    verification_uri_complete: string;\n    expires_in: number;\n    interval: number;\n}\n\n/** Raw response from POST /api/device/token (polling) */\nexport interface DeviceTokenResponse {\n    access_token?: string;\n    error?: string;\n    error_description?: string;\n}\n\n/** Options for starting device flow */\nexport interface AuthorizeDeviceOptions extends RequestOptions {\n    /** OAuth client ID. Defaults to the public polli CLI client. */\n    clientId?: string;\n    /** Space-separated scope string (default: \"generate keys usage\") */\n    scope?: string;\n}\n\n/**\n * Handle returned by `authorizeDevice()` — surfaces the user-facing\n * verification URL/code, and exposes `poll()` to block until approval.\n */\nexport interface DeviceAuthorization {\n    /** Short code the user types in the verification page */\n    userCode: string;\n    /** URL to open in a browser (includes the user code as a query param) */\n    verificationUri: string;\n    /** When the device code expires and polling must stop */\n    expiresAt: Date;\n    /** Block until the user approves. Resolves with an access token or throws. */\n    poll(): Promise<string>;\n}\n\n/** User identity returned by GET /api/device/userinfo */\nexport interface UserInfo {\n    sub?: string;\n    name?: string;\n    email?: string;\n    githubUsername?: string;\n    tier?: string;\n    [key: string]: unknown;\n}\n\n// ============================================================================\n// Image Generation (OpenAI-compatible POST /v1/images/generations)\n// ============================================================================\n\n/** Options for POST /v1/images/generations */\nexport interface ImageGenerateV1Options extends RequestOptions {\n    /** Image model to use (default: 'zimage') */\n    model?: ImageModel;\n    /** Size string like \"1024x1024\". Alternative to width + height. */\n    size?: string;\n    /** Image width in pixels (converted to `size` when both dimensions provided) */\n    width?: number;\n    /** Image height in pixels */\n    height?: number;\n    /** Number of images to generate (default: 1) */\n    n?: number;\n    /** Response format (default: server decides — usually b64_json) */\n    responseFormat?: \"url\" | \"b64_json\";\n    /** Reasoning mode for supported image models. Booleans are accepted for backward compatibility. */\n    reasoning?: boolean | ImageReasoningMode;\n    /** Seed for reproducible generation */\n    seed?: number;\n    /** Output quality */\n    quality?: ImageQuality;\n    /** Negative prompt - what to avoid */\n    negativePrompt?: string;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/** API error details */\nexport interface PollinationsErrorDetails {\n    code:\n        | \"BAD_REQUEST\"\n        | \"UNAUTHORIZED\"\n        | \"INSUFFICIENT_BALANCE\"\n        | \"PERMISSION_DENIED\"\n        | \"INTERNAL_ERROR\"\n        | string;\n    message: string;\n    timestamp: string;\n    details?: Record<string, unknown>;\n    requestId?: string;\n}\n\n/** Pollinations SDK Error */\nexport class PollinationsError extends Error {\n    code: string;\n    status: number;\n    details?: Record<string, unknown>;\n    requestId?: string;\n    /** Retry-After value in seconds (for 429 rate limit errors) */\n    retryAfter?: number;\n\n    constructor(\n        message: string,\n        code: string,\n        status: number,\n        details?: Record<string, unknown>,\n        requestId?: string,\n        retryAfter?: number,\n    ) {\n        super(message);\n        this.name = \"PollinationsError\";\n        this.code = code;\n        this.status = status;\n        this.details = details;\n        this.requestId = requestId;\n        this.retryAfter = retryAfter;\n    }\n}\n","import { createContext } from \"react\";\nimport type { AccountPermission } from \"../types.js\";\n\nexport interface AuthStateValue {\n    apiKey: string | null;\n    isLoggedIn: boolean;\n    isHydrated: boolean;\n    error: Error | null;\n}\n\n/**\n * Per-call overrides for the OAuth authorize redirect. Each field maps to a\n * BYOP (bring-your-own-policy) URL parameter on `enter.pollinations.ai/authorize`\n * — see `<PolliProvider>` props for the same fields as provider-level defaults.\n */\nexport interface AuthorizeRequest {\n    /** Extra OAuth scopes appended to provider defaults. */\n    permissions?: AccountPermission[];\n    /** Restrict the minted key to these model slugs. Omit / empty = all models. */\n    models?: string[];\n    /** Pollen budget to request for the minted key. */\n    budget?: number;\n    /** Days until the minted key expires. */\n    expiry?: number;\n}\n\nexport interface AuthActionsValue {\n    login: (request?: AuthorizeRequest) => void;\n    logout: () => void;\n    setApiKey: (apiKey: string | null) => void;\n    /** Resolved enter URL, useful for top-up / dashboard links. */\n    enterUrl: string;\n    /** Resolved account API URL used by opt-in account hooks. */\n    apiBaseUrl: string;\n}\n\nexport interface AuthContextValue extends AuthStateValue, AuthActionsValue {}\n\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n","import {\n    type ReactNode,\n    useCallback,\n    useEffect,\n    useMemo,\n    useState,\n} from \"react\";\nimport type { AccountPermission } from \"../types.js\";\nimport {\n    AuthContext,\n    type AuthContextValue,\n    type AuthorizeRequest,\n} from \"./contexts.js\";\nimport { consumeOAuthCallback } from \"./oauth.js\";\nimport {\n    resolveStorage,\n    type StorageAdapter,\n    type StorageOption,\n} from \"./storage.js\";\n\nexport type { AuthorizeRequest } from \"./contexts.js\";\nexport type { StorageAdapter, StorageOption } from \"./storage.js\";\n\nexport const DEFAULT_ENTER_URL = \"https://enter.pollinations.ai\";\n\nexport interface PolliProviderProps {\n    /** Publishable key (`pk_...`): the app identifier, not a user's API key. */\n    appKey: string;\n    children: ReactNode;\n    /**\n     * Where to persist the user's session token. Defaults to `\"localStorage\"`.\n     * Accepts `\"sessionStorage\"` or a custom synchronous `StorageAdapter`\n     * (e.g. cookie-backed or in-memory). Async backends like IndexedDB or\n     * React Native AsyncStorage are not supported — the interface is sync\n     * because hydration runs in a `useEffect`.\n     */\n    storage?: StorageOption;\n    /** OAuth scopes to request at login. Defaults to no optional account scopes. */\n    permissions?: AccountPermission[];\n    /**\n     * Default model slugs to request access to (BYOP). Empty / undefined means\n     * \"all models\". Per-call `login({ models })` overrides this.\n     */\n    models?: string[];\n    /**\n     * Default pollen budget to request for the minted key. Per-call\n     * `login({ budget })` overrides this.\n     */\n    budget?: number;\n    /**\n     * Default key lifetime in days. Per-call `login({ expiry })` overrides this.\n     */\n    expiry?: number;\n    /** Auth host. Defaults to `https://enter.pollinations.ai`. */\n    enterUrl?: string;\n    /** Account API host. Derived from `enterUrl + \"/api\"` unless explicitly set. */\n    apiBaseUrl?: string;\n}\n\nfunction buildAuthorizeUrl(args: {\n    enterUrl: string;\n    appKey: string;\n    permissions: readonly AccountPermission[];\n    redirectUrl: string;\n    state: string;\n    models?: readonly string[];\n    budget?: number;\n    expiry?: number;\n}): string {\n    const params = new URLSearchParams();\n    params.set(\"redirect_uri\", args.redirectUrl);\n    params.set(\"client_id\", args.appKey);\n    params.set(\"state\", args.state);\n    if (args.permissions.length > 0) {\n        params.set(\"scope\", args.permissions.join(\" \"));\n    }\n    if (args.models && args.models.length > 0) {\n        params.set(\"models\", args.models.join(\",\"));\n    }\n    if (args.budget !== undefined) {\n        params.set(\"budget\", String(args.budget));\n    }\n    if (args.expiry !== undefined) {\n        params.set(\"expiry\", String(args.expiry));\n    }\n    return `${args.enterUrl}/authorize?${params.toString()}`;\n}\n\n/**\n * Provides Pollinations auth state to descendants. Wrap your app once at the\n * root. Holds the delegated API key, handles the OAuth callback, and exposes\n * login/logout. Account data is fetched by opt-in hooks so apps only request\n * the data they render.\n */\nexport function PolliProvider({\n    appKey,\n    children,\n    storage: storageOption,\n    permissions,\n    models: defaultModels,\n    budget: defaultBudget,\n    expiry: defaultExpiry,\n    enterUrl = DEFAULT_ENTER_URL,\n    apiBaseUrl,\n}: PolliProviderProps) {\n    const storage = useMemo<StorageAdapter>(\n        () => resolveStorage(storageOption),\n        [storageOption],\n    );\n    const resolvedApiBaseUrl = apiBaseUrl ?? `${enterUrl}/api`;\n    const storageKey = `polli:${appKey}:token`;\n    const stateStorageKey = `polli:${appKey}:oauth_state`;\n\n    const defaultPermissions = useMemo<readonly AccountPermission[]>(\n        () => permissions ?? [],\n        [permissions],\n    );\n\n    // SSR-safe: start null so server + client first paint agree.\n    const [apiKey, setApiKey] = useState<string | null>(null);\n    const [isHydrated, setIsHydrated] = useState(false);\n    const [error, setError] = useState<Error | null>(null);\n\n    const updateApiKey = useCallback(\n        (nextApiKey: string | null) => {\n            if (nextApiKey) {\n                storage.setItem(storageKey, nextApiKey);\n            } else {\n                storage.removeItem(storageKey);\n            }\n            setApiKey(nextApiKey);\n            setError(null);\n        },\n        [storage, storageKey],\n    );\n\n    // Hydrate API key from storage + capture URL fragment after redirect.\n    useEffect(() => {\n        if (typeof window === \"undefined\") {\n            setIsHydrated(true);\n            return;\n        }\n\n        const result = consumeOAuthCallback(\n            window.location,\n            storage,\n            stateStorageKey,\n        );\n        if (result.cleanedUrl) {\n            window.history.replaceState({}, \"\", result.cleanedUrl);\n        }\n        if (result.invalidState) {\n            console.warn(\n                \"[PolliProvider] dropping auth response with missing or mismatched state\",\n            );\n            setError(new Error(\"Invalid OAuth state\"));\n        }\n        if (result.error) {\n            console.warn(\n                `[PolliProvider] auth error: ${result.error}${\n                    result.errorDescription\n                        ? ` — ${result.errorDescription}`\n                        : \"\"\n                }`,\n            );\n            setError(new Error(result.errorDescription ?? result.error));\n        }\n        if (result.apiKey) {\n            updateApiKey(result.apiKey);\n            setIsHydrated(true);\n            return;\n        }\n        const stored = storage.getItem(storageKey);\n        if (stored) setApiKey(stored);\n        setIsHydrated(true);\n    }, [stateStorageKey, storage, updateApiKey, storageKey]);\n\n    const login = useCallback(\n        (request?: AuthorizeRequest) => {\n            if (typeof window === \"undefined\") return;\n            const currentUrl =\n                window.location.href.split(\"#\")[0] ?? window.location.href;\n            const extraPermissions = request?.permissions;\n            const perms: AccountPermission[] =\n                extraPermissions && extraPermissions.length > 0\n                    ? Array.from(\n                          new Set([...defaultPermissions, ...extraPermissions]),\n                      )\n                    : [...defaultPermissions];\n            const state = crypto.randomUUID();\n            storage.setItem(stateStorageKey, state);\n            window.location.href = buildAuthorizeUrl({\n                enterUrl,\n                appKey,\n                permissions: perms,\n                redirectUrl: currentUrl,\n                state,\n                models: request?.models ?? defaultModels,\n                budget: request?.budget ?? defaultBudget,\n                expiry: request?.expiry ?? defaultExpiry,\n            });\n        },\n        [\n            enterUrl,\n            appKey,\n            defaultPermissions,\n            storage,\n            stateStorageKey,\n            defaultModels,\n            defaultBudget,\n            defaultExpiry,\n        ],\n    );\n\n    const logout = useCallback(() => {\n        updateApiKey(null);\n    }, [updateApiKey]);\n\n    const authValue = useMemo<AuthContextValue>(\n        () => ({\n            apiKey,\n            isLoggedIn: !!apiKey,\n            isHydrated,\n            error,\n            login,\n            logout,\n            setApiKey: updateApiKey,\n            enterUrl,\n            apiBaseUrl: resolvedApiBaseUrl,\n        }),\n        [\n            apiKey,\n            isHydrated,\n            error,\n            login,\n            logout,\n            updateApiKey,\n            enterUrl,\n            resolvedApiBaseUrl,\n        ],\n    );\n\n    return (\n        <AuthContext.Provider value={authValue}>\n            {children}\n        </AuthContext.Provider>\n    );\n}\n","/**\n * Pure OAuth callback parser. Extracted from PolliProvider so it can be\n * unit-tested without React.\n *\n * Reads `window.location.hash` for the `api_key` / `error` / `state`\n * fragment params that `enter.pollinations.ai/authorize` redirects back\n * with. Validates the `state` parameter against the value the client\n * persisted at login() time (CSRF protection), then signals to the caller\n * what to do.\n *\n * Validate-then-clear semantics: state is only cleared from storage when\n * the callback genuinely matches a pending login. A spoofed callback with\n * a wrong state must NOT clear stored state — otherwise an attacker could\n * DoS the real callback by planting a bad URL.\n */\n\nexport interface OAuthCallbackResult {\n    /**\n     * If auth params were present in the hash, the URL to replace\n     * `window.location` with via `history.replaceState`. Strips the auth\n     * params but preserves the route prefix (`#/dashboard`), any non-auth\n     * hash params, pathname, and search.\n     *\n     * `null` when no auth params were present (no rewrite needed).\n     */\n    cleanedUrl: string | null;\n    /** Valid api_key from a callback whose `state` matched. */\n    apiKey: string | null;\n    /** OAuth error code, if the callback was an error response. */\n    error: string | null;\n    errorDescription: string | null;\n    /**\n     * `true` when an `api_key` was present but `state` did not match.\n     * Callers should warn but otherwise ignore (do NOT clear stored state).\n     */\n    invalidState: boolean;\n}\n\ninterface ReadOnlyLocation {\n    hash: string;\n    pathname: string;\n    search: string;\n}\n\ninterface OAuthStorage {\n    getItem(key: string): string | null;\n    removeItem(key: string): void;\n}\n\nconst EMPTY: OAuthCallbackResult = {\n    cleanedUrl: null,\n    apiKey: null,\n    error: null,\n    errorDescription: null,\n    invalidState: false,\n};\n\nexport function consumeOAuthCallback(\n    location: ReadOnlyLocation,\n    storage: OAuthStorage,\n    stateStorageKey: string,\n): OAuthCallbackResult {\n    const rawHash = location.hash.startsWith(\"#\")\n        ? location.hash.slice(1)\n        : location.hash;\n    if (!rawHash) return EMPTY;\n\n    // Hash-router apps use `#/route?param=…` — the route prefix sits before\n    // the `?`, params after. Treating the whole hash as params would\n    // mis-parse the route and the cleanup step below would strip it.\n    const queryIdx = rawHash.indexOf(\"?\");\n    const routePrefix = queryIdx === -1 ? \"\" : rawHash.slice(0, queryIdx);\n    const paramString = queryIdx === -1 ? rawHash : rawHash.slice(queryIdx + 1);\n    const params = new URLSearchParams(paramString);\n\n    const key = params.get(\"api_key\");\n    const error = params.get(\"error\");\n    const receivedState = params.get(\"state\");\n    const errorDescription = params.get(\"error_description\");\n\n    if (!key && !error) return EMPTY;\n\n    for (const p of [\"api_key\", \"state\", \"error\", \"error_description\"]) {\n        params.delete(p);\n    }\n    const remaining = params.toString();\n    const newHash = remaining\n        ? routePrefix\n            ? `${routePrefix}?${remaining}`\n            : remaining\n        : routePrefix;\n    const cleanedUrl =\n        location.pathname + location.search + (newHash ? `#${newHash}` : \"\");\n\n    if (key) {\n        const expectedState = storage.getItem(stateStorageKey);\n        if (!expectedState || receivedState !== expectedState) {\n            return { ...EMPTY, cleanedUrl, invalidState: true };\n        }\n        storage.removeItem(stateStorageKey);\n        return { ...EMPTY, cleanedUrl, apiKey: key };\n    }\n\n    // error branch\n    const expectedState = storage.getItem(stateStorageKey);\n    if (expectedState && receivedState === expectedState) {\n        storage.removeItem(stateStorageKey);\n    }\n    return { ...EMPTY, cleanedUrl, error, errorDescription };\n}\n","/**\n * Synchronous key/value store for the user's session token and OAuth state.\n * Modeled on `window.localStorage` — implement against any sync backend\n * (cookies, in-memory, sessionStorage). Async backends (IndexedDB, RN\n * AsyncStorage) are out of scope.\n */\nexport interface StorageAdapter {\n    getItem(key: string): string | null;\n    setItem(key: string, value: string): void;\n    removeItem(key: string): void;\n}\n\nexport type StorageOption = \"localStorage\" | \"sessionStorage\" | StorageAdapter;\n\nconst NOOP_STORAGE: StorageAdapter = {\n    getItem: () => null,\n    setItem: () => undefined,\n    removeItem: () => undefined,\n};\n\nexport function resolveStorage(\n    option: StorageOption | undefined,\n): StorageAdapter {\n    if (typeof window === \"undefined\") return NOOP_STORAGE;\n    if (!option || option === \"localStorage\") return window.localStorage;\n    if (option === \"sessionStorage\") return window.sessionStorage;\n    return option;\n}\n"],"mappings":"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","names":["import_react","import_react","expectedState"]}