@solana/react 7.0.0 → 7.1.0-canary-20260812160611

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +117 -39
  2. package/dist/index.browser.cjs +64 -0
  3. package/dist/index.browser.cjs.map +1 -1
  4. package/dist/index.browser.mjs +58 -1
  5. package/dist/index.browser.mjs.map +1 -1
  6. package/dist/index.native.mjs +58 -1
  7. package/dist/index.native.mjs.map +1 -1
  8. package/dist/index.node.cjs +64 -0
  9. package/dist/index.node.cjs.map +1 -1
  10. package/dist/index.node.mjs +58 -1
  11. package/dist/index.node.mjs.map +1 -1
  12. package/dist/query.browser.cjs +5 -52
  13. package/dist/query.browser.cjs.map +1 -1
  14. package/dist/query.browser.mjs +5 -52
  15. package/dist/query.browser.mjs.map +1 -1
  16. package/dist/query.native.mjs +5 -52
  17. package/dist/query.native.mjs.map +1 -1
  18. package/dist/query.node.cjs +5 -52
  19. package/dist/query.node.cjs.map +1 -1
  20. package/dist/query.node.mjs +5 -52
  21. package/dist/query.node.mjs.map +1 -1
  22. package/dist/types/index.d.ts +7 -0
  23. package/dist/types/index.d.ts.map +1 -1
  24. package/dist/types/query/useSubscriptionQuery.d.ts +5 -5
  25. package/dist/types/query/useSubscriptionQuery.d.ts.map +1 -1
  26. package/dist/types/query/useTrackedDataQuery.d.ts +2 -2
  27. package/dist/types/query/useTrackedDataQuery.d.ts.map +1 -1
  28. package/dist/types/useAirdrop.d.ts +40 -0
  29. package/dist/types/useAirdrop.d.ts.map +1 -0
  30. package/dist/types/useClient.d.ts +7 -6
  31. package/dist/types/useClient.d.ts.map +1 -1
  32. package/dist/types/useIdentity.d.ts +28 -0
  33. package/dist/types/useIdentity.d.ts.map +1 -0
  34. package/dist/types/usePayer.d.ts +27 -0
  35. package/dist/types/usePayer.d.ts.map +1 -0
  36. package/dist/types/usePlanTransaction.d.ts +25 -0
  37. package/dist/types/usePlanTransaction.d.ts.map +1 -0
  38. package/dist/types/usePlanTransactions.d.ts +26 -0
  39. package/dist/types/usePlanTransactions.d.ts.map +1 -0
  40. package/dist/types/useSendTransaction.d.ts +29 -0
  41. package/dist/types/useSendTransaction.d.ts.map +1 -0
  42. package/dist/types/useSendTransactions.d.ts +28 -0
  43. package/dist/types/useSendTransactions.d.ts.map +1 -0
  44. package/package.json +10 -10
  45. package/src/index.ts +7 -0
  46. package/src/query/__tests__/useRequestQuery-test.browser.tsx +2 -2
  47. package/src/query/__tests__/useSubscriptionQuery-test.browser.tsx +7 -9
  48. package/src/query/__tests__/useTrackedDataQuery-test.browser.tsx +3 -3
  49. package/src/query/useSubscriptionQuery.ts +9 -7
  50. package/src/query/useTrackedDataQuery.ts +9 -4
  51. package/src/swr/__tests__/useRequestSWR-test.browser.tsx +4 -6
  52. package/src/useAirdrop.ts +45 -0
  53. package/src/useClient.ts +7 -6
  54. package/src/useIdentity.ts +47 -0
  55. package/src/usePayer.ts +44 -0
  56. package/src/usePlanTransaction.ts +30 -0
  57. package/src/usePlanTransactions.ts +31 -0
  58. package/src/useSendTransaction.ts +34 -0
  59. package/src/useSendTransactions.ts +33 -0
  60. package/dist/types/query/bridgeStoreToAsyncIterable.d.ts +0 -39
  61. package/dist/types/query/bridgeStoreToAsyncIterable.d.ts.map +0 -1
  62. package/src/query/__tests__/bridgeStoreToAsyncIterable-test.ts +0 -188
  63. package/src/query/bridgeStoreToAsyncIterable.ts +0 -105
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/query/useRequestQuery.ts","../src/query/bridgeStoreToAsyncIterable.ts","../src/query/useSubscriptionQuery.ts","../src/query/useTrackedDataQuery.ts"],"names":["useCallback","useQuery","experimental_streamedQuery"],"mappings":"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","file":"query.native.mjs","sourcesContent":["import type { ReactiveActionSource } from '@solana/kit';\nimport {\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\n\n/**\n * Options accepted by {@link useRequestQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseRequestQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useRequest`. Accepts the same source shape — a\n * {@link ReactiveActionSource} (satisfied by `PendingRpcRequest<T>`) or a\n * `(signal: AbortSignal) => Promise<T>` function — and routes it through TanStack Query's cache, so\n * components reading the same `key` dedupe into one in-flight request and the result becomes\n * Suspense-capable and visible in TanStack Query's devtools.\n *\n * The query's `queryFn` is handed TanStack Query's own `AbortSignal`, which aborts when the query\n * is cancelled (unmount, garbage collection, or a superseding refetch). That signal is threaded\n * into the source so cancellation propagates all the way down. When a `getAbortSignal` factory is\n * also supplied (e.g. for a per-attempt timeout), the two signals are combined via\n * {@link AbortSignal.any} — aborting either one cancels the attempt.\n *\n * Pass `null` for `source` to disable — useful when one of the source's inputs isn't yet known.\n * This maps to TanStack's `enabled: false` and combines with any `enabled` you pass yourself (the\n * query runs only when the source is non-null *and* your `enabled` is not `false`). Use\n * `result.refetch()` to refresh.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project the cached value into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function LatestBlockhash() {\n * const client = useClient<ClientWithRpc<GetLatestBlockhashApi>>();\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['latestBlockhash'],\n * client.rpc.getLatestBlockhash(),\n * { getAbortSignal: () => AbortSignal.timeout(5_000) },\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>Blockhash: {data!.value.blockhash}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async call. The source can be an inline closure recreated\n * // each render; the hook reads the latest one when the query fires and keys the cache off `key`,\n * // so there's no need to memoize it.\n * function Profile({ userId }: { userId: string }) {\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['users', userId],\n * (signal: AbortSignal) => fetch(`/api/users/${userId}`, { signal }).then(r => r.json()),\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>{data!.name}</p>;\n * }\n * ```\n *\n * @see {@link UseRequestQueryOptions}\n */\nexport function useRequestQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveActionSource<T> | ((signal: AbortSignal) => Promise<T>) | null,\n options?: UseRequestQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline closure passed each render\n // doesn't change the queryFn's identity. The queryFn reads the latest values from the refs when\n // it fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback(\n async ({ signal }: QueryFunctionContext): Promise<T> => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't schedule\n // this queryFn. This branch should be unreachable.\n throw new Error('useRequestQuery: the queryFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's signal\n // (when present) so aborting either cancels the attempt.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return await current(combinedSignal);\n }\n return await current.reactiveStore().withSignal(combinedSignal).dispatchAsync();\n // `sourceRef` and `getAbortSignalRef` are stable refs from `useLatest`, so listing them\n // leaves `queryFn` referentially stable across renders — TanStack keys off `key`, not\n // the queryFn identity, but a stable queryFn avoids needless churn.\n },\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* the caller hasn't\n // disabled it.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import {\n type ReactiveStreamStore,\n SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR,\n SolanaError,\n} from '@solana/kit';\n\n/**\n * Bridges a {@link ReactiveStreamStore} into the `AsyncIterable` contract that TanStack Query's\n * `experimental_streamedQuery` consumes. The TanStack twin of `bridgeStoreToSwr`: where the SWR\n * bridge pushes each store update into a `next` callback, this one exposes a *pull*-based async\n * iterable, since `streamedQuery` drives the stream by `for await`-ing it.\n *\n * Subscribes to the store, `connect()`s it bound to `signal`, and yields its unified lifecycle:\n * - `loaded` → yields the value, unless an optional `shouldYield` predicate rejects it. Latest-wins:\n * if several notifications land between pulls, only the most recent unconsumed value is yielded (a\n * subscription consumer wants the freshest state, not a backlog).\n * - `error` → throws, so the `streamedQuery` queryFn rejects and the query surfaces `error`.\n * Substitutes a {@link SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR} sentinel when the\n * store reports an error with a nullish payload (mirrors `bridgeStoreToSwr`). An error takes\n * precedence over a buffered value: if a `loaded` value is still pending when an `error` arrives,\n * that value is dropped and the error propagates (once errored, stop yielding).\n * - `signal` aborts → ends the iterable cleanly (no error). An abort means teardown — unmount, a\n * superseding refetch, or a caller-supplied deadline — so the subscription stops rather than\n * failing.\n *\n * Whichever way the iterable ends — value exhaustion, error, or abort — the `finally` block\n * unsubscribes and `reset()`s the store, aborting the active connection. The store the caller hands\n * in is owned by this bridge for the iterable's lifetime.\n *\n * @typeParam T - The notification type emitted by the store.\n *\n * @param store - An idle stream store to drive.\n * @param signal - The abort signal to bind the connection to (TanStack's query-cancellation signal,\n * optionally combined with a caller-supplied one). Aborting it tears the stream down.\n * @param shouldYield - Optional gate run against each `loaded` value before it is yielded. Return\n * `false` to drop the value. When omitted, every loaded value is yielded.\n *\n * @returns An `AsyncIterable<T>` that yields each store value until the store errors or `signal`\n * aborts.\n *\n * @internal\n */\nexport function bridgeStoreToAsyncIterable<T>(\n store: ReactiveStreamStore<T>,\n signal: AbortSignal,\n shouldYield?: (value: T) => boolean,\n): AsyncIterable<T> {\n return {\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n // Latest-wins single-slot buffer plus a one-shot \"something changed\" deferred the loop\n // parks on. `wake()` resolves the current deferred and arms a fresh one for the next park.\n let latest: { readonly value: T } | undefined;\n let failure: { readonly error: unknown } | undefined;\n let deferred = Promise.withResolvers<void>();\n const wake = () => {\n const { resolve } = deferred;\n deferred = Promise.withResolvers<void>();\n resolve();\n };\n\n const onChange = () => {\n const state = store.getState();\n if (state.status === 'loaded') {\n // Drop a value the gate rejects. The stream parks again rather than yielding it.\n if (shouldYield && !shouldYield(state.data)) return;\n latest = { value: state.data };\n wake();\n } else if (state.status === 'error') {\n // A nullish error would otherwise surface as a value-less success; substitute a\n // sentinel so the failure propagates, matching `bridgeStoreToSwr`.\n failure = {\n error: state.error ?? new SolanaError(SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR),\n };\n wake();\n }\n // `idle` / `loading` carry no value and no error — nothing to yield.\n };\n\n const onAbort = () => wake();\n signal.addEventListener('abort', onAbort, { once: true });\n const unsubscribe = store.subscribe(onChange);\n // Bind the connection to `signal` so an abort tears down the underlying stream too.\n store.withSignal(signal).connect();\n try {\n while (true) {\n // Abort wins over everything: an abort is teardown, so end cleanly without\n // surfacing the store's incidental abort-driven error state.\n if (signal.aborted) return;\n if (failure) throw failure.error;\n if (latest) {\n const { value } = latest;\n latest = undefined;\n yield value;\n continue;\n }\n await deferred.promise;\n }\n } finally {\n signal.removeEventListener('abort', onAbort);\n unsubscribe();\n store.reset();\n }\n },\n };\n}\n","import type { ReactiveStreamSource } from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\nimport { bridgeStoreToAsyncIterable } from './bridgeStoreToAsyncIterable';\n\n/**\n * Options accepted by {@link useSubscriptionQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseSubscriptionQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useSubscription`, for streams that have no one-shot\n * RPC fetch counterpart. Routes a long-lived stream through TanStack Query's cache via\n * `experimental_streamedQuery`, so components reading the same `key` share one underlying connection\n * and the stream participates in TanStack Query's cache and devtools.\n *\n * The source has the same two shapes as {@link useRequestQuery}, the pull-based analog of its\n * `(signal) => Promise<T>` arm: a {@link ReactiveStreamSource} (satisfied by an RPC subscription) or\n * a raw `(signal: AbortSignal) => AsyncIterable<T>` factory. `streamedQuery` is itself built on\n * `AsyncIterable`, so the factory's iterable is consumed directly; a `ReactiveStreamSource` is\n * adapted to one internally. The factory receives TanStack's query-cancellation signal (combined\n * with any `getAbortSignal` you supply) so a `fetch`- or socket-backed generator can bind\n * cancellation to it.\n *\n * `data` is the raw notification, exactly as the source emits it. For RPC subscriptions read\n * `data.value` and `data.context.slot` yourself.\n *\n * Because the stream never settles, the query stays in `fetchStatus: 'fetching'` for the\n * subscription's whole life — `isFetching` is permanently `true` and `isLoading` flips false\n * after the first notification. Read `data` / `error` / `status` and ignore `isFetching` for\n * subscriptions. `result.refetch()` reconnects: TanStack aborts the old signal (ending the prior\n * iterable, which resets its store) and opens a fresh stream. Fire and forget — for a never-ending\n * stream the returned promise never resolves, so don't `await refetch()` (a click handler that does\n * will hang forever).\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every notification is written to the cache live,\n * and the prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Pass `null` for `source` to disable — maps to TanStack's `enabled: false` and combines with any\n * `enabled` you pass (the query runs only when the source is non-null *and* your `enabled` is not\n * `false`).\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project each notification into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function SlotHeight() {\n * const client = useClient<ClientWithRpcSubscriptions<SlotNotificationsApi>>();\n * const { data, error } = useSubscriptionQuery(['slot'], client.rpcSubscriptions.slotNotifications());\n * if (error) return <p>Disconnected.</p>;\n * if (!data) return <p>Connecting…</p>;\n * return <p>Slot {String(data.slot)}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async iterable, threading the cancellation signal in.\n * function Ticker() {\n * const { data } = useSubscriptionQuery(['ticker'], (signal: AbortSignal) => streamPrices(signal));\n * return <p>{data ?? '…'}</p>;\n * }\n * ```\n *\n * @see {@link UseSubscriptionQueryOptions}\n * @see {@link useRequestQuery}\n */\nexport function useSubscriptionQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveStreamSource<T> | ((signal: AbortSignal) => AsyncIterable<T>) | null,\n options?: UseSubscriptionQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback<QueryFunction<T, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<T, T>` plus a replace-latest reducer types the cache value as the latest\n // scalar notification rather than `T[]`. `initialValue` only seeds the (ignored) reducer\n // accumulator before the first chunk, so its value is irrelevant — only its type matters.\n experimental_streamedQuery<T, T>({\n initialValue: undefined as T,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ signal }: QueryFunctionContext) => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useSubscriptionQuery: the streamFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return current(combinedSignal);\n }\n return bridgeStoreToAsyncIterable(current.reactiveStore(), combinedSignal);\n },\n })(context),\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* not disabled.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import { createReactiveStoreWithInitialValueAndSlotTracking, type SolanaRpcResponse } from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n useQueryClient,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\nimport type { TrackedDataSpec } from '../useTrackedData';\nimport { bridgeStoreToAsyncIterable } from './bridgeStoreToAsyncIterable';\n\n/**\n * Options accepted by {@link useTrackedDataQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam TItem - The unified item type produced by the spec's two mappers, surfaced as\n * `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied.\n */\nexport type UseTrackedDataQueryOptions<TItem, TError = unknown, TData = SolanaRpcResponse<TItem>> = Omit<\n UseQueryOptions<SolanaRpcResponse<TItem>, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useTrackedData`. Pairs a one-shot RPC fetch with an\n * ongoing subscription (slot-deduped by the underlying Kit primitive) and routes the unified stream\n * through TanStack Query's cache via `experimental_streamedQuery`, so components reading the same\n * `key` share one underlying connection and the stream participates in TanStack Query's cache and\n * devtools.\n *\n * `data` is the `SolanaRpcResponse<TItem>` envelope emitted by the underlying kit primitive — the\n * primitive's type guarantees the envelope shape, so callers can read `data.value` (the unified item\n * produced by the spec's mappers) and `data.context.slot` (the slot the store dedup'd on) directly.\n *\n * Pass a {@link TrackedDataSpec} keyed on whatever inputs it depends on; the hook reads the latest\n * one from a ref, so an inline spec recreated each render is fine (TanStack keys the cache off `key`,\n * not the spec identity — bump the `key` to swap specs). Pass `null` for `spec` to disable — maps to\n * TanStack's `enabled: false` and combines with any `enabled` you pass (the query runs only when the\n * spec is non-null *and* your `enabled` is not `false`).\n *\n * Because the subscription never settles, the query stays in `fetchStatus: 'fetching'` for its whole\n * life — `isFetching` is permanently `true` and `isLoading` flips false after the first value. Read\n * `data` / `error` / `status` and ignore `isFetching`. `result.refetch()` reconnects: TanStack aborts\n * the old signal (ending the prior iterable, which resets its store) and re-runs both the initial RPC\n * fetch and the subscription. Fire and forget — for a never-ending stream the returned promise never\n * resolves, so don't `await refetch()`.\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every value is written to the cache live, and the\n * prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Slot dedupe spans the TanStack cache, not just one store. The underlying primitive tracks a slot\n * high-water mark per store, but that mark dies when the store is disposed while the cache entry\n * survives. `refetch()` mints a fresh store (high-water reset to -1) while `refetchMode: 'append'`\n * keeps the prior envelope cached, so this hook bridges that gap: the reconnect's fresh store cannot\n * regress the cached envelope to an older slot — e.g. a lagging RPC node resolving the new initial\n * fetch behind the cached value is refused, and the warmer cached value stands until something newer\n * arrives.\n *\n * @typeParam TInitialValue - The value inside the initial RPC `SolanaRpcResponse` envelope.\n * @typeParam TStreamValue - The value inside subscription `SolanaRpcResponse` notifications.\n * @typeParam TItem - The unified item type produced by the two mappers, surfaced as `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied; pass a\n * `select: (data: SolanaRpcResponse<TItem>) => TData` option to project each value into a different\n * shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function AccountBalance({ address }: { address: Address }) {\n * const client = useClient<ClientWithRpc<GetBalanceApi> & ClientWithRpcSubscriptions<AccountNotificationsApi>>();\n * const spec = useMemo(() => ({\n * initialValueSource: client.rpc.getBalance(address),\n * initialValueMapper: (lamports: bigint) => lamports,\n * streamSource: client.rpcSubscriptions.accountNotifications(address),\n * streamValueMapper: ({ lamports }: { lamports: bigint }) => lamports,\n * }), [client, address]);\n * const { data, error } = useTrackedDataQuery(['balance', address], spec);\n * if (error) return <p>Failed to load.</p>;\n * return <p>{data ? `${data.value} lamports at slot ${data.context.slot}` : 'Loading…'}</p>;\n * }\n * ```\n *\n * @see {@link UseTrackedDataQueryOptions}\n * @see {@link useSubscriptionQuery}\n */\nexport function useTrackedDataQuery<\n TInitialValue,\n TStreamValue,\n TItem,\n TError = unknown,\n TData = SolanaRpcResponse<TItem>,\n>(\n key: QueryKey,\n spec: TrackedDataSpec<TInitialValue, TStreamValue, TItem> | null,\n options?: UseTrackedDataQueryOptions<TItem, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the spec and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const specRef = useLatest(spec);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n // The cache outlives the per-attempt store, but the store's slot high-water mark does not — a\n // fresh store (remount, refetch) starts its window at -1. The streamFn reads the currently\n // cached envelope through this client to refuse any value older than what the cache already\n // holds. `useQueryClient` returns a stable reference, so listing it in the deps below is churn-free.\n const queryClient = useQueryClient();\n\n const queryFn = useCallback<QueryFunction<SolanaRpcResponse<TItem>, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<Envelope, Envelope>` plus a replace-latest reducer types the cache value\n // as the latest scalar envelope rather than an array. `initialValue` only seeds the\n // (ignored) reducer accumulator before the first chunk, so its value is irrelevant — only\n // its type matters.\n experimental_streamedQuery<SolanaRpcResponse<TItem>, SolanaRpcResponse<TItem>>({\n initialValue: undefined as unknown as SolanaRpcResponse<TItem>,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ queryKey, signal }: QueryFunctionContext) => {\n const current = specRef.current;\n if (current == null) {\n // Defensive: a null spec forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useTrackedDataQuery: the streamFn ran with a null spec');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n const store = createReactiveStoreWithInitialValueAndSlotTracking(current);\n // Refuse any envelope older than the slot the cache already holds. A fresh store\n // (e.g. after a remount) tracks slots from scratch, so without this it could\n // overwrite the cached value with an older one from a lagging RPC node.\n return bridgeStoreToAsyncIterable(store, combinedSignal, value => {\n const cachedSlot =\n queryClient.getQueryData<SolanaRpcResponse<TItem>>(queryKey)?.context.slot ?? -1n;\n return value.context.slot > cachedSlot;\n });\n },\n })(context),\n [getAbortSignalRef, queryClient, specRef],\n );\n\n return useQuery<SolanaRpcResponse<TItem>, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null spec disables the query (TanStack's `enabled: false`). Combine with the caller's own\n // `enabled` so the query runs only when the spec is present *and* not disabled.\n enabled: spec != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/query/useRequestQuery.ts","../src/query/useSubscriptionQuery.ts","../src/query/useTrackedDataQuery.ts"],"names":["useCallback","useQuery","experimental_streamedQuery","bridgeStoreToAsyncIterable"],"mappings":"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","file":"query.native.mjs","sourcesContent":["import type { ReactiveActionSource } from '@solana/kit';\nimport {\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\n\n/**\n * Options accepted by {@link useRequestQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseRequestQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useRequest`. Accepts the same source shape — a\n * {@link ReactiveActionSource} (satisfied by `PendingRpcRequest<T>`) or a\n * `(signal: AbortSignal) => Promise<T>` function — and routes it through TanStack Query's cache, so\n * components reading the same `key` dedupe into one in-flight request and the result becomes\n * Suspense-capable and visible in TanStack Query's devtools.\n *\n * The query's `queryFn` is handed TanStack Query's own `AbortSignal`, which aborts when the query\n * is cancelled (unmount, garbage collection, or a superseding refetch). That signal is threaded\n * into the source so cancellation propagates all the way down. When a `getAbortSignal` factory is\n * also supplied (e.g. for a per-attempt timeout), the two signals are combined via\n * {@link AbortSignal.any} — aborting either one cancels the attempt.\n *\n * Pass `null` for `source` to disable — useful when one of the source's inputs isn't yet known.\n * This maps to TanStack's `enabled: false` and combines with any `enabled` you pass yourself (the\n * query runs only when the source is non-null *and* your `enabled` is not `false`). Use\n * `result.refetch()` to refresh.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project the cached value into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function LatestBlockhash() {\n * const client = useClient<ClientWithRpc<GetLatestBlockhashApi>>();\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['latestBlockhash'],\n * client.rpc.getLatestBlockhash(),\n * { getAbortSignal: () => AbortSignal.timeout(5_000) },\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>Blockhash: {data!.value.blockhash}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async call. The source can be an inline closure recreated\n * // each render; the hook reads the latest one when the query fires and keys the cache off `key`,\n * // so there's no need to memoize it.\n * function Profile({ userId }: { userId: string }) {\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['users', userId],\n * (signal: AbortSignal) => fetch(`/api/users/${userId}`, { signal }).then(r => r.json()),\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>{data!.name}</p>;\n * }\n * ```\n *\n * @see {@link UseRequestQueryOptions}\n */\nexport function useRequestQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveActionSource<T> | ((signal: AbortSignal) => Promise<T>) | null,\n options?: UseRequestQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline closure passed each render\n // doesn't change the queryFn's identity. The queryFn reads the latest values from the refs when\n // it fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback(\n async ({ signal }: QueryFunctionContext): Promise<T> => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't schedule\n // this queryFn. This branch should be unreachable.\n throw new Error('useRequestQuery: the queryFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's signal\n // (when present) so aborting either cancels the attempt.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return await current(combinedSignal);\n }\n return await current.reactiveStore().withSignal(combinedSignal).dispatchAsync();\n // `sourceRef` and `getAbortSignalRef` are stable refs from `useLatest`, so listing them\n // leaves `queryFn` referentially stable across renders — TanStack keys off `key`, not\n // the queryFn identity, but a stable queryFn avoids needless churn.\n },\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* the caller hasn't\n // disabled it.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import { bridgeStoreToAsyncIterable, type ReactiveStreamSource } from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\n\n/**\n * Options accepted by {@link useSubscriptionQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseSubscriptionQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useSubscription`, for streams that have no one-shot\n * RPC fetch counterpart. Routes a long-lived stream through TanStack Query's cache via\n * `experimental_streamedQuery`, so components reading the same `key` share one underlying connection\n * and the stream participates in TanStack Query's cache and devtools.\n *\n * The source has the same two shapes as {@link useRequestQuery}, the pull-based analog of its\n * `(signal) => Promise<T>` arm: a {@link ReactiveStreamSource} (satisfied by an RPC subscription) or\n * a raw `(signal: AbortSignal) => AsyncIterable<T>` factory. `streamedQuery` is itself built on\n * `AsyncIterable`, so the factory's iterable is consumed directly; a `ReactiveStreamSource` is\n * adapted to one internally. The factory receives TanStack's query-cancellation signal (combined\n * with any `getAbortSignal` you supply) so a `fetch`- or socket-backed generator can bind\n * cancellation to it.\n *\n * `data` is the raw notification, exactly as the source emits it. For RPC subscriptions read\n * `data.value` and `data.context.slot` yourself.\n *\n * Because the stream never settles, the query stays in `fetchStatus: 'fetching'` for the\n * subscription's whole life — `isFetching` is permanently `true` and `isLoading` flips false\n * after the first notification. Read `data` / `error` / `status` and ignore `isFetching` for\n * subscriptions. `result.refetch()` reconnects: TanStack aborts the old signal (tearing the prior\n * stream's connection down) and opens a fresh stream. Fire and forget — for a never-ending stream\n * the returned promise never resolves, so don't `await refetch()` (a click handler that does will\n * hang forever).\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every notification is written to the cache live,\n * and the prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Pass `null` for `source` to disable — maps to TanStack's `enabled: false` and combines with any\n * `enabled` you pass (the query runs only when the source is non-null *and* your `enabled` is not\n * `false`).\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project each notification into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function SlotHeight() {\n * const client = useClient<ClientWithRpcSubscriptions<SlotNotificationsApi>>();\n * const { data, error } = useSubscriptionQuery(['slot'], client.rpcSubscriptions.slotNotifications());\n * if (error) return <p>Disconnected.</p>;\n * if (!data) return <p>Connecting…</p>;\n * return <p>Slot {String(data.slot)}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async iterable, threading the cancellation signal in.\n * function Ticker() {\n * const { data } = useSubscriptionQuery(['ticker'], (signal: AbortSignal) => streamPrices(signal));\n * return <p>{data ?? '…'}</p>;\n * }\n * ```\n *\n * @see {@link UseSubscriptionQueryOptions}\n * @see {@link useRequestQuery}\n */\nexport function useSubscriptionQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveStreamSource<T> | ((signal: AbortSignal) => AsyncIterable<T>) | null,\n options?: UseSubscriptionQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback<QueryFunction<T, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<T, T>` plus a replace-latest reducer types the cache value as the latest\n // scalar notification rather than `T[]`. `initialValue` only seeds the (ignored) reducer\n // accumulator before the first chunk, so its value is irrelevant — only its type matters.\n experimental_streamedQuery<T, T>({\n initialValue: undefined as T,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ signal }: QueryFunctionContext) => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useSubscriptionQuery: the streamFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return current(combinedSignal);\n }\n const store = current.reactiveStore();\n // reset not needed, the connection tears down and the store gets GCed after abort\n store.withSignal(combinedSignal).connect();\n return bridgeStoreToAsyncIterable(store, combinedSignal);\n },\n })(context),\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* not disabled.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import {\n bridgeStoreToAsyncIterable,\n createReactiveStoreWithInitialValueAndSlotTracking,\n type SolanaRpcResponse,\n} from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n useQueryClient,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\nimport type { TrackedDataSpec } from '../useTrackedData';\n\n/**\n * Options accepted by {@link useTrackedDataQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam TItem - The unified item type produced by the spec's two mappers, surfaced as\n * `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied.\n */\nexport type UseTrackedDataQueryOptions<TItem, TError = unknown, TData = SolanaRpcResponse<TItem>> = Omit<\n UseQueryOptions<SolanaRpcResponse<TItem>, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useTrackedData`. Pairs a one-shot RPC fetch with an\n * ongoing subscription (slot-deduped by the underlying Kit primitive) and routes the unified stream\n * through TanStack Query's cache via `experimental_streamedQuery`, so components reading the same\n * `key` share one underlying connection and the stream participates in TanStack Query's cache and\n * devtools.\n *\n * `data` is the `SolanaRpcResponse<TItem>` envelope emitted by the underlying kit primitive — the\n * primitive's type guarantees the envelope shape, so callers can read `data.value` (the unified item\n * produced by the spec's mappers) and `data.context.slot` (the slot the store dedup'd on) directly.\n *\n * Pass a {@link TrackedDataSpec} keyed on whatever inputs it depends on; the hook reads the latest\n * one from a ref, so an inline spec recreated each render is fine (TanStack keys the cache off `key`,\n * not the spec identity — bump the `key` to swap specs). Pass `null` for `spec` to disable — maps to\n * TanStack's `enabled: false` and combines with any `enabled` you pass (the query runs only when the\n * spec is non-null *and* your `enabled` is not `false`).\n *\n * Because the subscription never settles, the query stays in `fetchStatus: 'fetching'` for its whole\n * life — `isFetching` is permanently `true` and `isLoading` flips false after the first value. Read\n * `data` / `error` / `status` and ignore `isFetching`. `result.refetch()` reconnects: TanStack aborts\n * the old signal (tearing the prior stream's connection down) and re-runs both the initial RPC fetch\n * and the subscription. Fire and forget — for a never-ending stream the returned promise never\n * resolves, so don't `await refetch()`.\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every value is written to the cache live, and the\n * prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Slot dedupe spans the TanStack cache, not just one store. The underlying primitive tracks a slot\n * high-water mark per store, but that mark dies when the store is disposed while the cache entry\n * survives. `refetch()` mints a fresh store (high-water reset to -1) while `refetchMode: 'append'`\n * keeps the prior envelope cached, so this hook bridges that gap: the reconnect's fresh store cannot\n * regress the cached envelope to an older slot — e.g. a lagging RPC node resolving the new initial\n * fetch behind the cached value is refused, and the warmer cached value stands until something newer\n * arrives.\n *\n * @typeParam TInitialValue - The value inside the initial RPC `SolanaRpcResponse` envelope.\n * @typeParam TStreamValue - The value inside subscription `SolanaRpcResponse` notifications.\n * @typeParam TItem - The unified item type produced by the two mappers, surfaced as `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied; pass a\n * `select: (data: SolanaRpcResponse<TItem>) => TData` option to project each value into a different\n * shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function AccountBalance({ address }: { address: Address }) {\n * const client = useClient<ClientWithRpc<GetBalanceApi> & ClientWithRpcSubscriptions<AccountNotificationsApi>>();\n * const spec = useMemo(() => ({\n * initialValueSource: client.rpc.getBalance(address),\n * initialValueMapper: (lamports: bigint) => lamports,\n * streamSource: client.rpcSubscriptions.accountNotifications(address),\n * streamValueMapper: ({ lamports }: { lamports: bigint }) => lamports,\n * }), [client, address]);\n * const { data, error } = useTrackedDataQuery(['balance', address], spec);\n * if (error) return <p>Failed to load.</p>;\n * return <p>{data ? `${data.value} lamports at slot ${data.context.slot}` : 'Loading…'}</p>;\n * }\n * ```\n *\n * @see {@link UseTrackedDataQueryOptions}\n * @see {@link useSubscriptionQuery}\n */\nexport function useTrackedDataQuery<\n TInitialValue,\n TStreamValue,\n TItem,\n TError = unknown,\n TData = SolanaRpcResponse<TItem>,\n>(\n key: QueryKey,\n spec: TrackedDataSpec<TInitialValue, TStreamValue, TItem> | null,\n options?: UseTrackedDataQueryOptions<TItem, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the spec and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const specRef = useLatest(spec);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n // The cache outlives the per-attempt store, but the store's slot high-water mark does not — a\n // fresh store (remount, refetch) starts its window at -1. The streamFn reads the currently\n // cached envelope through this client to refuse any value older than what the cache already\n // holds. `useQueryClient` returns a stable reference, so listing it in the deps below is churn-free.\n const queryClient = useQueryClient();\n\n const queryFn = useCallback<QueryFunction<SolanaRpcResponse<TItem>, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<Envelope, Envelope>` plus a replace-latest reducer types the cache value\n // as the latest scalar envelope rather than an array. `initialValue` only seeds the\n // (ignored) reducer accumulator before the first chunk, so its value is irrelevant — only\n // its type matters.\n experimental_streamedQuery<SolanaRpcResponse<TItem>, SolanaRpcResponse<TItem>>({\n initialValue: undefined as unknown as SolanaRpcResponse<TItem>,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ queryKey, signal }: QueryFunctionContext) => {\n const current = specRef.current;\n if (current == null) {\n // Defensive: a null spec forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useTrackedDataQuery: the streamFn ran with a null spec');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n const store = createReactiveStoreWithInitialValueAndSlotTracking(current);\n // reset not needed, the connection tears down and the store gets GCed after abort\n store.withSignal(combinedSignal).connect();\n // Refuse any envelope older than the slot the cache already holds. A fresh store\n // (e.g. after a remount) tracks slots from scratch, so without this it could\n // overwrite the cached value with an older one from a lagging RPC node.\n return bridgeStoreToAsyncIterable(store, combinedSignal, value => {\n const cachedSlot =\n queryClient.getQueryData<SolanaRpcResponse<TItem>>(queryKey)?.context.slot ?? -1n;\n return value.context.slot > cachedSlot;\n });\n },\n })(context),\n [getAbortSignalRef, queryClient, specRef],\n );\n\n return useQuery<SolanaRpcResponse<TItem>, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null spec disables the query (TanStack's `enabled: false`). Combine with the caller's own\n // `enabled` so the query runs only when the spec is present *and* not disabled.\n enabled: spec != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n"]}
@@ -46,56 +46,6 @@ function useRequestQuery(key, source, options) {
46
46
  queryKey: key
47
47
  });
48
48
  }
49
- function bridgeStoreToAsyncIterable(store, signal, shouldYield) {
50
- return {
51
- async *[Symbol.asyncIterator]() {
52
- let latest;
53
- let failure;
54
- let deferred = Promise.withResolvers();
55
- const wake = () => {
56
- const { resolve } = deferred;
57
- deferred = Promise.withResolvers();
58
- resolve();
59
- };
60
- const onChange = () => {
61
- const state = store.getState();
62
- if (state.status === "loaded") {
63
- if (shouldYield && !shouldYield(state.data)) return;
64
- latest = { value: state.data };
65
- wake();
66
- } else if (state.status === "error") {
67
- failure = {
68
- error: state.error ?? new kit.SolanaError(kit.SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR)
69
- };
70
- wake();
71
- }
72
- };
73
- const onAbort = () => wake();
74
- signal.addEventListener("abort", onAbort, { once: true });
75
- const unsubscribe = store.subscribe(onChange);
76
- store.withSignal(signal).connect();
77
- try {
78
- while (true) {
79
- if (signal.aborted) return;
80
- if (failure) throw failure.error;
81
- if (latest) {
82
- const { value } = latest;
83
- latest = void 0;
84
- yield value;
85
- continue;
86
- }
87
- await deferred.promise;
88
- }
89
- } finally {
90
- signal.removeEventListener("abort", onAbort);
91
- unsubscribe();
92
- store.reset();
93
- }
94
- }
95
- };
96
- }
97
-
98
- // src/query/useSubscriptionQuery.ts
99
49
  function useSubscriptionQuery(key, source, options) {
100
50
  const { getAbortSignal, ...queryOptions } = options ?? {};
101
51
  const sourceRef = useLatest(source);
@@ -124,7 +74,9 @@ function useSubscriptionQuery(key, source, options) {
124
74
  if (typeof current === "function") {
125
75
  return current(combinedSignal);
126
76
  }
127
- return bridgeStoreToAsyncIterable(current.reactiveStore(), combinedSignal);
77
+ const store = current.reactiveStore();
78
+ store.withSignal(combinedSignal).connect();
79
+ return kit.bridgeStoreToAsyncIterable(store, combinedSignal);
128
80
  }
129
81
  })(context)
