Skip to content

fix(client): remove frozen ObjectStackClient singleton (server-switch split-brain) - #107

Merged
os-zhuang merged 1 commit into
mainfrom
fix/server-switch-hardening
Jun 10, 2026
Merged

fix(client): remove frozen ObjectStackClient singleton (server-switch split-brain)#107
os-zhuang merged 1 commit into
mainfrom
fix/server-switch-hardening

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Hardening from the UX session's flagged runtime server-switch gap.

The landmine

objectStackClient was a module-level client built once at import with the initial API_URL. ObjectStackClient snapshots baseUrl in its constructor, so the instance stayed pinned to the startup host — after the user connects to a different server, anything importing it would silently talk to the old host (split-brain).

It had no real consumers (just an "is defined" test). Every live call site already uses either the React-provider client (recreated on server/token change via _layout's useMemo([token, serverUrl])) or the live-URL helpers apiFetch/resolveApiUrl/buildAuthInit (which read API_URL at call time).

Change

  • Remove the frozen const.
  • Keep getObjectStackClient() — it builds a fresh client against the current URL — as the documented non-React primitive, with a comment on why there is deliberately no singleton.
  • Regression test: getObjectStackClient() always reflects the latest setObjectStackApiUrl across a switch (server-a → server-b).
  • Drive-by: fix a latent tsc error in the P2 OptionBadge test (type: "status" isn't a valid FieldType literal → "select"; it slipped through because jest's babel transform doesn't typecheck).

Verification: this isn't browser-observable (dead-code removal + a guarantee real consumers already had). tsc clean, the new regression test + full suite pass, lint clean.

🤖 Generated with Claude Code

… split-brain)

`objectStackClient` was a module-level client built once at import with the
initial API_URL. `ObjectStackClient` snapshots `baseUrl` at construction, so it
stayed pinned to the startup host and would silently talk to the wrong server
after the user connected elsewhere — a split-brain landmine. It had no real
consumers (only a "is defined" test); every live call site uses the
React-provider client (recreated on server/token change) or the live-URL
helpers (`apiFetch`/`resolveApiUrl`, which read API_URL at call time).

Remove the const; keep `getObjectStackClient()` (builds fresh against the
current URL) as the documented non-React primitive, with a note on why there is
no singleton. Add a regression test asserting it always reflects the latest
`setObjectStackApiUrl` across a switch.

Also fixes a latent tsc error in the P2 OptionBadge test (`type: "status"` is
not a valid FieldType literal → `"select"`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit 9196f8e into main Jun 10, 2026
1 of 4 checks passed
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