Skip to content

Add reactiveStore to PendingRpcRequest#1555

Merged
mcintyre94 merged 1 commit into
mainfrom
rpc-reactive
May 7, 2026
Merged

Add reactiveStore to PendingRpcRequest#1555
mcintyre94 merged 1 commit into
mainfrom
rpc-reactive

Conversation

@mcintyre94
Copy link
Copy Markdown
Member

@mcintyre94 mcintyre94 commented Apr 22, 2026

Summary of Changes

This PR adds .reactiveStore()to PendingRpcRequest, similar to how we use it for PendingRpcSubscriptionRequest. This sends the request and synchronously returns a ReactiveActionStore.

As with our other reactive store features, this is intended to provide a better API for reactive consumers:

const store = rpc.getAccountInfo(address).reactiveStore();
const state = useSyncExternalStore(store.subscribe, store.getState);
if (state.status === 'error') return <ErrorMessage error={state.error} onRetry={store.dispatch} />;
if (state.status === 'running' && !state.data) return <Spinner />;
return <View data={state.data!} />;

Note that this has auto-dispatch semantics, meaning calling it has the same sort of behaviour as calling send() or the similar reactiveStore() on subscriptions.

Consumers that want to control sending precisely can use createActionStore((signal) => rpc.getAccountInfo(address).send({abortSignal: signal}) - this is just a thin wrapper that adds auto-dispatch and therefore skips the idle state.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 22, 2026

🦋 Changeset detected

Latest commit: f48797d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 47 packages
Name Type
@solana/rpc-spec Minor
@solana/kit Minor
@solana/accounts Minor
@solana/plugin-interfaces Minor
@solana/rpc-api Minor
@solana/rpc-transport-http Minor
@solana/rpc Minor
@solana/sysvars Minor
@solana/program-client-core Minor
@solana/rpc-graphql Minor
@solana/transaction-confirmation Minor
@solana/addresses Minor
@solana/assertions Minor
@solana/codecs-core Minor
@solana/codecs-data-structures Minor
@solana/codecs-numbers Minor
@solana/codecs-strings Minor
@solana/codecs Minor
@solana/compat Minor
@solana/errors Minor
@solana/fast-stable-stringify Minor
@solana/fixed-points Minor
@solana/functional Minor
@solana/instruction-plans Minor
@solana/instructions Minor
@solana/keys Minor
@solana/nominal-types Minor
@solana/offchain-messages Minor
@solana/options Minor
@solana/plugin-core Minor
@solana/programs Minor
@solana/promises Minor
@solana/react Minor
@solana/rpc-parsed-types Minor
@solana/rpc-spec-types Minor
@solana/rpc-subscriptions-api Minor
@solana/rpc-subscriptions-channel-websocket Minor
@solana/rpc-subscriptions-spec Minor
@solana/rpc-subscriptions Minor
@solana/rpc-transformers Minor
@solana/rpc-types Minor
@solana/signers Minor
@solana/subscribable Minor
@solana/transaction-messages Minor
@solana/transactions Minor
@solana/wallet-account-signer Minor
@solana/webcrypto-ed25519-polyfill Minor

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

Copy link
Copy Markdown
Member Author

mcintyre94 commented Apr 22, 2026

@bundlemon
Copy link
Copy Markdown

bundlemon Bot commented Apr 22, 2026

BundleMon

Files updated (4)
Status Path Size Limits
@solana/kit production bundle
kit/dist/index.production.min.js
52.24KB (+322B +0.61%) -
rpc-spec/dist/index.native.mjs
918B (+67B +7.87%) -
rpc-spec/dist/index.node.mjs
917B (+67B +7.88%) -
rpc-spec/dist/index.browser.mjs
918B (+66B +7.75%) -
Unchanged files (143)
Status Path Size Limits
errors/dist/index.node.mjs
20.54KB -
errors/dist/index.browser.mjs
20.52KB -
errors/dist/index.native.mjs
20.52KB -
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.81KB -
rpc-graphql/dist/index.node.mjs
18.81KB -
wallet-account-signer/dist/index.node.mjs
17.38KB -
wallet-account-signer/dist/index.browser.mjs
17.37KB -
wallet-account-signer/dist/index.native.mjs
17.37KB -
transaction-messages/dist/index.browser.mjs
11.32KB -
transaction-messages/dist/index.native.mjs
11.32KB -
transaction-messages/dist/index.node.mjs
11.32KB -
instruction-plans/dist/index.browser.mjs
6.58KB -
instruction-plans/dist/index.native.mjs
6.58KB -
instruction-plans/dist/index.node.mjs
6.58KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
codecs-data-structures/dist/index.browser.mjs
5.04KB -
codecs-data-structures/dist/index.native.mjs
5.03KB -
codecs-data-structures/dist/index.node.mjs
5.03KB -
offchain-messages/dist/index.browser.mjs
4.89KB -
offchain-messages/dist/index.native.mjs
4.89KB -
offchain-messages/dist/index.node.mjs
4.89KB -
transactions/dist/index.browser.mjs
4.07KB -
transactions/dist/index.native.mjs
4.07KB -
transactions/dist/index.node.mjs
4.07KB -
kit/dist/index.browser.mjs
3.97KB -
kit/dist/index.native.mjs
3.97KB -
kit/dist/index.node.mjs
3.97KB -
codecs-core/dist/index.browser.mjs
3.62KB -
codecs-core/dist/index.native.mjs
3.62KB -
codecs-core/dist/index.node.mjs
3.62KB -
webcrypto-ed25519-polyfill/dist/index.node.mj
s
3.61KB -
webcrypto-ed25519-polyfill/dist/index.browser
.mjs
3.59KB -
webcrypto-ed25519-polyfill/dist/index.native.
mjs
3.57KB -
rpc-subscriptions/dist/index.browser.mjs
3.37KB -
rpc-subscriptions/dist/index.node.mjs
3.34KB -
rpc-subscriptions/dist/index.native.mjs
3.31KB -
signers/dist/index.browser.mjs
3.26KB -
signers/dist/index.native.mjs
3.26KB -
signers/dist/index.node.mjs
3.26KB -
rpc-transformers/dist/index.browser.mjs
3.16KB -
rpc-transformers/dist/index.native.mjs
3.16KB -
rpc-transformers/dist/index.node.mjs
3.16KB -
react/dist/index.browser.mjs
3.09KB -
react/dist/index.native.mjs
3.09KB -
react/dist/index.node.mjs
3.09KB -
keys/dist/index.node.mjs
3.06KB -
addresses/dist/index.browser.mjs
2.93KB -
addresses/dist/index.native.mjs
2.92KB -
addresses/dist/index.node.mjs
2.92KB -
keys/dist/index.browser.mjs
2.85KB -
keys/dist/index.native.mjs
2.85KB -
subscribable/dist/index.node.mjs
2.68KB -
subscribable/dist/index.native.mjs
2.61KB -
subscribable/dist/index.browser.mjs
2.6KB -
codecs-strings/dist/index.browser.mjs
2.55KB -
codecs-strings/dist/index.node.mjs
2.51KB -
codecs-strings/dist/index.native.mjs
2.47KB -
transaction-confirmation/dist/index.node.mjs
2.41KB -
sysvars/dist/index.browser.mjs
2.37KB -
sysvars/dist/index.native.mjs
2.37KB -
sysvars/dist/index.node.mjs
2.37KB -
transaction-confirmation/dist/index.native.mj
s
2.36KB -
transaction-confirmation/dist/index.browser.m
js
2.35KB -
rpc-subscriptions-spec/dist/index.node.mjs
2.25KB -
rpc-subscriptions-spec/dist/index.native.mjs
2.2KB -
rpc-subscriptions-spec/dist/index.browser.mjs
2.2KB -
rpc/dist/index.node.mjs
1.95KB -
codecs-numbers/dist/index.browser.mjs
1.95KB -
codecs-numbers/dist/index.native.mjs
1.95KB -
codecs-numbers/dist/index.node.mjs
1.94KB -
rpc-transport-http/dist/index.browser.mjs
1.91KB -
rpc-transport-http/dist/index.native.mjs
1.9KB -
rpc/dist/index.native.mjs
1.81KB -
rpc-types/dist/index.browser.mjs
1.8KB -
rpc/dist/index.browser.mjs
1.8KB -
rpc-types/dist/index.native.mjs
1.8KB -
rpc-types/dist/index.node.mjs
1.8KB -
rpc-transport-http/dist/index.node.mjs
1.72KB -
rpc-subscriptions-channel-websocket/dist/inde
x.node.mjs
1.33KB -
rpc-subscriptions-channel-websocket/dist/inde
x.native.mjs
1.27KB -
rpc-subscriptions-channel-websocket/dist/inde
x.browser.mjs
1.26KB -
program-client-core/dist/index.browser.mjs
1.21KB -
program-client-core/dist/index.native.mjs
1.21KB -
program-client-core/dist/index.node.mjs
1.21KB -
options/dist/index.browser.mjs
1.18KB -
options/dist/index.native.mjs
1.18KB -
options/dist/index.node.mjs
1.17KB -
accounts/dist/index.browser.mjs
1.17KB -
accounts/dist/index.native.mjs
1.17KB -
accounts/dist/index.node.mjs
1.16KB -
rpc-api/dist/index.browser.mjs
976B -
rpc-api/dist/index.native.mjs
975B -
rpc-api/dist/index.node.mjs
973B -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec-types/dist/index.browser.mjs
962B -
rpc-spec-types/dist/index.native.mjs
961B -
rpc-spec-types/dist/index.node.mjs
959B -
rpc-subscriptions-api/dist/index.native.mjs
870B -
rpc-subscriptions-api/dist/index.node.mjs
869B -
rpc-subscriptions-api/dist/index.browser.mjs
868B -
promises/dist/index.native.mjs
841B -
promises/dist/index.node.mjs
840B -
promises/dist/index.browser.mjs
839B -
plugin-core/dist/index.browser.mjs
820B -
plugin-core/dist/index.native.mjs
819B -
plugin-core/dist/index.node.mjs
817B -
assertions/dist/index.browser.mjs
783B -
instructions/dist/index.browser.mjs
771B -
instructions/dist/index.native.mjs
770B -
instructions/dist/index.node.mjs
768B -
fast-stable-stringify/dist/index.browser.mjs
726B -
fast-stable-stringify/dist/index.native.mjs
725B -
assertions/dist/index.native.mjs
724B -
fast-stable-stringify/dist/index.node.mjs
724B -
assertions/dist/index.node.mjs
723B -
programs/dist/index.browser.mjs
329B -
programs/dist/index.native.mjs
327B -
programs/dist/index.node.mjs
325B -
fs-impl/dist/index.browser.mjs
245B -
event-target-impl/dist/index.node.mjs
230B -
functional/dist/index.browser.mjs
154B -
functional/dist/index.native.mjs
152B -
text-encoding-impl/dist/index.native.mjs
152B -
functional/dist/index.node.mjs
151B -
codecs/dist/index.browser.mjs
145B -
codecs/dist/index.native.mjs
144B -
codecs/dist/index.node.mjs
142B -
event-target-impl/dist/index.browser.mjs
133B -
ws-impl/dist/index.node.mjs
131B -
text-encoding-impl/dist/index.browser.mjs
122B -
fs-impl/dist/index.node.mjs
120B -
text-encoding-impl/dist/index.node.mjs
119B -
ws-impl/dist/index.browser.mjs
113B -
crypto-impl/dist/index.node.mjs
111B -
crypto-impl/dist/index.browser.mjs
109B -
rpc-parsed-types/dist/index.browser.mjs
66B -
rpc-parsed-types/dist/index.native.mjs
65B -
rpc-parsed-types/dist/index.node.mjs
63B -

Total files change +522B +0.1%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 22, 2026

Documentation Preview: https://kit-docs-1hqmgirgi-anza-tech.vercel.app

@mcintyre94
Copy link
Copy Markdown
Member Author

@trevor-cortex

Copy link
Copy Markdown

@trevor-cortex trevor-cortex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, clean addition. The reactiveStore() method mirrors the existing PendingRpcSubscriptionRequest.reactiveStore() pattern, and the implementation is a tiny, easy-to-reason-about wrapper: createReactiveActionStore + immediate auto-dispatch. Docblock + README + changeset + tests are all in place. LGTM modulo the comments below.

Summary

  • New reactiveStore(): ReactiveActionStore<[], TResponse> method on PendingRpcRequest, auto-fires on construction and returns a store with the standard { subscribe, getState, dispatch, dispatchAsync, reset } contract.
  • @solana/subscribable added as a runtime dep of @solana/rpc-spec.
  • ES2024.Promise added to the rpc-spec tsconfig lib (needed for Promise.withResolvers() in the new tests).
  • Mock PendingRpcRequest shapes updated across several packages (accounts, kit, rpc-graphql, transaction-confirmation) to satisfy the expanded type.
  • New CLAUDE.md guidance about preferring jest.useFakeTimers() + runAllTimersAsync() over hand-rolled microtask flushers, which the new tests themselves follow.

Things to watch / notes for subsequent reviewers

  • No external abortSignal option on reactiveStore() — unlike send({ abortSignal }). Consumers tie cancellation to store.reset() or let dispatch() supersede in-flight calls. The PR description mentions the escape hatch (createActionStore((signal) => rpc.getAccountInfo(address).send({ abortSignal: signal }))), which is fine, but worth confirming this is the intended ergonomic — a follow-up that accepts an external signal wouldn't be a breaking change, so deferring is reasonable.
  • Auto-dispatch semantics: the ReactiveActionStore returned here can re-enter idle via reset() but not re-enter running without an explicit dispatch(). Documented in the changeset/README; flagging because it's the main behavioural subtlety.
  • @solana/subscribable in @solana/rpc-spec's runtime deps pulls the event-target-impl / promises chain into the spec package. Consistent with the subscription-side precedent, but it does slightly expand the spec package's surface — worth a sanity-check.
  • Test hygiene: the new describe installs fake timers in beforeEach with no matching afterEach to restore real timers — see inline comment.
  • Bundle-size impact on @solana/kit via the new transitive dep — BundleMon will tell us, but worth eyeballing.

Comment thread packages/rpc-spec/src/__tests__/rpc-test.ts
Comment on lines 118 to +122
plan: RpcPlan<TResponse>,
): PendingRpcRequest<TResponse> {
return {
reactiveStore(): ReactiveActionStore<[], TResponse> {
const store = createReactiveActionStore<[], TResponse>(signal => plan.execute({ signal, transport }));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question (non-blocking): would it be worth accepting an optional { abortSignal } here, mirroring send(options)? Use cases: binding the store's lifetime to a parent controller (e.g. Svelte onDestroy, Vue onScopeDispose, or a composed cancellation tree) without having to manually wire reset(). The escape-hatch pattern in the PR description (createActionStore(signal => ...send({ abortSignal: signal }))) works but loses the one-liner ergonomics. Happy to defer — this is purely additive and can be layered on later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considered this but I don't think it's needed. There's nothing that can leak here - the http request is abortable and doesn't leave anything behind. In contrast to subscriptions where we have a subscription to clean up.

Having both signals requires fiddly wiring in subscriptions, which is worth it there but would just be complexity for no value here IMO.

Comment thread packages/rpc-graphql/src/loaders/__tests__/account-loader-test.ts Outdated
@mcintyre94 mcintyre94 marked this pull request as ready for review April 23, 2026 09:48
@mcintyre94 mcintyre94 requested a review from lorisleiva April 23, 2026 09:49
Copy link
Copy Markdown
Member

@lorisleiva lorisleiva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late review, looks good to me! 👌

Copy link
Copy Markdown
Member Author

mcintyre94 commented May 7, 2026

Merge activity

  • May 7, 2:56 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 7, 3:34 PM UTC: Graphite rebased this pull request as part of a merge.
  • May 7, 3:43 PM UTC: @mcintyre94 merged this pull request with Graphite.

@mcintyre94 mcintyre94 changed the base branch from action-store to graphite-base/1555 May 7, 2026 15:21
@mcintyre94 mcintyre94 changed the base branch from graphite-base/1555 to main May 7, 2026 15:32
@mcintyre94 mcintyre94 merged commit 5e1644d into main May 7, 2026
14 checks passed
@mcintyre94 mcintyre94 deleted the rpc-reactive branch May 7, 2026 15:43
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🔎💬 Inkeep AI search and chat service is syncing content for source 'Solana Kit Docs'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants