Skip to content

Move bridgeStoreToAsyncIterable from react/query to subscribable/ - #1811

Merged
mcintyre94 merged 1 commit into
mainfrom
worktree-stream-store-async-iterable
Jul 13, 2026
Merged

Move bridgeStoreToAsyncIterable from react/query to subscribable/#1811
mcintyre94 merged 1 commit into
mainfrom
worktree-stream-store-async-iterable

Conversation

@mcintyre94

@mcintyre94 mcintyre94 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Problem

When we added the Tanstack Query versions of useSubscription and useTrackedData, we added a bridge to an AsyncIterable from the ReactiveStreamStore. This is how TanStack's (experimental) streamedQuery is implemented.

This bridge can be useful for consumers outside of Tanstack Query, which want to consume an AsyncIterable. It's likely that more/future libraries will eventually standardise on this data structure too.

We intentionally didn't just use the AsyncIterable from rpcSubscriptions.method().subscribe() for this, for compatibility with useSubscription and also because the store lifecycle is better suited for UI, always providing the latest data and providing status etc.

So any other consumer looking for an AsyncIterable in reactive UI would benefit from this bridge, which was internal in react/query.

Summary of Changes

This PR moves bridgeStoreToAsyncIterable to packages/subscribable as a public export.

It's made less opinionated and now just observes the store, the caller still controls connect/reset. This aligns it with the rest of the subscribable API. The react/query consumers are updated accordingly.

@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d67e6c8

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

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

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

@bundlemon

bundlemon Bot commented Jul 1, 2026

Copy link
Copy Markdown

BundleMon

Files updated (10)
Status Path Size Limits
subscribable/dist/index.native.mjs
3.06KB (+334B +11.94%) -
subscribable/dist/index.node.mjs
3.13KB (+334B +11.65%) -
subscribable/dist/index.browser.mjs
3.05KB (+333B +11.93%) -
@solana/kit production bundle
kit/dist/index.production.min.js
55.12KB (+255B +0.45%) -
errors/dist/index.browser.mjs
21.58KB (+53B +0.24%) -
errors/dist/index.native.mjs
21.58KB (+53B +0.24%) -
errors/dist/index.node.mjs
21.6KB (+52B +0.24%) -
wallet-account-signer/dist/index.browser.mjs
18.29KB (+33B +0.18%) -
wallet-account-signer/dist/index.native.mjs
18.29KB (+33B +0.18%) -
wallet-account-signer/dist/index.node.mjs
18.31KB (+33B +0.18%) -
Unchanged files (140)
Status Path Size Limits
rpc-graphql/dist/index.browser.mjs
18.82KB -
rpc-graphql/dist/index.native.mjs
18.82KB -
rpc-graphql/dist/index.node.mjs
18.82KB -
transaction-messages/dist/index.browser.mjs
11.34KB -
transaction-messages/dist/index.native.mjs
11.34KB -
transaction-messages/dist/index.node.mjs
11.34KB -
instruction-plans/dist/index.browser.mjs
7.02KB -
instruction-plans/dist/index.native.mjs
7.02KB -
instruction-plans/dist/index.node.mjs
7.02KB -
codecs-data-structures/dist/index.browser.mjs
5.3KB -
codecs-data-structures/dist/index.native.mjs
5.3KB -
codecs-data-structures/dist/index.node.mjs
5.29KB -
fixed-points/dist/index.browser.mjs
5.08KB -
fixed-points/dist/index.native.mjs
5.07KB -
fixed-points/dist/index.node.mjs
5.07KB -
offchain-messages/dist/index.browser.mjs
5.06KB -
offchain-messages/dist/index.native.mjs
5.06KB -
offchain-messages/dist/index.node.mjs
5.06KB -
react/dist/index.browser.mjs
5.02KB -
react/dist/index.node.mjs
5.02KB -
react/dist/index.native.mjs
5.02KB -
kit/dist/index.browser.mjs
4.6KB -
kit/dist/index.native.mjs
4.6KB -
kit/dist/index.node.mjs
4.6KB -
transactions/dist/index.browser.mjs
4.07KB -
transactions/dist/index.native.mjs
4.07KB -
transactions/dist/index.node.mjs
4.07KB -
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.15KB -
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 -
transaction-introspection/dist/index.browser.
mjs
2.75KB -
transaction-introspection/dist/index.native.m
js
2.75KB -
transaction-introspection/dist/index.node.mjs
2.75KB -
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.42KB -
transaction-confirmation/dist/index.native.mj
s
2.37KB -
sysvars/dist/index.browser.mjs
2.37KB -
sysvars/dist/index.native.mjs
2.37KB -
transaction-confirmation/dist/index.browser.m
js
2.37KB -
sysvars/dist/index.node.mjs
2.37KB -
rpc-subscriptions-spec/dist/index.node.mjs
2.23KB -
rpc-subscriptions-spec/dist/index.native.mjs
2.19KB -
rpc-subscriptions-spec/dist/index.browser.mjs
2.19KB -
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-types/dist/index.browser.mjs
1.9KB -
rpc-types/dist/index.native.mjs
1.9KB -
rpc-types/dist/index.node.mjs
1.9KB -
rpc-transport-http/dist/index.browser.mjs
1.89KB -
rpc-transport-http/dist/index.native.mjs
1.89KB -
rpc/dist/index.native.mjs
1.81KB -
rpc/dist/index.browser.mjs
1.8KB -
rpc-transport-http/dist/index.node.mjs
1.71KB -
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-spec-types/dist/index.browser.mjs
1.15KB -
rpc-spec-types/dist/index.native.mjs
1.15KB -
rpc-spec-types/dist/index.node.mjs
1.15KB -
rpc-api/dist/index.browser.mjs
1015B -
rpc-api/dist/index.native.mjs
1014B -
rpc-api/dist/index.node.mjs
1012B -
compat/dist/index.browser.mjs
969B -
compat/dist/index.native.mjs
968B -
compat/dist/index.node.mjs
966B -
rpc-spec/dist/index.browser.mjs
898B -
rpc-spec/dist/index.native.mjs
897B -
rpc-spec/dist/index.node.mjs
896B -
rpc-subscriptions-api/dist/index.native.mjs
871B -
rpc-subscriptions-api/dist/index.browser.mjs
870B -
rpc-subscriptions-api/dist/index.node.mjs
870B -
promises/dist/index.native.mjs
841B -
promises/dist/index.node.mjs
840B -
promises/dist/index.browser.mjs
839B -
plugin-core/dist/index.browser.mjs
799B -
plugin-core/dist/index.native.mjs
798B -
plugin-core/dist/index.node.mjs
796B -
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 +1.48KB +0.27%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

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

