{"version":3,"file":"create-web-socket.cjs","names":[],"sources":["../../src/ws/create-web-socket.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — reconnect with backoff, heartbeat,\n * the handshake and silence timers that detect a link which never fails out loud,\n * the send queue that survives a disconnect and the listener set that must be re-\n * attached to each new socket — one connection's lifetime, one closure. The queue\n * and the reconnect timer are the same decision seen twice.\n */\nimport {\n    backoffDelay,\n    isRejectionCloseCode,\n    shouldRetryClose,\n    type WebSocketLostReason,\n} from \"./resilience\";\nimport { decodeFrame } from \"../utils/json-frame\";\nimport type { SchemaIssue, SchemaLike } from \"../utils/schema-like\";\n\nexport type WebSocketStatus = \"idle\" | \"connecting\" | \"open\" | \"closing\" | \"closed\" | \"error\";\n\nexport interface WebSocketMessage<T> {\n    /** Parsed payload — validated when `schema` is set, JSON-decoded when possible, raw string otherwise. */\n    data: T;\n    /** The original `MessageEvent`. */\n    raw: MessageEvent;\n}\n\nexport interface CreateWebSocketOptions<T> {\n    /** Subprotocol(s) forwarded to the `WebSocket` constructor. */\n    protocols?: string | string[];\n    /** Max reconnect attempts. Default: 10. Pass 0 to disable. */\n    maxRetries?: number;\n    /** Initial backoff (ms). Doubles each attempt, capped at `maxBackoff`. Default: 1000. */\n    initialBackoff?: number;\n    /** Maximum backoff (ms). Default: 30000. */\n    maxBackoff?: number;\n    /**\n     * Fraction of each backoff delay added at random, 0–1. Default: 0.3.\n     *\n     * Matters when the *server* is what went down: every client retries on the\n     * same schedule, so the box comes back up into a synchronized stampede. Pass\n     * `0` for a fixed schedule.\n     */\n    jitter?: number;\n    /**\n     * How long one handshake may stay in `CONNECTING` before the attempt is\n     * abandoned and retried (ms). Default: 8000. Pass 0 to disable.\n     *\n     * A `WebSocket` that cannot reach its server does not necessarily fail: it\n     * sits in `CONNECTING` firing neither `open` nor `close` nor `error`. A retry\n     * chain built only on those events stops on its first hung attempt and never\n     * moves again — and hung, rather than refused, is precisely how a bad mobile\n     * link behaves, which is the case reconnection exists for.\n     */\n    handshakeTimeout?: number;\n    /**\n     * Silence tolerated on an open socket before the link is treated as dead (ms).\n     * Default: 0 (off).\n     *\n     * The socket only reports a connection that closes cleanly. A link that dies\n     * mid-flight leaves `readyState` at `OPEN` on this side with nothing ever\n     * arriving again, so silence is the only symptom available. The timer is\n     * re-armed by **any** inbound frame, not just by pings — traffic is traffic.\n     *\n     * Set it to a comfortable multiple of the server's ping interval (2.5× is a\n     * good default) so one dropped ping is not mistaken for an outage. When the\n     * server announces its own interval in the handshake, feed that back with\n     * {@link WebSocketController.setSilenceTimeout} instead of hard-coding the\n     * value on both ends.\n     */\n    silenceTimeout?: number;\n    /**\n     * Suspend the retry schedule while `navigator.onLine` is false, and resume on\n     * the `online` event. Default: true.\n     *\n     * Burning retries against a radio that is switched off is how a phone\n     * exhausts its budget inside a tunnel and gives up exactly when it comes out\n     * the other side.\n     */\n    waitForOnline?: boolean;\n    /**\n     * Ping interval (ms). When set, the client sends `pingPayload` periodically\n     * to keep the socket alive. Default: 0 (disabled).\n     *\n     * Leave it off against a `tempest-fastapi-sdk` server: that server pings on\n     * its own and answers a client-sent `{\"type\":\"ping\"}` with nothing, while a\n     * strict handler rejects the unknown frame. What it needs from the client\n     * is the `pong` reply, which `respondToPing` sends for you.\n     */\n    pingInterval?: number;\n    /** Payload sent on each ping. Default: `JSON.stringify({ type: \"ping\" })`. */\n    pingPayload?: string | Blob | BufferSource;\n    /**\n     * Reply to a server `{\"type\":\"ping\"}` with `pongPayload`. Default: true.\n     *\n     * `tempest-fastapi-sdk` closes a socket with code `4408` when no `pong`\n     * arrives within `WS_HEARTBEAT_TIMEOUT_SECONDS`, so a client that stays\n     * silent is dropped once per timeout. The ping is still forwarded to\n     * `onMessage` — the reply is sent before your handler runs.\n     */\n    respondToPing?: boolean;\n    /** Payload sent in reply to a server ping. Default: `JSON.stringify({ type: \"pong\" })`. */\n    pongPayload?: string | Blob | BufferSource;\n    /**\n     * Buffer payloads sent while the socket is not open and flush them on the\n     * next `open`. Default: false — `send()` returns false and drops.\n     *\n     * Without it, an action fired during reconnect backoff vanishes and the UI\n     * cannot tell \"never sent\" from \"sent and ignored\".\n     */\n    queueWhileClosed?: boolean;\n    /** Cap on buffered payloads when `queueWhileClosed` is on. Default: 100. */\n    maxQueuedMessages?: number;\n    /** Parse incoming frames. Default: JSON with raw-string fallback. */\n    parser?: (raw: string) => T;\n    /**\n     * A frame arrived that is not valid JSON, and no `parser` was supplied.\n     *\n     * Registering this drops the frame instead of delivering it: the previous\n     * behaviour handed `onMessage` the raw `string` announced as your message\n     * type, so the failure surfaced later, at the first property access, with\n     * nothing left pointing at the parse. Leave it out and that behaviour is\n     * kept, with a one-time warning in development builds.\n     */\n    onParseError?: (error: unknown, raw: string) => void;\n    /**\n     * Schema every decoded frame must satisfy, from zod, valibot, arktype or\n     * anything else exposing `~standard` or `.safeParse`.\n     *\n     * Without it nothing changes: the payload reaches `onMessage` announced as\n     * `T` on the strength of the type argument alone, which is a promise about\n     * the server that TypeScript cannot keep. With it, a frame that does not\n     * match is **not** delivered — the same rule `onParseError` already follows —\n     * and `onValidationError` hears why. The value delivered is the schema's\n     * output, so coercions and defaults are honoured.\n     *\n     * When `parser` is also supplied, it decodes first and the schema validates\n     * what it returned.\n     *\n     * A server ping is still answered when the schema drops it: the heartbeat is\n     * the transport's contract with the server, not the app's with its payload,\n     * and a socket that stops sending `pong` is closed with `4408` once per\n     * timeout. The validation itself must be synchronous — a frame is decoded\n     * inside the `message` handler and delivered from it, so an async schema\n     * would deliver frames in whatever order their validations settled; that\n     * case is reported through `onValidationError` instead of awaited.\n     */\n    schema?: SchemaLike<T>;\n    /**\n     * A frame was decoded but the `schema` refused it, so it was dropped.\n     *\n     * The one signal that does not depend on how the app's bundler resolves\n     * `process`: the one-time development warning behind `onParseError` needs\n     * `isDevBuild()` to be able to answer, and this callback is the app's own.\n     */\n    onValidationError?: (issues: SchemaIssue[], raw: string) => void;\n    onOpen?: (event: Event) => void;\n    onMessage?: (message: WebSocketMessage<T>) => void;\n    onClose?: (event: CloseEvent) => void;\n    onError?: (event: Event) => void;\n    onStatusChange?: (status: WebSocketStatus) => void;\n    /**\n     * A retry has been scheduled. `attempt` is 1-based, `total` is `maxRetries`.\n     *\n     * Reconnecting is not an error and reads badly as one: announcing every\n     * attempt puts a fresh \"the connection dropped\" in front of someone whose\n     * session is in the middle of coming back on its own. Show a quiet\n     * reconnecting state here and treat {@link CreateWebSocketOptions.onLost} as\n     * the failure.\n     */\n    onReconnecting?: (attempt: number, total: number) => void;\n    /**\n     * The socket is back up after at least one retry.\n     *\n     * Nothing is resumed for you: a server that keys state by connection sees a\n     * brand-new client, so this is where the caller re-subscribes, re-joins or\n     * refetches whatever the gap invalidated.\n     */\n    onReconnected?: () => void;\n    /**\n     * No further attempt will be made — `\"rejected\"` when the server refused the\n     * client outright (close code 4400–4499, minus the 4408 heartbeat timeout),\n     * `\"exhausted\"` when the schedule ran out.\n     *\n     * This is the one that deserves UI, because it is the only state the caller\n     * can act on: offer a \"try again\" that calls\n     * {@link WebSocketController.reconnect}.\n     */\n    onLost?: (reason: WebSocketLostReason) => void;\n}\n\nexport interface WebSocketController {\n    /** Send a payload over the current connection. No-op when not open. */\n    send: (payload: string | Blob | BufferSource) => boolean;\n    /** Close the connection and stop reconnecting. */\n    close: (code?: number, reason?: string) => void;\n    /** Force an immediate reconnect, resetting the retry counter. */\n    reconnect: () => void;\n    /**\n     * Change the silence watchdog at runtime, in ms. `0` disables it.\n     *\n     * For the common case where the server announces its heartbeat interval in\n     * the first frame, so the tolerated silence is not hard-coded on both ends:\n     *\n     * ```ts\n     * onMessage: ({ data }) => {\n     *   if (data.type === \"welcome\") socket.setSilenceTimeout(data.heartbeat_seconds * 2500);\n     * }\n     * ```\n     */\n    setSilenceTimeout: (ms: number) => void;\n    /**\n     * Resolves on the first successful open, rejects when the socket is lost\n     * before ever opening.\n     *\n     * Joining and dropping are different events: a call that never connected has\n     * to be reported, while one that dropped mid-session should reconnect\n     * quietly. Await this for the join, handle\n     * {@link CreateWebSocketOptions.onLost} for the drop. Pair it with\n     * `maxRetries: 0` when the first attempt should fail fast instead of\n     * spending the whole schedule on a server that is not there.\n     */\n    opened: Promise<void>;\n    /** Current connection status. */\n    readonly status: WebSocketStatus;\n}\n\n/**\n * Open a WebSocket that survives a bad network: exponential backoff with jitter,\n * a handshake timeout, a silence watchdog, optional heartbeat pings and typed\n * JSON parsing.\n *\n * Three failure modes are covered that an event-driven retry loop misses on its\n * own, because none of them fire an event: a handshake that hangs instead of\n * failing, an open socket whose link died mid-flight, and a device with its\n * radio off burning the retry budget. See `handshakeTimeout`, `silenceTimeout`\n * and `waitForOnline`.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Controller exposing `send`, `close`, `reconnect`, `setSilenceTimeout`,\n *   `opened` and `status`.\n *\n * @example\n * const socket = createWebSocket(url, {\n *   silenceTimeout: 75_000,\n *   onReconnecting: (n, total) => setBanner(`Reconectando ${n}/${total}…`),\n *   onReconnected: () => refetchEverything(),\n *   onLost: (reason) => setBanner(reason === \"rejected\" ? \"Acesso negado\" : \"Sem conexão\"),\n * });\n * await socket.opened;\n */\nexport function createWebSocket<T = unknown>(\n    url: string,\n    options: CreateWebSocketOptions<T> = {},\n): WebSocketController {\n    const {\n        protocols,\n        maxRetries = 10,\n        initialBackoff = 1000,\n        maxBackoff = 30000,\n        jitter = 0.3,\n        handshakeTimeout = 8000,\n        silenceTimeout = 0,\n        waitForOnline = true,\n        pingInterval = 0,\n        pingPayload = JSON.stringify({ type: \"ping\" }),\n        respondToPing = true,\n        pongPayload = JSON.stringify({ type: \"pong\" }),\n        queueWhileClosed = false,\n        maxQueuedMessages = 100,\n        parser,\n        onOpen,\n        onMessage,\n        onClose,\n        onError,\n        onParseError,\n        schema,\n        onValidationError,\n        onStatusChange,\n        onReconnecting,\n        onReconnected,\n        onLost,\n    } = options;\n\n    let socket: WebSocket | null = null;\n    let retryTimer: ReturnType<typeof setTimeout> | null = null;\n    let pingTimer: ReturnType<typeof setInterval> | null = null;\n    let handshakeTimer: ReturnType<typeof setTimeout> | null = null;\n    let silenceTimer: ReturnType<typeof setTimeout> | null = null;\n    let onlineListener: (() => void) | null = null;\n    let silenceWindow = silenceTimeout;\n    let retries = 0;\n    let status: WebSocketStatus = \"idle\";\n    let closed = false;\n    let everOpened = false;\n    const outbox: Array<string | Blob | BufferSource> = [];\n\n    let settleOpen: (() => void) | null = null;\n    let failOpen: ((error: Error) => void) | null = null;\n    const opened = new Promise<void>((resolve, reject) => {\n        settleOpen = resolve;\n        failOpen = reject;\n    });\n    opened.catch(() => undefined);\n\n    /** True for a decoded frame that is the server's heartbeat ping. */\n    function isServerPing(data: unknown): boolean {\n        return (\n            typeof data === \"object\" &&\n            data !== null &&\n            (data as { type?: unknown }).type === \"ping\"\n        );\n    }\n\n    /**\n     * Whether an undelivered frame was a server ping, read from its raw text.\n     *\n     * The delivered path tests the decoded payload, which is what `parser` was\n     * given the frame to produce. A frame `schema` refused never reaches that\n     * path, and a heartbeat the app's schema does not describe is the normal\n     * case rather than an exotic one — so the reply is decided from the wire\n     * text instead, since the server closes with `4408` when no `pong` arrives.\n     *\n     * The substring test in front keeps the ordinary frame at one scan: only a\n     * frame that mentions `\"ping\"` at all is worth parsing a second time.\n     *\n     * @param raw - The frame body as text.\n     * @returns Whether a `pong` is owed.\n     */\n    function isServerPingFrame(raw: string): boolean {\n        if (!raw.includes('\"ping\"')) return false;\n        try {\n            return isServerPing(JSON.parse(raw));\n        } catch {\n            return false;\n        }\n    }\n\n    /** Send everything buffered while the socket was down, oldest first. */\n    function flushOutbox(ws: WebSocket): void {\n        while (outbox.length > 0 && ws.readyState === WebSocket.OPEN) {\n            ws.send(outbox.shift()!);\n        }\n    }\n\n    function setStatus(next: WebSocketStatus): void {\n        if (status === next) return;\n        status = next;\n        onStatusChange?.(next);\n    }\n\n    function clearPing(): void {\n        if (pingTimer) {\n            clearInterval(pingTimer);\n            pingTimer = null;\n        }\n    }\n\n    function clearHandshake(): void {\n        if (handshakeTimer) {\n            clearTimeout(handshakeTimer);\n            handshakeTimer = null;\n        }\n    }\n\n    function clearSilence(): void {\n        if (silenceTimer) {\n            clearTimeout(silenceTimer);\n            silenceTimer = null;\n        }\n    }\n\n    function startPing(): void {\n        if (!pingInterval || pingInterval <= 0) return;\n        clearPing();\n        pingTimer = setInterval(() => {\n            if (socket?.readyState === WebSocket.OPEN) {\n                socket.send(pingPayload);\n            }\n        }, pingInterval);\n    }\n\n    /**\n     * Restart the silence timer, because something just arrived.\n     *\n     * Armed off any inbound frame rather than off pongs alone: a busy exchange\n     * already proves the link is carrying data, and a protocol whose pings the\n     * client never sees would otherwise reconnect in the middle of working\n     * traffic.\n     */\n    function armSilence(): void {\n        clearSilence();\n        if (closed || silenceWindow <= 0) return;\n        silenceTimer = setTimeout(onSilence, silenceWindow);\n    }\n\n    /**\n     * Treat a socket that went quiet as dead and start reconnecting.\n     *\n     * Handlers are detached before closing so the synthetic `close` does not also\n     * schedule a retry — that would advance the backoff twice for one failure and\n     * halve the time the connection is given to recover.\n     */\n    function onSilence(): void {\n        clearSilence();\n        if (closed || !socket) return;\n        detach(socket);\n        socket = null;\n        setStatus(\"closed\");\n        scheduleReconnect();\n    }\n\n    /**\n     * Abandon a handshake that never resolved either way.\n     *\n     * The socket is closed while still `CONNECTING`, which is the one case the\n     * console warns about — and the right trade here, because the alternative is\n     * deferring the close to an `open` event that by definition is not coming.\n     */\n    function abandonHandshake(ws: WebSocket): void {\n        clearHandshake();\n        if (closed || ws.readyState !== WebSocket.CONNECTING) return;\n        detach(ws);\n        if (socket === ws) socket = null;\n        setStatus(\"closed\");\n        scheduleReconnect();\n    }\n\n    /** Drop every handler and close, so the socket can die without being heard. */\n    function detach(ws: WebSocket): void {\n        ws.onopen = null;\n        ws.onmessage = null;\n        ws.onerror = null;\n        ws.onclose = null;\n        try {\n            ws.close();\n        } catch {\n            /* already unusable — nothing to release and nothing to report */\n        }\n    }\n\n    /** Stop for good, telling the caller which of the two dead ends it is. */\n    function lose(reason: WebSocketLostReason): void {\n        clearSilence();\n        clearHandshake();\n        clearNetworkWait();\n        if (reason === \"rejected\") closed = true;\n        setStatus(\"error\");\n        if (!everOpened && failOpen) {\n            const reject = failOpen;\n            failOpen = null;\n            settleOpen = null;\n            reject(new Error(`websocket_${reason}`));\n        }\n        onLost?.(reason);\n    }\n\n    /**\n     * Queue the next attempt, or wait for the network when there is none.\n     *\n     * While the browser reports no connectivity the schedule is suspended and the\n     * `online` event drives the next attempt instead, so a device in a tunnel\n     * does not spend its whole budget before coming out the other side.\n     */\n    function scheduleReconnect(): void {\n        if (closed) return;\n        if (retries >= maxRetries) {\n            lose(\"exhausted\");\n            return;\n        }\n        const delay = backoffDelay(retries, { initialBackoff, maxBackoff, jitter });\n        retries += 1;\n        onReconnecting?.(retries, maxRetries);\n\n        if (waitForOnline && typeof navigator !== \"undefined\" && navigator.onLine === false) {\n            waitForNetwork();\n            return;\n        }\n        retryTimer = setTimeout(connect, delay);\n    }\n\n    function waitForNetwork(): void {\n        if (onlineListener || typeof window === \"undefined\") return;\n        const listener = (): void => {\n            clearNetworkWait();\n            if (!closed) connect();\n        };\n        onlineListener = listener;\n        window.addEventListener(\"online\", listener);\n    }\n\n    function clearNetworkWait(): void {\n        if (!onlineListener || typeof window === \"undefined\") return;\n        window.removeEventListener(\"online\", onlineListener);\n        onlineListener = null;\n    }\n\n    /**\n     * Open a socket, replacing whatever is there.\n     *\n     * The handshake timer is cleared first because it belongs to the socket\n     * being replaced, and it holds a reference to it: left armed, it fires later\n     * against a connection nobody is waiting for, clears the *new* socket's\n     * timer on its way through, and schedules a retry that drops a connection\n     * still in flight. `reconnect()` on a hung socket is the path that reaches\n     * it.\n     */\n    function connect(): void {\n        if (closed) return;\n        retryTimer = null;\n        clearHandshake();\n        if (socket) {\n            const previous = socket;\n            previous.onmessage = null;\n            previous.onclose = null;\n            previous.onerror = null;\n            if (previous.readyState !== WebSocket.CONNECTING) previous.onopen = null;\n            closeSocket(previous);\n        }\n        setStatus(\"connecting\");\n\n        const ws = new WebSocket(url, protocols);\n        socket = ws;\n\n        if (handshakeTimeout > 0) {\n            handshakeTimer = setTimeout(() => abandonHandshake(ws), handshakeTimeout);\n        }\n\n        ws.onopen = (event) => {\n            clearHandshake();\n            const recovered = retries > 0;\n            retries = 0;\n            everOpened = true;\n            setStatus(\"open\");\n            startPing();\n            armSilence();\n            flushOutbox(ws);\n            if (settleOpen) {\n                const resolve = settleOpen;\n                settleOpen = null;\n                failOpen = null;\n                resolve();\n            }\n            onOpen?.(event);\n            if (recovered) onReconnected?.();\n        };\n\n        ws.onmessage = (event) => {\n            armSilence();\n            const raw = typeof event.data === \"string\" ? event.data : \"\";\n            const decoded = decodeFrame<T>(raw, \"createWebSocket\", {\n                parser,\n                onParseError,\n                schema,\n                onValidationError,\n            });\n            if (!decoded.delivered) {\n                if (respondToPing && ws.readyState === WebSocket.OPEN && isServerPingFrame(raw)) {\n                    ws.send(pongPayload);\n                }\n                return;\n            }\n            const data = decoded.data;\n            if (respondToPing && isServerPing(data) && ws.readyState === WebSocket.OPEN) {\n                ws.send(pongPayload);\n            }\n            onMessage?.({ data, raw: event });\n        };\n\n        ws.onerror = (event) => {\n            onError?.(event);\n        };\n\n        /**\n         * Classify the close before deciding anything.\n         *\n         * Three outcomes, in order: a refusal never gets better by trying again;\n         * a died-in-flight or temporarily-unavailable close is retried; an\n         * ordinary goodbye (a clean 1000) is the session ending on purpose and\n         * deserves no error. The one exception is a goodbye on a socket that\n         * never opened — the server hung up during the handshake, which the\n         * caller awaiting `opened` has to hear about.\n         */\n        ws.onclose = (event) => {\n            clearHandshake();\n            clearPing();\n            clearSilence();\n            onClose?.(event);\n            socket = null;\n            setStatus(\"closed\");\n            if (closed) return;\n            if (isRejectionCloseCode(event.code)) {\n                lose(\"rejected\");\n                return;\n            }\n            if (shouldRetryClose(event.code, event.wasClean)) {\n                scheduleReconnect();\n                return;\n            }\n            if (!everOpened) lose(\"rejected\");\n        };\n    }\n\n    function send(payload: string | Blob | BufferSource): boolean {\n        if (socket?.readyState === WebSocket.OPEN) {\n            socket.send(payload);\n            return true;\n        }\n        if (!queueWhileClosed || closed) return false;\n        if (outbox.length >= maxQueuedMessages) outbox.shift();\n        outbox.push(payload);\n        return true;\n    }\n\n    function close(code?: number, reason?: string): void {\n        closed = true;\n        if (retryTimer) {\n            clearTimeout(retryTimer);\n            retryTimer = null;\n        }\n        clearPing();\n        clearHandshake();\n        clearSilence();\n        clearNetworkWait();\n        retries = 0;\n        outbox.length = 0;\n        if (failOpen) {\n            const reject = failOpen;\n            failOpen = null;\n            settleOpen = null;\n            reject(new Error(\"websocket_closed\"));\n        }\n        if (socket) {\n            setStatus(\"closing\");\n            closeSocket(socket, code, reason);\n            socket = null;\n        }\n        setStatus(\"closed\");\n    }\n\n    /**\n     * Close a socket without the \"closed before the connection is established\"\n     * console warning.\n     *\n     * A socket still in `CONNECTING` cannot be closed cleanly — the browser\n     * logs that warning on every attempt. React's StrictMode mounts, unmounts\n     * and remounts each component in development, so the first socket is\n     * always torn down mid-handshake and the message shows up in every dev\n     * session of every app using the hook. Deferring the close to `onopen`\n     * costs one round trip and keeps the console usable.\n     */\n    function closeSocket(ws: WebSocket, code?: number, reason?: string): void {\n        if (ws.readyState === WebSocket.CONNECTING) {\n            ws.onopen = () => ws.close(code, reason);\n            ws.onmessage = null;\n            ws.onerror = null;\n            ws.onclose = null;\n            return;\n        }\n        ws.close(code, reason);\n    }\n\n    function reconnect(): void {\n        if (retryTimer) {\n            clearTimeout(retryTimer);\n            retryTimer = null;\n        }\n        clearNetworkWait();\n        retries = 0;\n        closed = false;\n        connect();\n    }\n\n    function setSilenceTimeout(ms: number): void {\n        silenceWindow = Number.isFinite(ms) && ms > 0 ? ms : 0;\n        if (socket?.readyState === WebSocket.OPEN) armSilence();\n        else clearSilence();\n    }\n\n    connect();\n\n    return {\n        send,\n        close,\n        reconnect,\n        setSilenceTimeout,\n        opened,\n        get status() {\n            return status;\n        },\n    };\n}\n"],"mappings":"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"}