130
82
  ),
@@ -171,7 +123,8 @@ function useTrackedDataQuery(key, spec, options) {
171
123
  const userSignal = getAbortSignalRef.current?.();
172
124
  const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;
173
125
  const store = kit.createReactiveStoreWithInitialValueAndSlotTracking(current);
174
- return bridgeStoreToAsyncIterable(store, combinedSignal, (value) => {
126
+ store.withSignal(combinedSignal).connect();
127
+ return kit.bridgeStoreToAsyncIterable(store, combinedSignal, (value) => {
175
128
  const cachedSlot = queryClient.getQueryData(queryKey)?.context.slot ?? -1n;
176
129
  return value.context.slot > cachedSlot;
177
130
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useIsomorphicLayoutEffect.ts","../src/useLatest.ts","../src/query/useRequestQuery.ts","../src/query/bridgeStoreToAsyncIterable.ts","../src/query/useSubscriptionQuery.ts","../src/query/useTrackedDataQuery.ts"],"names":["useLayoutEffect","useEffect","useRef","useCallback","useQuery","SolanaError","SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR","experimental_streamedQuery","useQueryClient","createReactiveStoreWithInitialValueAndSlotTracking"],"mappings":"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","file":"query.node.cjs","sourcesContent":["import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * `useLayoutEffect` warns when run on the server because layout effects only make sense after a\n * DOM is mounted. For our use, plain `useEffect` is functionally equivalent on the server (no\n * event handlers can fire mid-render anyway), so we pick at module load time and stay silent\n * during SSR.\n *\n * @internal\n */\nexport const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n","import { type MutableRefObject, useRef } from 'react';\n\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';\n\n/**\n * Hold `value` in a ref that is re-synced to the latest render's value after every commit.\n *\n * Lets a callback (or a store created once) read the freshest closure value at call time without\n * taking it as a dependency, so the consumer's own identity stays stable across renders. The sync\n * runs in a layout effect so the ref is current before passive effects and event handlers fire.\n *\n * @typeParam T - The type of the value being tracked.\n *\n * @returns A stable {@link MutableRefObject} whose `current` always reflects the most recent render.\n *\n * @internal\n */\nexport function useLatest<T>(value: T): MutableRefObject<T> {\n const ref = useRef(value);\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import type { ReactiveActionSource } from '@solana/kit';\nimport {\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\n\n/**\n * Options accepted by {@link useRequestQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseRequestQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useRequest`. Accepts the same source shape — a\n * {@link ReactiveActionSource} (satisfied by `PendingRpcRequest<T>`) or a\n * `(signal: AbortSignal) => Promise<T>` function — and routes it through TanStack Query's cache, so\n * components reading the same `key` dedupe into one in-flight request and the result becomes\n * Suspense-capable and visible in TanStack Query's devtools.\n *\n * The query's `queryFn` is handed TanStack Query's own `AbortSignal`, which aborts when the query\n * is cancelled (unmount, garbage collection, or a superseding refetch). That signal is threaded\n * into the source so cancellation propagates all the way down. When a `getAbortSignal` factory is\n * also supplied (e.g. for a per-attempt timeout), the two signals are combined via\n * {@link AbortSignal.any} — aborting either one cancels the attempt.\n *\n * Pass `null` for `source` to disable — useful when one of the source's inputs isn't yet known.\n * This maps to TanStack's `enabled: false` and combines with any `enabled` you pass yourself (the\n * query runs only when the source is non-null *and* your `enabled` is not `false`). Use\n * `result.refetch()` to refresh.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project the cached value into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function LatestBlockhash() {\n * const client = useClient<ClientWithRpc<GetLatestBlockhashApi>>();\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['latestBlockhash'],\n * client.rpc.getLatestBlockhash(),\n * { getAbortSignal: () => AbortSignal.timeout(5_000) },\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>Blockhash: {data!.value.blockhash}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async call. The source can be an inline closure recreated\n * // each render; the hook reads the latest one when the query fires and keys the cache off `key`,\n * // so there's no need to memoize it.\n * function Profile({ userId }: { userId: string }) {\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['users', userId],\n * (signal: AbortSignal) => fetch(`/api/users/${userId}`, { signal }).then(r => r.json()),\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>{data!.name}</p>;\n * }\n * ```\n *\n * @see {@link UseRequestQueryOptions}\n */\nexport function useRequestQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveActionSource<T> | ((signal: AbortSignal) => Promise<T>) | null,\n options?: UseRequestQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline closure passed each render\n // doesn't change the queryFn's identity. The queryFn reads the latest values from the refs when\n // it fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback(\n async ({ signal }: QueryFunctionContext): Promise<T> => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't schedule\n // this queryFn. This branch should be unreachable.\n throw new Error('useRequestQuery: the queryFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's signal\n // (when present) so aborting either cancels the attempt.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return await current(combinedSignal);\n }\n return await current.reactiveStore().withSignal(combinedSignal).dispatchAsync();\n // `sourceRef` and `getAbortSignalRef` are stable refs from `useLatest`, so listing them\n // leaves `queryFn` referentially stable across renders — TanStack keys off `key`, not\n // the queryFn identity, but a stable queryFn avoids needless churn.\n },\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* the caller hasn't\n // disabled it.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import {\n type ReactiveStreamStore,\n SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR,\n SolanaError,\n} from '@solana/kit';\n\n/**\n * Bridges a {@link ReactiveStreamStore} into the `AsyncIterable` contract that TanStack Query's\n * `experimental_streamedQuery` consumes. The TanStack twin of `bridgeStoreToSwr`: where the SWR\n * bridge pushes each store update into a `next` callback, this one exposes a *pull*-based async\n * iterable, since `streamedQuery` drives the stream by `for await`-ing it.\n *\n * Subscribes to the store, `connect()`s it bound to `signal`, and yields its unified lifecycle:\n * - `loaded` → yields the value, unless an optional `shouldYield` predicate rejects it. Latest-wins:\n * if several notifications land between pulls, only the most recent unconsumed value is yielded (a\n * subscription consumer wants the freshest state, not a backlog).\n * - `error` → throws, so the `streamedQuery` queryFn rejects and the query surfaces `error`.\n * Substitutes a {@link SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR} sentinel when the\n * store reports an error with a nullish payload (mirrors `bridgeStoreToSwr`). An error takes\n * precedence over a buffered value: if a `loaded` value is still pending when an `error` arrives,\n * that value is dropped and the error propagates (once errored, stop yielding).\n * - `signal` aborts → ends the iterable cleanly (no error). An abort means teardown — unmount, a\n * superseding refetch, or a caller-supplied deadline — so the subscription stops rather than\n * failing.\n *\n * Whichever way the iterable ends — value exhaustion, error, or abort — the `finally` block\n * unsubscribes and `reset()`s the store, aborting the active connection. The store the caller hands\n * in is owned by this bridge for the iterable's lifetime.\n *\n * @typeParam T - The notification type emitted by the store.\n *\n * @param store - An idle stream store to drive.\n * @param signal - The abort signal to bind the connection to (TanStack's query-cancellation signal,\n * optionally combined with a caller-supplied one). Aborting it tears the stream down.\n * @param shouldYield - Optional gate run against each `loaded` value before it is yielded. Return\n * `false` to drop the value. When omitted, every loaded value is yielded.\n *\n * @returns An `AsyncIterable<T>` that yields each store value until the store errors or `signal`\n * aborts.\n *\n * @internal\n */\nexport function bridgeStoreToAsyncIterable<T>(\n store: ReactiveStreamStore<T>,\n signal: AbortSignal,\n shouldYield?: (value: T) => boolean,\n): AsyncIterable<T> {\n return {\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n // Latest-wins single-slot buffer plus a one-shot \"something changed\" deferred the loop\n // parks on. `wake()` resolves the current deferred and arms a fresh one for the next park.\n let latest: { readonly value: T } | undefined;\n let failure: { readonly error: unknown } | undefined;\n let deferred = Promise.withResolvers<void>();\n const wake = () => {\n const { resolve } = deferred;\n deferred = Promise.withResolvers<void>();\n resolve();\n };\n\n const onChange = () => {\n const state = store.getState();\n if (state.status === 'loaded') {\n // Drop a value the gate rejects. The stream parks again rather than yielding it.\n if (shouldYield && !shouldYield(state.data)) return;\n latest = { value: state.data };\n wake();\n } else if (state.status === 'error') {\n // A nullish error would otherwise surface as a value-less success; substitute a\n // sentinel so the failure propagates, matching `bridgeStoreToSwr`.\n failure = {\n error: state.error ?? new SolanaError(SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR),\n };\n wake();\n }\n // `idle` / `loading` carry no value and no error — nothing to yield.\n };\n\n const onAbort = () => wake();\n signal.addEventListener('abort', onAbort, { once: true });\n const unsubscribe = store.subscribe(onChange);\n // Bind the connection to `signal` so an abort tears down the underlying stream too.\n store.withSignal(signal).connect();\n try {\n while (true) {\n // Abort wins over everything: an abort is teardown, so end cleanly without\n // surfacing the store's incidental abort-driven error state.\n if (signal.aborted) return;\n if (failure) throw failure.error;\n if (latest) {\n const { value } = latest;\n latest = undefined;\n yield value;\n continue;\n }\n await deferred.promise;\n }\n } finally {\n signal.removeEventListener('abort', onAbort);\n unsubscribe();\n store.reset();\n }\n },\n };\n}\n","import type { ReactiveStreamSource } from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\nimport { bridgeStoreToAsyncIterable } from './bridgeStoreToAsyncIterable';\n\n/**\n * Options accepted by {@link useSubscriptionQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseSubscriptionQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useSubscription`, for streams that have no one-shot\n * RPC fetch counterpart. Routes a long-lived stream through TanStack Query's cache via\n * `experimental_streamedQuery`, so components reading the same `key` share one underlying connection\n * and the stream participates in TanStack Query's cache and devtools.\n *\n * The source has the same two shapes as {@link useRequestQuery}, the pull-based analog of its\n * `(signal) => Promise<T>` arm: a {@link ReactiveStreamSource} (satisfied by an RPC subscription) or\n * a raw `(signal: AbortSignal) => AsyncIterable<T>` factory. `streamedQuery` is itself built on\n * `AsyncIterable`, so the factory's iterable is consumed directly; a `ReactiveStreamSource` is\n * adapted to one internally. The factory receives TanStack's query-cancellation signal (combined\n * with any `getAbortSignal` you supply) so a `fetch`- or socket-backed generator can bind\n * cancellation to it.\n *\n * `data` is the raw notification, exactly as the source emits it. For RPC subscriptions read\n * `data.value` and `data.context.slot` yourself.\n *\n * Because the stream never settles, the query stays in `fetchStatus: 'fetching'` for the\n * subscription's whole life — `isFetching` is permanently `true` and `isLoading` flips false\n * after the first notification. Read `data` / `error` / `status` and ignore `isFetching` for\n * subscriptions. `result.refetch()` reconnects: TanStack aborts the old signal (ending the prior\n * iterable, which resets its store) and opens a fresh stream. Fire and forget — for a never-ending\n * stream the returned promise never resolves, so don't `await refetch()` (a click handler that does\n * will hang forever).\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every notification is written to the cache live,\n * and the prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Pass `null` for `source` to disable — maps to TanStack's `enabled: false` and combines with any\n * `enabled` you pass (the query runs only when the source is non-null *and* your `enabled` is not\n * `false`).\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project each notification into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function SlotHeight() {\n * const client = useClient<ClientWithRpcSubscriptions<SlotNotificationsApi>>();\n * const { data, error } = useSubscriptionQuery(['slot'], client.rpcSubscriptions.slotNotifications());\n * if (error) return <p>Disconnected.</p>;\n * if (!data) return <p>Connecting…</p>;\n * return <p>Slot {String(data.slot)}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async iterable, threading the cancellation signal in.\n * function Ticker() {\n * const { data } = useSubscriptionQuery(['ticker'], (signal: AbortSignal) => streamPrices(signal));\n * return <p>{data ?? '…'}</p>;\n * }\n * ```\n *\n * @see {@link UseSubscriptionQueryOptions}\n * @see {@link useRequestQuery}\n */\nexport function useSubscriptionQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveStreamSource<T> | ((signal: AbortSignal) => AsyncIterable<T>) | null,\n options?: UseSubscriptionQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback<QueryFunction<T, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<T, T>` plus a replace-latest reducer types the cache value as the latest\n // scalar notification rather than `T[]`. `initialValue` only seeds the (ignored) reducer\n // accumulator before the first chunk, so its value is irrelevant — only its type matters.\n experimental_streamedQuery<T, T>({\n initialValue: undefined as T,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ signal }: QueryFunctionContext) => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useSubscriptionQuery: the streamFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return current(combinedSignal);\n }\n return bridgeStoreToAsyncIterable(current.reactiveStore(), combinedSignal);\n },\n })(context),\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* not disabled.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import { createReactiveStoreWithInitialValueAndSlotTracking, type SolanaRpcResponse } from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n useQueryClient,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\nimport type { TrackedDataSpec } from '../useTrackedData';\nimport { bridgeStoreToAsyncIterable } from './bridgeStoreToAsyncIterable';\n\n/**\n * Options accepted by {@link useTrackedDataQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam TItem - The unified item type produced by the spec's two mappers, surfaced as\n * `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied.\n */\nexport type UseTrackedDataQueryOptions<TItem, TError = unknown, TData = SolanaRpcResponse<TItem>> = Omit<\n UseQueryOptions<SolanaRpcResponse<TItem>, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useTrackedData`. Pairs a one-shot RPC fetch with an\n * ongoing subscription (slot-deduped by the underlying Kit primitive) and routes the unified stream\n * through TanStack Query's cache via `experimental_streamedQuery`, so components reading the same\n * `key` share one underlying connection and the stream participates in TanStack Query's cache and\n * devtools.\n *\n * `data` is the `SolanaRpcResponse<TItem>` envelope emitted by the underlying kit primitive — the\n * primitive's type guarantees the envelope shape, so callers can read `data.value` (the unified item\n * produced by the spec's mappers) and `data.context.slot` (the slot the store dedup'd on) directly.\n *\n * Pass a {@link TrackedDataSpec} keyed on whatever inputs it depends on; the hook reads the latest\n * one from a ref, so an inline spec recreated each render is fine (TanStack keys the cache off `key`,\n * not the spec identity — bump the `key` to swap specs). Pass `null` for `spec` to disable — maps to\n * TanStack's `enabled: false` and combines with any `enabled` you pass (the query runs only when the\n * spec is non-null *and* your `enabled` is not `false`).\n *\n * Because the subscription never settles, the query stays in `fetchStatus: 'fetching'` for its whole\n * life — `isFetching` is permanently `true` and `isLoading` flips false after the first value. Read\n * `data` / `error` / `status` and ignore `isFetching`. `result.refetch()` reconnects: TanStack aborts\n * the old signal (ending the prior iterable, which resets its store) and re-runs both the initial RPC\n * fetch and the subscription. Fire and forget — for a never-ending stream the returned promise never\n * resolves, so don't `await refetch()`.\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every value is written to the cache live, and the\n * prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Slot dedupe spans the TanStack cache, not just one store. The underlying primitive tracks a slot\n * high-water mark per store, but that mark dies when the store is disposed while the cache entry\n * survives. `refetch()` mints a fresh store (high-water reset to -1) while `refetchMode: 'append'`\n * keeps the prior envelope cached, so this hook bridges that gap: the reconnect's fresh store cannot\n * regress the cached envelope to an older slot — e.g. a lagging RPC node resolving the new initial\n * fetch behind the cached value is refused, and the warmer cached value stands until something newer\n * arrives.\n *\n * @typeParam TInitialValue - The value inside the initial RPC `SolanaRpcResponse` envelope.\n * @typeParam TStreamValue - The value inside subscription `SolanaRpcResponse` notifications.\n * @typeParam TItem - The unified item type produced by the two mappers, surfaced as `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied; pass a\n * `select: (data: SolanaRpcResponse<TItem>) => TData` option to project each value into a different\n * shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function AccountBalance({ address }: { address: Address }) {\n * const client = useClient<ClientWithRpc<GetBalanceApi> & ClientWithRpcSubscriptions<AccountNotificationsApi>>();\n * const spec = useMemo(() => ({\n * initialValueSource: client.rpc.getBalance(address),\n * initialValueMapper: (lamports: bigint) => lamports,\n * streamSource: client.rpcSubscriptions.accountNotifications(address),\n * streamValueMapper: ({ lamports }: { lamports: bigint }) => lamports,\n * }), [client, address]);\n * const { data, error } = useTrackedDataQuery(['balance', address], spec);\n * if (error) return <p>Failed to load.</p>;\n * return <p>{data ? `${data.value} lamports at slot ${data.context.slot}` : 'Loading…'}</p>;\n * }\n * ```\n *\n * @see {@link UseTrackedDataQueryOptions}\n * @see {@link useSubscriptionQuery}\n */\nexport function useTrackedDataQuery<\n TInitialValue,\n TStreamValue,\n TItem,\n TError = unknown,\n TData = SolanaRpcResponse<TItem>,\n>(\n key: QueryKey,\n spec: TrackedDataSpec<TInitialValue, TStreamValue, TItem> | null,\n options?: UseTrackedDataQueryOptions<TItem, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the spec and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const specRef = useLatest(spec);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n // The cache outlives the per-attempt store, but the store's slot high-water mark does not — a\n // fresh store (remount, refetch) starts its window at -1. The streamFn reads the currently\n // cached envelope through this client to refuse any value older than what the cache already\n // holds. `useQueryClient` returns a stable reference, so listing it in the deps below is churn-free.\n const queryClient = useQueryClient();\n\n const queryFn = useCallback<QueryFunction<SolanaRpcResponse<TItem>, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<Envelope, Envelope>` plus a replace-latest reducer types the cache value\n // as the latest scalar envelope rather than an array. `initialValue` only seeds the\n // (ignored) reducer accumulator before the first chunk, so its value is irrelevant — only\n // its type matters.\n experimental_streamedQuery<SolanaRpcResponse<TItem>, SolanaRpcResponse<TItem>>({\n initialValue: undefined as unknown as SolanaRpcResponse<TItem>,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ queryKey, signal }: QueryFunctionContext) => {\n const current = specRef.current;\n if (current == null) {\n // Defensive: a null spec forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useTrackedDataQuery: the streamFn ran with a null spec');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n const store = createReactiveStoreWithInitialValueAndSlotTracking(current);\n // Refuse any envelope older than the slot the cache already holds. A fresh store\n // (e.g. after a remount) tracks slots from scratch, so without this it could\n // overwrite the cached value with an older one from a lagging RPC node.\n return bridgeStoreToAsyncIterable(store, combinedSignal, value => {\n const cachedSlot =\n queryClient.getQueryData<SolanaRpcResponse<TItem>>(queryKey)?.context.slot ?? -1n;\n return value.context.slot > cachedSlot;\n });\n },\n })(context),\n [getAbortSignalRef, queryClient, specRef],\n );\n\n return useQuery<SolanaRpcResponse<TItem>, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null spec disables the query (TanStack's `enabled: false`). Combine with the caller's own\n // `enabled` so the query runs only when the spec is present *and* not disabled.\n enabled: spec != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/useIsomorphicLayoutEffect.ts","../src/useLatest.ts","../src/query/useRequestQuery.ts","../src/query/useSubscriptionQuery.ts","../src/query/useTrackedDataQuery.ts"],"names":["useLayoutEffect","useEffect","useRef","useCallback","useQuery","experimental_streamedQuery","bridgeStoreToAsyncIterable","useQueryClient","createReactiveStoreWithInitialValueAndSlotTracking"],"mappings":";;;;;;;AAUO,IAAM,yBAAA,GAA4B,OAAO,MAAA,KAAW,WAAA,GAAcA,qBAAA,GAAkBC,eAAA;;;ACOpF,SAAS,UAAa,KAAA,EAA+B;AACxD,EAAA,MAAM,GAAA,GAAMC,aAAO,KAAK,CAAA;AACxB,EAAA,yBAAA,CAA0B,MAAM;AAC5B,IAAA,GAAA,CAAI,OAAA,GAAU,KAAA;AAAA,EAClB,CAAC,CAAA;AACD,EAAA,OAAO,GAAA;AACX;;;AC4DO,SAAS,eAAA,CACZ,GAAA,EACA,MAAA,EACA,OAAA,EAC6B;AAE7B,EAAA,MAAM,EAAE,cAAA,EAAgB,GAAG,YAAA,EAAa,GAAI,WAAW,EAAC;AAKxD,EAAA,MAAM,SAAA,GAAY,UAAU,MAAM,CAAA;AAClC,EAAA,MAAM,iBAAA,GAAoB,UAAU,cAAc,CAAA;AAElD,EAAA,MAAM,OAAA,GAAUC,iBAAA;AAAA,IACZ,OAAO,EAAE,MAAA,EAAO,KAAwC;AACpD,MAAA,MAAM,UAAU,SAAA,CAAU,OAAA;AAC1B,MAAA,IAAI,WAAW,IAAA,EAAM;AAGjB,QAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,MACzE;AACA,MAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,IAAU;AAG/C,MAAA,MAAM,cAAA,GAAiB,aAAa,WAAA,CAAY,GAAA,CAAI,CAAC,MAAA,EAAQ,UAAU,CAAC,CAAA,GAAI,MAAA;AAC5E,MAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AAC/B,QAAA,OAAO,MAAM,QAAQ,cAAc,CAAA;AAAA,MACvC;AACA,MAAA,OAAO,MAAM,OAAA,CAAQ,aAAA,GAAgB,UAAA,CAAW,cAAc,EAAE,aAAA,EAAc;AAAA,IAIlF,CAAA;AAAA,IACA,CAAC,mBAAmB,SAAS;AAAA,GACjC;AAEA,EAAA,OAAOC,mBAAA,CAAqC;AAAA,IACxC,GAAG,YAAA;AAAA;AAAA;AAAA;AAAA,IAIH,OAAA,EAAS,MAAA,IAAU,IAAA,KAAS,YAAA,CAAa,OAAA,IAAW,IAAA,CAAA;AAAA,IACpD,OAAA;AAAA,IACA,QAAA,EAAU;AAAA,GACb,CAAA;AACL;ACrCO,SAAS,oBAAA,CACZ,GAAA,EACA,MAAA,EACA,OAAA,EAC6B;AAE7B,EAAA,MAAM,EAAE,cAAA,EAAgB,GAAG,YAAA,EAAa,GAAI,WAAW,EAAC;AAKxD,EAAA,MAAM,SAAA,GAAY,UAAU,MAAM,CAAA;AAClC,EAAA,MAAM,iBAAA,GAAoB,UAAU,cAAc,CAAA;AAElD,EAAA,MAAM,OAAA,GAAUD,iBAAAA;AAAA,IACZ,CAAA,OAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKIE,qCAAA,CAAiC;AAAA,QAC7B,YAAA,EAAc,MAAA;AAAA,QACd,OAAA,EAAS,CAAC,SAAA,EAAW,KAAA,KAAU,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAK/B,WAAA,EAAa,QAAA;AAAA,QACb,QAAA,EAAU,CAAC,EAAE,MAAA,EAAO,KAA4B;AAC5C,UAAA,MAAM,UAAU,SAAA,CAAU,OAAA;AAC1B,UAAA,IAAI,WAAW,IAAA,EAAM;AAGjB,YAAA,MAAM,IAAI,MAAM,2DAA2D,CAAA;AAAA,UAC/E;AACA,UAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,IAAU;AAG/C,UAAA,MAAM,cAAA,GAAiB,aAAa,WAAA,CAAY,GAAA,CAAI,CAAC,MAAA,EAAQ,UAAU,CAAC,CAAA,GAAI,MAAA;AAC5E,UAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AAC/B,YAAA,OAAO,QAAQ,cAAc,CAAA;AAAA,UACjC;AACA,UAAA,MAAM,KAAA,GAAQ,QAAQ,aAAA,EAAc;AAEpC,UAAA,KAAA,CAAM,UAAA,CAAW,cAAc,CAAA,CAAE,OAAA,EAAQ;AACzC,UAAA,OAAOC,8BAAA,CAA2B,OAAO,cAAc,CAAA;AAAA,QAC3D;AAAA,OACH,EAAE,OAAO;AAAA,KAAA;AAAA,IACd,CAAC,mBAAmB,SAAS;AAAA,GACjC;AAEA,EAAA,OAAOF,mBAAAA,CAAqC;AAAA;AAAA,IAExC,oBAAA,EAAsB,KAAA;AAAA,IACtB,KAAA,EAAO,KAAA;AAAA,IACP,SAAA,EAAW,QAAA;AAAA,IACX,GAAG,YAAA;AAAA;AAAA;AAAA,IAGH,OAAA,EAAS,MAAA,IAAU,IAAA,KAAS,YAAA,CAAa,OAAA,IAAW,IAAA,CAAA;AAAA,IACpD,OAAA;AAAA,IACA,QAAA,EAAU;AAAA,GACb,CAAA;AACL;ACnDO,SAAS,mBAAA,CAOZ,GAAA,EACA,IAAA,EACA,OAAA,EAC6B;AAE7B,EAAA,MAAM,EAAE,cAAA,EAAgB,GAAG,YAAA,EAAa,GAAI,WAAW,EAAC;AAKxD,EAAA,MAAM,OAAA,GAAU,UAAU,IAAI,CAAA;AAC9B,EAAA,MAAM,iBAAA,GAAoB,UAAU,cAAc,CAAA;AAMlD,EAAA,MAAM,cAAcG,yBAAA,EAAe;AAEnC,EAAA,MAAM,OAAA,GAAUJ,iBAAAA;AAAA,IACZ,CAAA,OAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMIE,qCAAAA,CAA+E;AAAA,QAC3E,YAAA,EAAc,MAAA;AAAA,QACd,OAAA,EAAS,CAAC,SAAA,EAAW,KAAA,KAAU,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAK/B,WAAA,EAAa,QAAA;AAAA,QACb,QAAA,EAAU,CAAC,EAAE,QAAA,EAAU,QAAO,KAA4B;AACtD,UAAA,MAAM,UAAU,OAAA,CAAQ,OAAA;AACxB,UAAA,IAAI,WAAW,IAAA,EAAM;AAGjB,YAAA,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAAA,UAC5E;AACA,UAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,IAAU;AAG/C,UAAA,MAAM,cAAA,GAAiB,aAAa,WAAA,CAAY,GAAA,CAAI,CAAC,MAAA,EAAQ,UAAU,CAAC,CAAA,GAAI,MAAA;AAC5E,UAAA,MAAM,KAAA,GAAQG,uDAAmD,OAAO,CAAA;AAExE,UAAA,KAAA,CAAM,UAAA,CAAW,cAAc,CAAA,CAAE,OAAA,EAAQ;AAIzC,UAAA,OAAOF,8BAAAA,CAA2B,KAAA,EAAO,cAAA,EAAgB,CAAA,KAAA,KAAS;AAC9D,YAAA,MAAM,aACF,WAAA,CAAY,YAAA,CAAuC,QAAQ,CAAA,EAAG,OAAA,CAAQ,QAAQ,CAAC,EAAA;AACnF,YAAA,OAAO,KAAA,CAAM,QAAQ,IAAA,GAAO,UAAA;AAAA,UAChC,CAAC,CAAA;AAAA,QACL;AAAA,OACH,EAAE,OAAO;AAAA,KAAA;AAAA,IACd,CAAC,iBAAA,EAAmB,WAAA,EAAa,OAAO;AAAA,GAC5C;AAEA,EAAA,OAAOF,mBAAAA,CAA4D;AAAA;AAAA,IAE/D,oBAAA,EAAsB,KAAA;AAAA,IACtB,KAAA,EAAO,KAAA;AAAA,IACP,SAAA,EAAW,QAAA;AAAA,IACX,GAAG,YAAA;AAAA;AAAA;AAAA,IAGH,OAAA,EAAS,IAAA,IAAQ,IAAA,KAAS,YAAA,CAAa,OAAA,IAAW,IAAA,CAAA;AAAA,IAClD,OAAA;AAAA,IACA,QAAA,EAAU;AAAA,GACb,CAAA;AACL","file":"query.node.cjs","sourcesContent":["import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * `useLayoutEffect` warns when run on the server because layout effects only make sense after a\n * DOM is mounted. For our use, plain `useEffect` is functionally equivalent on the server (no\n * event handlers can fire mid-render anyway), so we pick at module load time and stay silent\n * during SSR.\n *\n * @internal\n */\nexport const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n","import { type MutableRefObject, useRef } from 'react';\n\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';\n\n/**\n * Hold `value` in a ref that is re-synced to the latest render's value after every commit.\n *\n * Lets a callback (or a store created once) read the freshest closure value at call time without\n * taking it as a dependency, so the consumer's own identity stays stable across renders. The sync\n * runs in a layout effect so the ref is current before passive effects and event handlers fire.\n *\n * @typeParam T - The type of the value being tracked.\n *\n * @returns A stable {@link MutableRefObject} whose `current` always reflects the most recent render.\n *\n * @internal\n */\nexport function useLatest<T>(value: T): MutableRefObject<T> {\n const ref = useRef(value);\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import type { ReactiveActionSource } from '@solana/kit';\nimport {\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\n\n/**\n * Options accepted by {@link useRequestQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseRequestQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useRequest`. Accepts the same source shape — a\n * {@link ReactiveActionSource} (satisfied by `PendingRpcRequest<T>`) or a\n * `(signal: AbortSignal) => Promise<T>` function — and routes it through TanStack Query's cache, so\n * components reading the same `key` dedupe into one in-flight request and the result becomes\n * Suspense-capable and visible in TanStack Query's devtools.\n *\n * The query's `queryFn` is handed TanStack Query's own `AbortSignal`, which aborts when the query\n * is cancelled (unmount, garbage collection, or a superseding refetch). That signal is threaded\n * into the source so cancellation propagates all the way down. When a `getAbortSignal` factory is\n * also supplied (e.g. for a per-attempt timeout), the two signals are combined via\n * {@link AbortSignal.any} — aborting either one cancels the attempt.\n *\n * Pass `null` for `source` to disable — useful when one of the source's inputs isn't yet known.\n * This maps to TanStack's `enabled: false` and combines with any `enabled` you pass yourself (the\n * query runs only when the source is non-null *and* your `enabled` is not `false`). Use\n * `result.refetch()` to refresh.\n *\n * @typeParam T - The value the underlying request resolves to.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project the cached value into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function LatestBlockhash() {\n * const client = useClient<ClientWithRpc<GetLatestBlockhashApi>>();\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['latestBlockhash'],\n * client.rpc.getLatestBlockhash(),\n * { getAbortSignal: () => AbortSignal.timeout(5_000) },\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>Blockhash: {data!.value.blockhash}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async call. The source can be an inline closure recreated\n * // each render; the hook reads the latest one when the query fires and keys the cache off `key`,\n * // so there's no need to memoize it.\n * function Profile({ userId }: { userId: string }) {\n * const { data, error, isLoading, refetch } = useRequestQuery(\n * ['users', userId],\n * (signal: AbortSignal) => fetch(`/api/users/${userId}`, { signal }).then(r => r.json()),\n * );\n * if (error) return <button onClick={() => refetch()}>Retry</button>;\n * if (isLoading) return <p>Loading…</p>;\n * return <p>{data!.name}</p>;\n * }\n * ```\n *\n * @see {@link UseRequestQueryOptions}\n */\nexport function useRequestQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveActionSource<T> | ((signal: AbortSignal) => Promise<T>) | null,\n options?: UseRequestQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline closure passed each render\n // doesn't change the queryFn's identity. The queryFn reads the latest values from the refs when\n // it fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback(\n async ({ signal }: QueryFunctionContext): Promise<T> => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't schedule\n // this queryFn. This branch should be unreachable.\n throw new Error('useRequestQuery: the queryFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's signal\n // (when present) so aborting either cancels the attempt.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return await current(combinedSignal);\n }\n return await current.reactiveStore().withSignal(combinedSignal).dispatchAsync();\n // `sourceRef` and `getAbortSignalRef` are stable refs from `useLatest`, so listing them\n // leaves `queryFn` referentially stable across renders — TanStack keys off `key`, not\n // the queryFn identity, but a stable queryFn avoids needless churn.\n },\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* the caller hasn't\n // disabled it.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import { bridgeStoreToAsyncIterable, type ReactiveStreamSource } from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\n\n/**\n * Options accepted by {@link useSubscriptionQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied.\n */\nexport type UseSubscriptionQueryOptions<T, TError = unknown, TData = T> = Omit<\n UseQueryOptions<T, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useSubscription`, for streams that have no one-shot\n * RPC fetch counterpart. Routes a long-lived stream through TanStack Query's cache via\n * `experimental_streamedQuery`, so components reading the same `key` share one underlying connection\n * and the stream participates in TanStack Query's cache and devtools.\n *\n * The source has the same two shapes as {@link useRequestQuery}, the pull-based analog of its\n * `(signal) => Promise<T>` arm: a {@link ReactiveStreamSource} (satisfied by an RPC subscription) or\n * a raw `(signal: AbortSignal) => AsyncIterable<T>` factory. `streamedQuery` is itself built on\n * `AsyncIterable`, so the factory's iterable is consumed directly; a `ReactiveStreamSource` is\n * adapted to one internally. The factory receives TanStack's query-cancellation signal (combined\n * with any `getAbortSignal` you supply) so a `fetch`- or socket-backed generator can bind\n * cancellation to it.\n *\n * `data` is the raw notification, exactly as the source emits it. For RPC subscriptions read\n * `data.value` and `data.context.slot` yourself.\n *\n * Because the stream never settles, the query stays in `fetchStatus: 'fetching'` for the\n * subscription's whole life — `isFetching` is permanently `true` and `isLoading` flips false\n * after the first notification. Read `data` / `error` / `status` and ignore `isFetching` for\n * subscriptions. `result.refetch()` reconnects: TanStack aborts the old signal (tearing the prior\n * stream's connection down) and opens a fresh stream. Fire and forget — for a never-ending stream\n * the returned promise never resolves, so don't `await refetch()` (a click handler that does will\n * hang forever).\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every notification is written to the cache live,\n * and the prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Pass `null` for `source` to disable — maps to TanStack's `enabled: false` and combines with any\n * `enabled` you pass (the query runs only when the source is non-null *and* your `enabled` is not\n * `false`).\n *\n * @typeParam T - The notification type emitted by the source.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to `T` when no\n * `select` is supplied; pass a `select: (data: T) => TData` option to project each notification into\n * a different shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function SlotHeight() {\n * const client = useClient<ClientWithRpcSubscriptions<SlotNotificationsApi>>();\n * const { data, error } = useSubscriptionQuery(['slot'], client.rpcSubscriptions.slotNotifications());\n * if (error) return <p>Disconnected.</p>;\n * if (!data) return <p>Connecting…</p>;\n * return <p>Slot {String(data.slot)}</p>;\n * }\n *\n * // Function shape — wraps an arbitrary async iterable, threading the cancellation signal in.\n * function Ticker() {\n * const { data } = useSubscriptionQuery(['ticker'], (signal: AbortSignal) => streamPrices(signal));\n * return <p>{data ?? '…'}</p>;\n * }\n * ```\n *\n * @see {@link UseSubscriptionQueryOptions}\n * @see {@link useRequestQuery}\n */\nexport function useSubscriptionQuery<T, TError = unknown, TData = T>(\n key: QueryKey,\n source: ReactiveStreamSource<T> | ((signal: AbortSignal) => AsyncIterable<T>) | null,\n options?: UseSubscriptionQueryOptions<T, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the source and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const sourceRef = useLatest(source);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n const queryFn = useCallback<QueryFunction<T, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<T, T>` plus a replace-latest reducer types the cache value as the latest\n // scalar notification rather than `T[]`. `initialValue` only seeds the (ignored) reducer\n // accumulator before the first chunk, so its value is irrelevant — only its type matters.\n experimental_streamedQuery<T, T>({\n initialValue: undefined as T,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ signal }: QueryFunctionContext) => {\n const current = sourceRef.current;\n if (current == null) {\n // Defensive: a null source forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useSubscriptionQuery: the streamFn ran with a null source');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n if (typeof current === 'function') {\n return current(combinedSignal);\n }\n const store = current.reactiveStore();\n // reset not needed, the connection tears down and the store gets GCed after abort\n store.withSignal(combinedSignal).connect();\n return bridgeStoreToAsyncIterable(store, combinedSignal);\n },\n })(context),\n [getAbortSignalRef, sourceRef],\n );\n\n return useQuery<T, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null source disables the query (TanStack's `enabled: false`). Combine with the caller's\n // own `enabled` so the query runs only when the source is present *and* not disabled.\n enabled: source != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n","import {\n bridgeStoreToAsyncIterable,\n createReactiveStoreWithInitialValueAndSlotTracking,\n type SolanaRpcResponse,\n} from '@solana/kit';\nimport {\n experimental_streamedQuery,\n type QueryFunction,\n type QueryFunctionContext,\n type QueryKey,\n useQuery,\n useQueryClient,\n type UseQueryOptions,\n type UseQueryResult,\n} from '@tanstack/react-query';\nimport { useCallback } from 'react';\n\nimport { useLatest } from '../useLatest';\nimport type { UseRequestOptions } from '../useRequest';\nimport type { TrackedDataSpec } from '../useTrackedData';\n\n/**\n * Options accepted by {@link useTrackedDataQuery}: every option `@tanstack/react-query`'s `useQuery`\n * takes (minus `queryKey` and `queryFn`, which this hook owns) plus the Kit-specific\n * {@link UseRequestOptions}.\n *\n * @typeParam TItem - The unified item type produced by the spec's two mappers, surfaced as\n * `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied.\n */\nexport type UseTrackedDataQueryOptions<TItem, TError = unknown, TData = SolanaRpcResponse<TItem>> = Omit<\n UseQueryOptions<SolanaRpcResponse<TItem>, TError, TData, QueryKey>,\n 'queryFn' | 'queryKey'\n> &\n UseRequestOptions;\n\n/**\n * TanStack Query-backed counterpart to core's `useTrackedData`. Pairs a one-shot RPC fetch with an\n * ongoing subscription (slot-deduped by the underlying Kit primitive) and routes the unified stream\n * through TanStack Query's cache via `experimental_streamedQuery`, so components reading the same\n * `key` share one underlying connection and the stream participates in TanStack Query's cache and\n * devtools.\n *\n * `data` is the `SolanaRpcResponse<TItem>` envelope emitted by the underlying kit primitive — the\n * primitive's type guarantees the envelope shape, so callers can read `data.value` (the unified item\n * produced by the spec's mappers) and `data.context.slot` (the slot the store dedup'd on) directly.\n *\n * Pass a {@link TrackedDataSpec} keyed on whatever inputs it depends on; the hook reads the latest\n * one from a ref, so an inline spec recreated each render is fine (TanStack keys the cache off `key`,\n * not the spec identity — bump the `key` to swap specs). Pass `null` for `spec` to disable — maps to\n * TanStack's `enabled: false` and combines with any `enabled` you pass (the query runs only when the\n * spec is non-null *and* your `enabled` is not `false`).\n *\n * Because the subscription never settles, the query stays in `fetchStatus: 'fetching'` for its whole\n * life — `isFetching` is permanently `true` and `isLoading` flips false after the first value. Read\n * `data` / `error` / `status` and ignore `isFetching`. `result.refetch()` reconnects: TanStack aborts\n * the old signal (tearing the prior stream's connection down) and re-runs both the initial RPC fetch\n * and the subscription. Fire and forget — for a never-ending stream the returned promise never\n * resolves, so don't `await refetch()`.\n *\n * Defaults, all overridable: `retry: false` (the underlying reactive store owns retry/backoff),\n * `staleTime: Infinity` and `refetchOnWindowFocus: false` (a focus revalidation must not tear down\n * and re-open the socket — the subscription is what keeps data fresh). The internal `refetchMode` is\n * `'append'` paired with a replace-latest reducer: every value is written to the cache live, and the\n * prior value stays visible across a reconnect (stale-while-revalidate).\n *\n * Slot dedupe spans the TanStack cache, not just one store. The underlying primitive tracks a slot\n * high-water mark per store, but that mark dies when the store is disposed while the cache entry\n * survives. `refetch()` mints a fresh store (high-water reset to -1) while `refetchMode: 'append'`\n * keeps the prior envelope cached, so this hook bridges that gap: the reconnect's fresh store cannot\n * regress the cached envelope to an older slot — e.g. a lagging RPC node resolving the new initial\n * fetch behind the cached value is refused, and the warmer cached value stands until something newer\n * arrives.\n *\n * @typeParam TInitialValue - The value inside the initial RPC `SolanaRpcResponse` envelope.\n * @typeParam TStreamValue - The value inside subscription `SolanaRpcResponse` notifications.\n * @typeParam TItem - The unified item type produced by the two mappers, surfaced as `data.value`.\n * @typeParam TError - The error type TanStack Query will surface on failure.\n * @typeParam TData - The shape of `data` after any `select` transform. Defaults to the\n * `SolanaRpcResponse<TItem>` envelope when no `select` is supplied; pass a\n * `select: (data: SolanaRpcResponse<TItem>) => TData` option to project each value into a different\n * shape and `result.data` narrows accordingly.\n *\n * @example\n * ```tsx\n * function AccountBalance({ address }: { address: Address }) {\n * const client = useClient<ClientWithRpc<GetBalanceApi> & ClientWithRpcSubscriptions<AccountNotificationsApi>>();\n * const spec = useMemo(() => ({\n * initialValueSource: client.rpc.getBalance(address),\n * initialValueMapper: (lamports: bigint) => lamports,\n * streamSource: client.rpcSubscriptions.accountNotifications(address),\n * streamValueMapper: ({ lamports }: { lamports: bigint }) => lamports,\n * }), [client, address]);\n * const { data, error } = useTrackedDataQuery(['balance', address], spec);\n * if (error) return <p>Failed to load.</p>;\n * return <p>{data ? `${data.value} lamports at slot ${data.context.slot}` : 'Loading…'}</p>;\n * }\n * ```\n *\n * @see {@link UseTrackedDataQueryOptions}\n * @see {@link useSubscriptionQuery}\n */\nexport function useTrackedDataQuery<\n TInitialValue,\n TStreamValue,\n TItem,\n TError = unknown,\n TData = SolanaRpcResponse<TItem>,\n>(\n key: QueryKey,\n spec: TrackedDataSpec<TInitialValue, TStreamValue, TItem> | null,\n options?: UseTrackedDataQueryOptions<TItem, TError, TData>,\n): UseQueryResult<TData, TError> {\n // Split our option off the TanStack config so we can hand the rest to `useQuery` cleanly.\n const { getAbortSignal, ...queryOptions } = options ?? {};\n\n // Ref-sync the spec and the abort-signal factory so an inline value passed each render doesn't\n // change the streamFn's identity. The streamFn reads the latest values from the refs when it\n // fires; TanStack uses the key (not the queryFn identity) for cache lookup.\n const specRef = useLatest(spec);\n const getAbortSignalRef = useLatest(getAbortSignal);\n\n // The cache outlives the per-attempt store, but the store's slot high-water mark does not — a\n // fresh store (remount, refetch) starts its window at -1. The streamFn reads the currently\n // cached envelope through this client to refuse any value older than what the cache already\n // holds. `useQueryClient` returns a stable reference, so listing it in the deps below is churn-free.\n const queryClient = useQueryClient();\n\n const queryFn = useCallback<QueryFunction<SolanaRpcResponse<TItem>, QueryKey>>(\n context =>\n // `streamedQuery`'s generics default to array accumulation (`TData = Array<TQueryFnData>`).\n // The explicit `<Envelope, Envelope>` plus a replace-latest reducer types the cache value\n // as the latest scalar envelope rather than an array. `initialValue` only seeds the\n // (ignored) reducer accumulator before the first chunk, so its value is irrelevant — only\n // its type matters.\n experimental_streamedQuery<SolanaRpcResponse<TItem>, SolanaRpcResponse<TItem>>({\n initialValue: undefined as unknown as SolanaRpcResponse<TItem>,\n reducer: (_previous, chunk) => chunk,\n // `'append'` is the only mode that writes every chunk to the cache live while leaving\n // the prior value in place across a refetch. `'reset'` would flash to pending on each\n // reconnect; `'replace'` only flushes once at stream end — which for a never-ending\n // subscription never happens (an abort skips the final write), freezing the value.\n refetchMode: 'append',\n streamFn: ({ queryKey, signal }: QueryFunctionContext) => {\n const current = specRef.current;\n if (current == null) {\n // Defensive: a null spec forces `enabled: false` below, so TanStack won't\n // schedule this queryFn. This branch should be unreachable.\n throw new Error('useTrackedDataQuery: the streamFn ran with a null spec');\n }\n const userSignal = getAbortSignalRef.current?.();\n // `signal` is TanStack's query-cancellation signal. Combine it with the caller's\n // signal (when present) so aborting either tears the stream down.\n const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;\n const store = createReactiveStoreWithInitialValueAndSlotTracking(current);\n // reset not needed, the connection tears down and the store gets GCed after abort\n store.withSignal(combinedSignal).connect();\n // Refuse any envelope older than the slot the cache already holds. A fresh store\n // (e.g. after a remount) tracks slots from scratch, so without this it could\n // overwrite the cached value with an older one from a lagging RPC node.\n return bridgeStoreToAsyncIterable(store, combinedSignal, value => {\n const cachedSlot =\n queryClient.getQueryData<SolanaRpcResponse<TItem>>(queryKey)?.context.slot ?? -1n;\n return value.context.slot > cachedSlot;\n });\n },\n })(context),\n [getAbortSignalRef, queryClient, specRef],\n );\n\n return useQuery<SolanaRpcResponse<TItem>, TError, TData, QueryKey>({\n // Sensible defaults for a long-lived stream; all overridable by `queryOptions` below.\n refetchOnWindowFocus: false,\n retry: false,\n staleTime: Infinity,\n ...queryOptions,\n // A null spec disables the query (TanStack's `enabled: false`). Combine with the caller's own\n // `enabled` so the query runs only when the spec is present *and* not disabled.\n enabled: spec != null && (queryOptions.enabled ?? true),\n queryFn,\n queryKey: key,\n });\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { useLatest } from './chunk-CZI6SUQB.node.mjs';
2
2
  import { useQuery, experimental_streamedQuery, useQueryClient } from '@tanstack/react-query';
3
3
  import { useCallback } from 'react';
4
- import { createReactiveStoreWithInitialValueAndSlotTracking, SolanaError, SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR } from '@solana/kit';
4
+ import { bridgeStoreToAsyncIterable, createReactiveStoreWithInitialValueAndSlotTracking } from '@solana/kit';
5
5
 
6
6
  function useRequestQuery(key, source, options) {
7
7
  const { getAbortSignal, ...queryOptions } = options ?? {};
@@ -32,56 +32,6 @@ function useRequestQuery(key, source, options) {
32
32
  queryKey: key
33
33
  });
34
34
  }
35
- function bridgeStoreToAsyncIterable(store, signal, shouldYield) {
36
- return {
37
- async *[Symbol.asyncIterator]() {
38
- let latest;
39
- let failure;
40
- let deferred = Promise.withResolvers();
41
- const wake = () => {
42
- const { resolve } = deferred;
43
- deferred = Promise.withResolvers();
44
- resolve();
45
- };
46
- const onChange = () => {
47
- const state = store.getState();
48
- if (state.status === "loaded") {
49
- if (shouldYield && !shouldYield(state.data)) return;
50
- latest = { value: state.data };
51
- wake();
52
- } else if (state.status === "error") {
53
- failure = {
54
- error: state.error ?? new SolanaError(SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR)
55
- };
56
- wake();
57
- }
58
- };
59
- const onAbort = () => wake();
60
- signal.addEventListener("abort", onAbort, { once: true });
61
- const unsubscribe = store.subscribe(onChange);
62
- store.withSignal(signal).connect();
63
- try {
64
- while (true) {
65
- if (signal.aborted) return;
66
- if (failure) throw failure.error;
67
- if (latest) {
68
- const { value } = latest;
69
- latest = void 0;
70
- yield value;
71
- continue;
72
- }
73
- await deferred.promise;
74
- }
75
- } finally {
76
- signal.removeEventListener("abort", onAbort);
77
- unsubscribe();
78
- store.reset();
79
- }
80
- }
81
- };
82
- }
83
-
84
- // src/query/useSubscriptionQuery.ts
85
35
  function useSubscriptionQuery(key, source, options) {
86
36
  const { getAbortSignal, ...queryOptions } = options ?? {};
87
37
  const sourceRef = useLatest(source);
@@ -110,7 +60,9 @@ function useSubscriptionQuery(key, source, options) {
110
60
  if (typeof current === "function") {
111
61
  return current(combinedSignal);
112
62
  }
113
- return bridgeStoreToAsyncIterable(current.reactiveStore(), combinedSignal);
63
+ const store = current.reactiveStore();
64
+ store.withSignal(combinedSignal).connect();
65
+ return bridgeStoreToAsyncIterable(store, combinedSignal);
114
66
  }
115
67
  })(context)
116
68
  ),
@@ -157,6 +109,7 @@ function useTrackedDataQuery(key, spec, options) {
157
109
  const userSignal = getAbortSignalRef.current?.();
158
110
  const combinedSignal = userSignal ? AbortSignal.any([signal, userSignal]) : signal;
159
111
  const store = createReactiveStoreWithInitialValueAndSlotTracking(current);
112
+ store.withSignal(combinedSignal).connect();
160
113
  return bridgeStoreToAsyncIterable(store, combinedSignal, (value) => {
161
114
  const cachedSlot = queryClient.getQueryData(queryKey)?.context.slot ?? -1n;
162
115
  return value.context.slot > cachedSlot;