@mcintyre94

Copy link
Copy Markdown
Member Author

@trevor-cortex

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice refactor — LGTM overall (leaving as COMMENT since I don't have approval rights on this repo). Moving bridgeStoreToAsyncIterable to @solana/subscribable is the right call: it's a store adapter with no React/TanStack coupling, and the behavioural change (bridge only observes; caller owns connect()/reset()) aligns it with the rest of the package where a store does nothing until you connect it. The new "seeds from current snapshot" semantic falls out cleanly and is a nice quality-of-life improvement — order of connect vs iteration no longer matters.

Correctness spot-checks all pass:

  • Seeding: subscribe then a synchronous onChange() is safe — latest-wins collapses any duplicate emission if the store also fires synchronously on subscribe.
  • Already-aborted signal: the while (true) loop's first check is signal.aborted, so an entering-aborted state returns cleanly before touching failure/latest. Abort still wins over an incidental store error triggered by the abort itself.
  • Already-errored store: onChange() seeds failure and the loop throws on first pull — covered by the new throws an error already present when iteration begins test.
  • Hook call sites: both useSubscriptionQuery and useTrackedDataQuery connect before handing the store to the bridge, so the pending first pull will see either the seeded value or park correctly.

Error code addition is well-placed (sequential in the reserved 8195000–8195999 subscribable range, added to the union alphabetically, matching message). Retaining SOLANA_ERROR__REACT__SUBSCRIPTION_CLOSED_WITHOUT_ERROR for the SWR bridge is correct — you can't remove codes, and re-pointing SWR would be a breaking behavioural change. Worth a follow-up thought (not this PR): the two codes now describe the same condition ("stream closed without a payload"), so consumers checking one won't catch the other. If SWR ever migrates in a major bump it'd be nice to converge on the new subscribable code.

Docs (reactive-stores.mdx, subscribable/README.md) are excellent — the contrast against createAsyncIterableFromDataPublisher (queued, publisher-level) and the RPC subscription's own iterable (raw transport, no store lifecycle) is exactly the framing readers need. Docblock covers @param/@throws/@see/@example per the skill.

Test updates look right, and the switch to throwing stubs for connect/withSignal/reset in createFakeStore is textbook per the AGENTS.md placeholder-mock guidance — an accidental lifecycle call now fails loudly.

One consistency question for subsequent reviewers to weigh in on (see inline on useTrackedDataQuery.ts).

Changeset marks @solana/subscribable, @solana/errors, @solana/kit, @solana/react all minor. That looks right: new public export in subscribable+kit+errors; react's useSubscriptionQuery/useTrackedDataQuery change is behavioural (no more reset() on teardown) but the consumer-observable state is the same (connection torn down via signal abort, store GC'd), so minor is defensible pre-1.0.

Comment thread packages/react/src/query/useTrackedDataQuery.ts
@mcintyre94
mcintyre94 force-pushed the worktree-stream-store-async-iterable branch from d480375 to d67e6c8 Compare July 2, 2026 08:55
@mcintyre94
mcintyre94 requested a review from lorisleiva July 6, 2026 10:12

@lorisleiva lorisleiva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Makes sense!

@mcintyre94
mcintyre94 added this pull request to the merge queue Jul 13, 2026
Merged via the queue into main with commit 7022c26 Jul 13, 2026
13 checks passed
@mcintyre94
mcintyre94 deleted the worktree-stream-store-async-iterable branch July 13, 2026 12:25
@github-actions

Copy link
Copy Markdown
Contributor

Because there has been no activity on this PR for 14 days since it was merged, it has been automatically locked. Please open a new issue if it requires a follow up.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Jul 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants