Add useSubscriptionSwr to the @solana/react/swr adapter#1710
Closed
mcintyre94 wants to merge 16 commits into
Closed
Add useSubscriptionSwr to the @solana/react/swr adapter#1710mcintyre94 wants to merge 16 commits into
useSubscriptionSwr to the @solana/react/swr adapter#1710mcintyre94 wants to merge 16 commits into
Conversation
Adds the Kit client context layer for `@solana/react`: a single provider that publishes a caller-owned Kit client to its subtree, plus two hooks — `useClient` (basic accessor with optional generic narrowing) and `useClientCapability` (runtime-checked accessor with a structured missing-capability error). Required by these hooks and by any plugin-specific hook that depends on a client capability; generic primitives like `useAction` (forthcoming) work against arbitrary async functions and don't need a provider. The provider accepts both synchronous clients and promise-returning ones — when given a promise (e.g. `createClient().use(asyncPlugin())`) it suspends via the nearest `<Suspense>` boundary until the client resolves. On React 19 it delegates to `React.use(promise)`; on React 18 an internal thrown-promise shim, keyed by promise identity, honours the same contract. Two new `SolanaError` codes are reserved in the `[9000000-9000999]` range: `SOLANA_ERROR__REACT__MISSING_PROVIDER` (thrown by `useClient` outside a provider) and `SOLANA_ERROR__REACT__MISSING_CAPABILITY` (thrown by `useClientCapability` with `hookName` + `providerHint` context, listing every missing capability when an array is passed). Additive; no impact on the existing wallet-account hooks. Verified with browser + node unit tests and TS-only typetests across both `@solana/react` and `@solana/errors`.
Bridges an arbitrary async function into a reactive `ActionResult<TArgs, TResult>` with `send` / `status` / `data` / `error` / `reset`. Each `send(...)` runs the function with a fresh `AbortSignal` and tracks its lifecycle through React state; a second `send` while a first is in flight aborts the first. `fn` is held in a ref that always points at the latest render's closure — there is no `deps` array to maintain. Each `send(...)` invokes the most recently rendered `fn`, so values captured inside (form state, route params, etc.) are always fresh. This matches the convention used by `useMutation` in TanStack Query and `useWriteContract` in wagmi. In-flight calls are unaffected — they continue with the closure they captured at dispatch time. Built on `createReactiveActionStore` from `@solana/subscribable`. Awaiters of a superseded call see a rejection with an `AbortError` filterable via `isAbortError` from `@solana/promises`.
`reactiveStore()` no longer dispatches the request automatically when called. It now returns a `ReactiveActionStore` in the `idle` state, leaving the caller responsible for the initial `dispatch()`. The auto-fire was created to make `reactiveStore()` parallel to `send()` — the consumer got a "live" store back the same way they get a live promise. In practice the auto-fire created an asymmetry at the start of the store's lifecycle: the initial request had no caller-visible dispatch site, so attaching an `AbortSignal` to that one specific attempt required a separate construction-time option distinct from the mechanism used for all subsequent attempts. Without the auto-fire, every dispatch is the caller's, and signal attachment lives uniformly at the dispatch site. This also brings `reactiveStore()` in line with `createReactiveActionStore(fn)` (which doesn't auto-fire either), so the two creation paths now have identical "construct then dispatch" semantics. React hooks consuming `reactiveStore` (e.g. `useRequest`) handle the dispatch internally — consumers of those hooks see no change. Major version bump for `@solana/rpc-spec`: callers using `reactiveStore()` directly will need to add an explicit `store.dispatch()` to keep the existing fire-on-creation behavior.
…tion
`ReactiveActionStore` now exposes `withSignal(signal)` — a thin wrapper that returns `{ dispatch, dispatchAsync }` bindings composing the caller's signal with the store's internal per-dispatch controller via `AbortSignal.any`. Aborting either cancels the in-flight call and surfaces the abort reason on state. The bare `dispatch` / `dispatchAsync` signatures are unchanged, so this is additive — no existing caller breaks.
The wrapper supports two patterns. Per-attempt timeout: `store.withSignal(AbortSignal.timeout(5_000)).dispatch(args)` — a fresh clock per call, with different call sites free to pass different timeouts without rebuilding the store. Shared kill switch: hold one `AbortController`, bind the wrapper once (`const killable = store.withSignal(killCtrl.signal)`), use `killable.dispatch(...)` everywhere; aborting the controller cancels the in-flight call and short-circuits future calls on that wrapper.
`PendingRpcRequest.reactiveStore()` (and the `ReactiveActionSource` duck-type) also accepts an optional `{ signal?: AbortSignal }` so a caller-provided cancellation source can be attached to the initial dispatch fired implicitly by `reactiveStore()` — same role `abortSignal` plays for `send()`. The `signal` is not a store-level setting; subsequent dispatches on the returned store go through `store.withSignal(...).dispatch(...)` like any other store.
The shared `@solana/test-config` browser environment polyfills `AbortSignal.any` because jsdom 22 (the version pinned here) doesn't ship it. Replacing the AbortSignal class wholesale would break jsdom's brand checks for `addEventListener({ signal })`, so the patch is limited to the missing static method.
Errors from a `ReactiveActionStore` now persist through a subsequent `running` state, matching the existing behavior for `data`. A re-dispatch after a failure keeps the previous error in `state.error` until the new attempt resolves — `success` clears it, a new failure replaces it. This mirrors how SWR and TanStack Query handle revalidation: stale-while-revalidate applies symmetrically to data and errors, so consumers don't have to choose between flickering and losing context on retry. The `running` variant of `ReactiveActionState<T>` widens from `error: undefined` to `error: unknown`, which surfaces through `useAction` as a behavior change: the `error` field now persists across a new `send(...)` call until that call resolves, instead of clearing immediately. The README, JSDoc, and tests are updated to match. `useRequest` will pick this up via a follow-up commit on the next branch in the stack.
`useRequest(source)` fires a request on mount and re-fires whenever the source identity changes. The source is either a `ReactiveActionSource<T>` (the `{ reactiveStore() }` duck-type — `PendingRpcRequest` is the canonical implementation) or an async function `(signal: AbortSignal) => Promise<T>` for wrapping arbitrary one-shot async work (a `fetch`, a third-party SDK call). Memoize the input with `useMemo` (source) or `useCallback` (function) keyed on its inputs; `react-hooks/exhaustive-deps` then catches stale closures by default. Pass `null` to disable.
The hook returns `{ data, error, refresh, status }` with `status` as one of `fetching | success | error | disabled`. `refresh()` dispatches on the same underlying action store, so its built-in stale-while-revalidate keeps `data` and `error` populated through the next `fetching` attempt — the bridge doesn't mirror state externally.
Optional `getAbortSignal: () => AbortSignal` is invoked on every attempt (initial fire + every `refresh()`). The returned signal is composed with the store's per-dispatch controller via `AbortSignal.any` through `withSignal(signal).dispatch()`. The natural use is per-attempt timeouts (`() => AbortSignal.timeout(5_000)`), which reset on refresh. The factory is held in a ref synced to the latest render — inline closures are fine. `refresh()` accepts an optional `{ abortSignal }` override for one specific attempt; presence-based semantics distinguish \"use factory\" (omit the key), \"explicit signal\" (\`{ abortSignal: signal }\`), and \"no signal\" (\`{ abortSignal: undefined }\`).
Disabling and unmount cancellation are handled the React-native way: pass \`null\` for the source (result reports \`disabled\`), or let the component unmount (effect cleanup aborts the in-flight call). The \`disabledActionStore\` returned for the null-source case has no-op \`dispatch\` and \`reset\` — pinned by a new test file so the \`useRequest\` effect can rely on the invariant without an explicit gate.
Lifts the \`useIsomorphicLayoutEffect\` helper that was duplicated in \`useAction.ts\` into a shared internal module so both hooks share a single definition.
Exports \`RequestResult<T>\` and \`UseRequestOptions\` for plugin hooks that build on this surface.
…icitly
`createReactiveStoreFromDataPublisherFactory` (and `createReactiveStoreWithInitialValueAndSlotTracking`) no longer auto-connect on construction. The returned store starts in `status: 'idle'`; the caller calls `store.connect()` to fire the underlying request/subscription, mirroring how `ReactiveActionStore` requires an explicit `dispatch()`. This unifies the lifecycle model across both primitives — stores are state machines that callers orchestrate, not self-starting subscriptions — and lets `useSubscription` adopt the same `useEffect(() => { store.connect(); return () => store.reset(); })` pattern that `useRequest` uses today.
`ReactiveStreamStore<T>` gains two new methods: `connect()` (open or re-open the stream — transitions through `loading` from `idle` or `retrying` from any other status while preserving stale data) and `reset()` (abort the current connection, clearing `data` and `error`, and return to `idle`). The state union grows an `idle` variant. `retry()` becomes a deprecated alias that delegates to `connect()` guarded by `status === 'error'` — preserving its original error-only behaviour for callers who relied on it, but new code should call `connect()` directly.
`createReactiveStoreFromDataPublisher` (the non-factory variant accepting a ready-made `DataPublisher`) is removed. Its only documented consumer was the deprecated `PendingRpcSubscriptionsRequest.reactive()` method, which is also removed in this commit. Both have been deprecated for some time; the cascade removal lands them together rather than ratcheting through two more release cycles. Code that built a store around a singleton publisher should wrap it in `createDataPublisher: () => Promise.resolve(publisher)` and use the factory variant.
`createReactiveStoreWithInitialValueAndSlotTracking` is also lazy: starts in `idle`, requires `connect()` to fire the RPC request and open the subscription. The `lastUpdateSlot` window resets on `reset()` so a fresh `connect()` starts a new slot-tracking lifecycle. Tests in `@solana/subscribable`, `@solana/rpc-subscriptions-spec`, and `@solana/kit` updated accordingly (the `reactive()` test block in `rpc-subscriptions-spec` is removed).
The error code `SOLANA_ERROR__SUBSCRIBABLE__RETRY_NOT_SUPPORTED` is left in the codes table per the never-remove-error-codes rule, but is no longer produced by any code path in this repo.
…lation
Adds `store.withSignal(signal).connect()` to `ReactiveStreamStore`, mirroring the action store's per-dispatch `withSignal()` pattern — callers attach a per-connection signal at the call site instead of baking one into the store's construction. Drops the construction-time `abortSignal` option on `createReactiveStoreFromDataPublisherFactory`, `createReactiveStoreWithInitialValueAndSlotTracking`, and `PendingRpcSubscriptionsRequest.reactiveStore()`. The duck-type `ReactiveStreamSource<T>.reactiveStore()` is now parameter-less, mirroring `ReactiveActionSource<T>.reactiveStore()`.
`store.withSignal(signal)` returns a thin wrapper exposing `connect()`. Each call composes the caller-provided signal with the per-connection inner controller via `AbortSignal.any` — aborting either tears down the active connection. Aborting the caller-provided signal surfaces the abort reason on state as `{ status: 'error' }`; supersession via the internal controller (a newer `connect()` or `reset()`) stays silent so the newer call owns state. Use cases:
- Per-connection timeout: `store.withSignal(AbortSignal.timeout(30_000)).connect()` mints a fresh clock per attempt.
- Permanent kill switch: hold one `AbortController`, bind the wrapper once (\`const killable = store.withSignal(killCtrl.signal)\`), and use \`killable.connect()\` everywhere. After \`killCtrl.abort()\`, every future call short-circuits to error. Bare \`store.connect()\` calls bypass the bound signal — same discipline contract as the action store's bind-once pattern.
\`createDataPublisher\` is widened from \`() => Promise<DataPublisher>\` to \`(signal: AbortSignal) => Promise<DataPublisher>\`. The store passes the composed per-connection signal to the factory so the underlying transport can stop on per-connection abort, not just the stream-store's listeners. Existing no-arg factories still satisfy the new shape — TypeScript allows fewer parameters than the declared type.
Internal: the per-connection inner controller plus the optional caller signal are composed with \`AbortSignal.any\`, replacing the manually-scoped abort forwarder and the prior \`outerController\` that bridged the construction-time \`abortSignal\` to inner connections. Same observable behaviour for the supersede / reset paths; new behaviour for the caller-signal path (surfaces as error rather than silently disconnecting).
Tests in \`@solana/subscribable\`, \`@solana/rpc-subscriptions-spec\`, and \`@solana/kit\` updated to exercise the new \`withSignal\` API. The "abort signal" describe blocks (which tested the deprecated construction-time signal) are replaced with focused \`withSignal()\` blocks covering the per-connection timeout, kill-switch, and supersede-doesn't-touch-caller-signal cases.
Mirrors the action store's `running` (which already merged "first call vs subsequent call"). `data` and `error` are preserved through `loading` for stale-while-revalidate UX.
`ReactiveState<T>` drops the `retrying` variant. `loading` widens from `{ data: undefined, error: undefined }` to `{ data: T | undefined, error: unknown }`. Both `createReactiveStoreFromDataPublisherFactory` and `createReactiveStoreWithInitialValueAndSlotTracking` transition every `connect()` through `loading`, preserving `currentState.data` and `currentState.error`.
Consumers that need to distinguish first-load from reconnect inspect `data === undefined` instead of relying on a separate `retrying` status. Bumps `@solana/subscribable` and `@solana/kit` major.
Tests in both packages updated: `retrying`-state assertions now assert `loading` carrying stale data + error. Downstream `useSubscription` (next commit) drops `retrying` from `SubscriptionResult.status` and passes data + error through the `loading` case.
…types` Adds `UnwrapRpcResponse<T>` (a conditional type that unwraps `SolanaRpcResponse<U> → U` and passes non-envelope types through) and `isSolanaRpcResponse()` (a runtime type guard) to `@solana/rpc-types`. The guard validates the envelope shape by duck-typing `context.slot: bigint` and the presence of `value` — only the load-bearing fields, so adding new envelope fields like `apiVersion` in the future doesn't ripple through the guard's contract. The narrowed type is `SolanaRpcResponse<UnwrapRpcResponse<T>>`, inferring the inner type from `T` directly so callers don't need a second generic parameter. Callers that previously needed to lift `slot` from `context` can branch on the guard and access whatever fields they need.
`useSubscription(source)` opens a stream-store subscription on mount, re-opens whenever the source identity changes, and tears it down on unmount. The source is any `ReactiveStreamSource<T>` — `PendingRpcSubscriptionsRequest` is the canonical implementation. Pass `null` to disable.
The hook mirrors `useRequest`'s structure exactly: construct the lazy store via `useMemo`, fire `store.connect()` in a `useEffect`, tear down via `store.reset()` in cleanup. Same StrictMode-safe lifecycle pattern (mount → cleanup aborts → mount re-fires), same vocabulary, same per-call signal API.
The hook returns `{ data, error, reconnect, slot, status }`. Status is one of `loading | loaded | error | disabled`. After a notification arrives, an error-channel publish transitions to `error` while preserving the stale `data`; `reconnect()` returns to `loading` (preserving stale `data` and `error` for stale-while-revalidate) before settling on `loaded` or a fresh `error`. The bridge maps the store's `idle` state to `loading` when enabled (matching the about-to-commit-effect render) and to `disabled` when the source is null.
Notifications shaped as `SolanaRpcResponse<U>` (account/program/signature) are unwrapped via `isSolanaRpcResponse` from `@solana/rpc-types`: `data` is the inner value `U` and `slot` is lifted from `context.slot`. Raw notifications (slot/logs/root) pass through with `slot: undefined`. The `UnwrapRpcResponse<T>` conditional type (also from `@solana/rpc-types`) tracks the runtime unwrap at the type level.
Optional `getAbortSignal: () => AbortSignal` is invoked on every connection (initial subscribe + every `reconnect()`). The returned signal is composed with the store's per-connection controller via `AbortSignal.any` through `withSignal(signal).connect()`. The natural use is per-connection timeouts (`() => AbortSignal.timeout(30_000)`), which reset on reconnect. Factory is ref-synced — inline closures are fine. `reconnect()` accepts an optional `{ abortSignal }` override for one specific attempt; presence-based semantics distinguish "use factory" (omit the key), "explicit signal" (`{ abortSignal: signal }`), and "no signal" (`{ abortSignal: undefined }`).
SSR-safe: on the server the connect effect doesn't run, so the store stays `idle` and the hook reports `status: 'loading'`. The first client render hydrates from the same paint and commits the connect.
Adds `disabledStreamStore<T>()` to `staticStores.ts` — the stream-store analogue of `disabledActionStore` for the null-source case. Pinned by new tests alongside the existing `disabledActionStore` invariants.
Exports `SubscriptionResult<T>` and `UseSubscriptionOptions` for plugin hooks to build on.
…rt `@solana/subscribable` from kit
Switches `@solana/react` from pinning each consumed Kit sub-package individually to a single `@solana/kit` peer dependency. Promoting Kit to a peer means apps that depend on both `@solana/react` and `@solana/kit` get one deduplicated Kit instance — important for anything relying on shared types and `instanceof SolanaError` checks — and lets consumers upgrade Kit independently of React within the peer range. Mirrors the convention used by Solana program clients.
To support a single import root, this branch also adds `export * from '@solana/subscribable'` to the `@solana/kit` barrel. The reactive store types (`ReactiveStreamSource`, `ReactiveStreamStore`, `ReactiveActionSource`, etc.) and helpers (`createReactiveActionStore`, `createReactiveStoreFromDataPublisherFactory`) were already needed by anyone consuming Kit's `PendingRpcSubscriptionsRequest.reactiveStore()` — promoting them onto the Kit surface formalises that.
Every `import { … } from '@solana/subscribable'` (and the remaining sub-package imports) in `packages/react/src/**` is rewritten to `from '@solana/kit'`. No behaviour change beyond the dep-tree shape; `@solana/kit` itself depends on the same sub-packages.
`@solana/promises` stays a direct dep — it's a small utility not part of Kit's public surface.
…store config
Add `RpcSendable<T>` to `@solana/rpc-spec` and `RpcSubscribable<T>` to `@solana/rpc-subscriptions-spec` — structural duck-types covering just `send({ abortSignal })` and `subscribe({ abortSignal })` respectively. Both are intentionally narrower than the concrete `PendingRpcRequest<T>` / `PendingRpcSubscriptionsRequest<T>`, which also expose `reactiveStore()`. Use them at consumer boundaries to accept request-like objects without forcing producers to implement the full store-bearing shape.
Loosens the `rpcRequest` and `rpcSubscriptionRequest` fields on `CreateReactiveStoreWithInitialValueAndSlotTrackingConfig` (exported) and the internal config for `createAsyncGeneratorWithInitialValueAndSlotTracking` to use the new types. Both primitives only ever call `.send()` and `.subscribe()` on their inputs, so the previous `Pending*Request` constraint was over-tight — existing callers passing concrete `rpc.getAccountInfo(addr)` / `rpcSubscriptions.accountNotifications(addr)` results still satisfy the loosened types structurally.
Motivation: plugin-authored wrappers (caching layers, request batchers, request dedup) and test mocks no longer need to stub out an unused `reactiveStore()` method just to satisfy the type. Parallels the existing `ReactiveStreamSource<T>` / `ReactiveActionSource<T>` duck-types in `@solana/subscribable` — same pattern, applied to the producer side of one-shot and subscription requests.
`useTrackedData(spec)` renders reactive state for an RPC subscription seeded by a one-shot RPC fetch, slot-deduped. The subscription (e.g. `accountNotifications`) is the primary source of live updates; the initial fetch (e.g. `getBalance`, `getAccountInfo`) provides a value to surface as soon as it resolves — typically before the first subscription notification arrives — so the `loading` paint is shorter than subscription-only would give you. The underlying store slot-dedupes between the two sources: out-of-order arrivals never regress the surfaced value.
The hook is built on `createReactiveStoreWithInitialValueAndSlotTracking` from `@solana/kit` — the slot tracking, abort plumbing, and stale-while-revalidate behaviour live one layer down. The React surface reduces to `useSyncExternalStore` glue plus the per-attempt signal API. The Kit primitive's config type is re-shaped as `TrackedDataSpec<TRpcValue, TSubscriptionValue, TItem>` for friendlier use-site naming; the two are mutually assignable.
Returns `{ data, slot, error, refresh, status }` where `status` is one of `loading | loaded | error | disabled`. `slot` is lifted from the underlying `SolanaRpcResponse` envelope so consumers can show "data as of slot X" UIs. After a notification arrives, an error transitions to `status: 'error'` while preserving the stale `data` and `slot`; `refresh()` re-runs both the initial RPC and the subscription, returns `status` to `loading` (preserving stale `data` and `error` for stale-while-revalidate), and settles on `loaded` or a fresh `error`. Pass `null` for the spec to gate the work off — the result reports `status: 'disabled'`. Memoize the spec with `useMemo` keyed on its inputs — stable identity is how the hook knows when to tear down and re-run.
Optional `getAbortSignal: () => AbortSignal` is invoked on every attempt (initial run + every `refresh()`). The returned signal is composed with the store's per-attempt controller via `AbortSignal.any` through `withSignal(signal).connect()`. The natural use is per-attempt timeouts (`() => AbortSignal.timeout(30_000)`), which reset on refresh. Factory is ref-synced — inline closures are fine. `refresh()` accepts an optional `{ abortSignal }` override for one specific attempt; presence-based semantics distinguish "use factory" (omit the key), "explicit signal" (`{ abortSignal: signal }`), and "no signal" (`{ abortSignal: undefined }`).
SSR-safe: on the server the connect effect doesn't run, so the store stays `idle` and the hook reports `status: 'loading'`. The first client render hydrates from the same paint and commits the connect.
Adds an internal `useTrackedDataResult` bridge — lighter than `useSubscriptionResult` because the envelope shape is fixed at the type level (the kit primitive always emits `SolanaRpcResponse<TItem>`), so `data` and `slot` come straight off the unwrapped envelope, no runtime duck-type detection.
Exports `TrackedDataResult<T>`, `TrackedDataSpec<TRpc, TSub, T>`, and `UseTrackedDataOptions` alongside the hook so plugin hooks built on top can declare their return shape against them.
Adds an opt-in `@solana/react/swr` subpath that bridges Kit's reactive primitives into SWR's cache. Hooks under this adapter carry the `Swr` suffix to keep the cache backing visible at the call site, mirroring the spec's naming convention. `useRequestSwr(key, source, options?)` is the SWR-backed counterpart to `useRequest`. Accepts the same source shape — a `ReactiveActionSource<T>` (satisfied by `PendingRpcRequest<T>`) or a `(signal: AbortSignal) => Promise<T>` function — and routes it through SWR so components reading the same key share one in-flight fetch and participate in SWR's revalidation, persistence, devtools, and Suspense semantics. Returns SWR's native `SWRResponse<T>` (not a Kit-flavored shape) so SWR-savvy users see the API they expect. Either `key === null` or `source === null` disables the fetch. The hook coerces key to null when source is null so the two halves can be gated independently — useful when one of the source's inputs isn't yet known. Mirrors `useRequest`'s nullable-source pattern. Options merge SWR's `SWRConfiguration` with the Kit-only `getAbortSignal: () => AbortSignal` option imported from `useRequest` — same factory pattern. Invoked per attempt; the returned signal is threaded into the source (`fn(signal)` for function sources, `withSignal(signal).dispatchAsync()` for `ReactiveActionSource`). Primary use is per-attempt timeouts. SWR doesn't know about the abort itself — only the source does — but the source's rejection surfaces via `result.error`. To re-fire on demand, use SWR's native `result.mutate()` (no Kit-specific `refresh` is exposed; SWR owns that verb). Subpath plumbing follows the existing `@solana/kit/program-client-core` pattern: the `getBaseConfig.ts` tsup helper special-cases `@solana/react` to emit a `swr.*` bundle per platform, `tsconfig.declarations.json` includes the new entry, and `package.json` exports the subpath alongside the main entry with a parallel browser remap. `swr@^2` is an optional peer dependency — consumers who don't use the subpath aren't required to install it.
SWR-backed counterpart to `useSubscription`. Routes a `ReactiveStreamSource<T>` through SWR's `useSWRSubscription` so multiple components reading the same key share one underlying connection and participate in SWR's cache and devtools.
Returns SWR's native `{ data, error }` shape. `data` is a `SlotTaggedValue<T>` — a flat `{ slot, value }` wrapper that mirrors core `useSubscription`'s top-level `{ data, slot }` fields, packaged into the single `data` slot that SWR subscriptions surface. `data.value` is the unwrapped notification (`SolanaRpcResponse` envelopes are decomposed via `isSolanaRpcResponse`); `data.slot` is lifted from `context.slot` or `undefined` for raw, non-envelope notifications.
Either `key === null` or `source === null` disables the subscription, mirroring `useSubscription`'s nullable-source pattern. The Kit-only `getAbortSignal` option is imported from `useSubscription` and threaded into the source's `withSignal(signal).connect()` for per-connection signals (typically timeouts).
`SlotTaggedValue<T>` is exported from this module and re-used by the upcoming `useTrackedDataSwr` — both SWR hooks present the same flat decomposition so a switch between them is just a different generic parameter, not a different access pattern.
🦋 Changeset detectedLatest commit: 395ad3f The changes in this PR will be included in the next version bump. This PR includes changesets to release 47 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This was referenced May 28, 2026
Member
Author
This was referenced May 28, 2026
BundleMonFiles updated (28)
Unchanged files (119)
Total files change +4.13KB +0.78% Final result: ✅ View report in BundleMon website ➡️ |
Contributor
|
Documentation Preview: https://kit-docs-p7rjctju4-anza-tech.vercel.app |
25d40de to
773481f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
Summary of Changes
Fixes #