Skip to content

fix(client): keep concurrent connect() alive across reconnect - #69

Merged
chybisov merged 1 commit into
mainfrom
fix/reconnect-hardening
Jul 27, 2026
Merged

fix(client): keep concurrent connect() alive across reconnect#69
chybisov merged 1 commit into
mainfrom
fix/reconnect-hardening

Conversation

@chybisov

Copy link
Copy Markdown
Member

Follow-up to #66 / #67.

Problem

reconnect runs on mount and polls each connector for a provider for up to 5s (retryUntil, 5000ms), so it is commonly in flight for several seconds on every page load. A user can complete a manual connect() inside that window.

When no connector reconnected, the tail reset fired unconditionally and wiped that fresh connection:

if (!connected) {
  config.setState((x) => ({ ...x, connections: new Map(), current: null, status: 'disconnected' }))
}

The user ends up disconnected immediately after successfully connecting.

Fix

Gate the reset on the store still being in the state reconnect put it in, so it only clears what reconnect itself owns:

const isStillReconnecting =
  config.state.status === 'reconnecting' || config.state.status === 'connecting'

if (!connected && isStillReconnecting) { /* reset */ }

This is wagmi's guard ("Prevent overwriting connected status from race condition"), the one piece of that block #66 did not port. Both interleavings are safe: if connect() finishes first the status is connected and the reset is skipped; if it is still in flight the status is connecting, the reset clears an empty map, and connect() then writes its own state on top.

Tests

Restores the @bigmi/client vitest setup and unisat.spec.ts that were added and then removed within #67 — the specs pass unchanged against the merged code. Adds reconnect.spec.ts covering:

pnpm test now covers @bigmi/client as well: 6 passed (client), 32 passed / 1 skipped (core).

Considered and rejected

Making current deterministic under parallelism (first-to-resolve wins rather than first in sorted order, so recentConnectorId priority is best-effort). Both implementations — rank tracking shared across parallel tasks, or a post-hoc re-pick — add a second synchronicity invariant to already-subtle code, and the loser is still a live connector. Not worth the complexity; happy to revisit if multi-wallet users report it.

Validation

pnpm check, pnpm check:types, pnpm check:circular-deps, pnpm knip:check, pnpm test all pass.

reconnect runs on mount and polls for a wallet provider for up to 5s, so a
user can complete a manual connect() while it is still in flight. When no
connector reconnected, the tail reset wiped that fresh connection along
with the stale rehydrated entries.

Gate the reset on the store still being in the reconnecting/connecting
state reconnect put it in, matching wagmi.

Also restore the @bigmi/client vitest setup and the UniSat connector spec
removed in #67, and add reconnect specs covering the stale-connection
replacement, the no-connector reset, and the race above.
@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c895e02

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

This PR includes changesets to release 2 packages
Name Type
@bigmi/client Patch
@bigmi/react Patch

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

@chybisov
chybisov merged commit 7ff01b5 into main Jul 27, 2026
6 checks passed
@chybisov
chybisov deleted the fix/reconnect-hardening branch July 27, 2026 12:07
@github-actions github-actions Bot mentioned this pull request Jul 27, 2026
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.

1 participant