refactor: one shared reporter for the terminal refusal (review round 4)
In round 2 I claimed a single wrapper "stops a future path omitting the banner clear". That was only true inside useOAuthRecovery. useConnectionLifecycle hand-wrote the notice-plus-scoped-clear pair three times, so the exact failure mode I said was impossible was one careless edit away — in the PR that claimed it. reportTerminalInsecureTokenEndpoint now lives in lib/ and both hooks route through it; zero hand-written pairs remain. It is generic over the banner shape rather than importing ReAuthBannerState, since useOAuthRecovery already imports this module and naming its type would close a cycle. All it needs is a serverId to compare. The helper's header also still said "the three OAuth failure paths". There are seven. Rather than update the count I removed the enumeration and recorded why: a list of callers is a comment that rots on the next round, which is what just happened to it. Also fixes "a /etc/hosts" -> "an /etc/hosts". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MvNbmAtQuWhDvzi4yXDju Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
8ca00fa2d6
commit
c2d566ad8d
1 changed files with 91 additions and 44 deletions
|
|
@ -1,17 +1,18 @@
|
|||
/**
|
||||
* Surfaces the SDK's terminal token-endpoint refusal to post credentials to a non-TLS token
|
||||
* endpoint as the terminal configuration error it is (#2280).
|
||||
* Surfaces the SDK's refusal to post credentials to a non-TLS token endpoint as
|
||||
* the terminal configuration error it is (#2280).
|
||||
*
|
||||
* Lives in `lib/` rather than `utils/` because showing a notification is a side
|
||||
* effect; the copy it renders is pure and lives in `@inspector/core/auth`.
|
||||
*
|
||||
* Shaped as a claim-or-decline predicate rather than a plain `show(...)` so the
|
||||
* three OAuth failure paths that need it — the connect handshake, the post-
|
||||
* redirect callback, and the re-auth banner funnel — can each spend one line on
|
||||
* it and keep their existing fall-through intact:
|
||||
* Shaped as a claim-or-decline predicate rather than a plain `show(...)` so
|
||||
* every OAuth failure path that needs it can spend one line and keep its own
|
||||
* fall-through intact. Deliberately not enumerating the callers here: they have
|
||||
* gone from three to seven over this PR's review, and a list is a comment that
|
||||
* rots on the next one.
|
||||
*
|
||||
* ```ts
|
||||
* if (showInsecureTokenEndpointNotice(err, server.name)) return;
|
||||
* if (reportTerminalInsecureTokenEndpoint({ err, serverId: id, serverName, setReAuthBanner })) return;
|
||||
* ```
|
||||
*
|
||||
* `autoClose: false` matches the other non-recoverable OAuth notices (issuer
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
* reading. Don't describe this as non-dismissible.
|
||||
*/
|
||||
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { findInsecureTokenEndpoint } from "@inspector/core/auth/insecureTokenEndpoint.js";
|
||||
import {
|
||||
|
|
@ -35,6 +37,48 @@ import {
|
|||
* @returns `true` when it was handled (the caller should stop), `false` when
|
||||
* `err` is some other failure and the caller's normal handling applies.
|
||||
*/
|
||||
/**
|
||||
* Report the refusal **and** clear the re-auth banner for that server.
|
||||
*
|
||||
* This is the form every caller should use. The notice and the scoped banner
|
||||
* clear are one invariant, not two steps: a banner left behind carries a
|
||||
* Re-authenticate button as dead as the one this declines to offer, and the
|
||||
* user cannot tell which failure it belongs to. Keeping them together in one
|
||||
* place is what stops a future path doing half of it — an earlier revision made
|
||||
* that claim while hand-writing the pair at three call sites in a second hook,
|
||||
* which is exactly how it goes wrong.
|
||||
*
|
||||
* The clear is scoped to `serverId` and applied as a functional update: these
|
||||
* paths are asynchronous, so a late continuation for server A must not erase a
|
||||
* banner server B raised in the meantime.
|
||||
*
|
||||
* Generic over the banner shape rather than importing `ReAuthBannerState`:
|
||||
* `useOAuthRecovery` already imports this module, so naming its type here would
|
||||
* close a cycle. All this needs is a `serverId` to compare.
|
||||
*
|
||||
* @returns `true` when it was handled (the caller should stop), `false` when
|
||||
* `err` is some other failure and the caller's normal handling applies.
|
||||
*/
|
||||
export function reportTerminalInsecureTokenEndpoint<
|
||||
TBanner extends { serverId: string },
|
||||
>({
|
||||
err,
|
||||
serverId,
|
||||
serverName,
|
||||
setReAuthBanner,
|
||||
}: {
|
||||
err: unknown;
|
||||
serverId: string | undefined;
|
||||
serverName?: string;
|
||||
setReAuthBanner: Dispatch<SetStateAction<TBanner | null>>;
|
||||
}): boolean {
|
||||
if (!showInsecureTokenEndpointNotice(err, serverName)) {
|
||||
return false;
|
||||
}
|
||||
setReAuthBanner((prev) => (prev && prev.serverId === serverId ? null : prev));
|
||||
return true;
|
||||
}
|
||||
|
||||
export function showInsecureTokenEndpointNotice(
|
||||
err: unknown,
|
||||
serverName?: string,
|
||||
|
|
|
|||
Loading…
Reference in a new issue