refactor(account): share token freshness helper (#20591)

pull/20592/head^2
Kit Langton 2026-04-01 22:57:45 -04:00 committed by GitHub
parent 5daf2fa7f0
commit 916afb5220
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 13 additions and 6 deletions

View File

@ -120,6 +120,10 @@ class TokenRefreshRequest extends Schema.Class<TokenRefreshRequest>("TokenRefres
const clientId = "opencode-cli" const clientId = "opencode-cli"
const eagerRefreshThreshold = Duration.minutes(5) const eagerRefreshThreshold = Duration.minutes(5)
const eagerRefreshThresholdMs = Duration.toMillis(eagerRefreshThreshold)
const isTokenFresh = (tokenExpiry: number | null, now: number) =>
tokenExpiry != null && tokenExpiry > now + eagerRefreshThresholdMs
const mapAccountServiceError = const mapAccountServiceError =
(message = "Account service operation failed") => (message = "Account service operation failed") =>
@ -219,7 +223,7 @@ export namespace Account {
const account = maybeAccount.value const account = maybeAccount.value
const now = yield* Clock.currentTimeMillis const now = yield* Clock.currentTimeMillis
if (account.token_expiry && account.token_expiry > now + Duration.toMillis(eagerRefreshThreshold)) { if (isTokenFresh(account.token_expiry, now)) {
return account.access_token return account.access_token
} }
@ -229,7 +233,7 @@ export namespace Account {
const resolveToken = Effect.fnUntraced(function* (row: AccountRow) { const resolveToken = Effect.fnUntraced(function* (row: AccountRow) {
const now = yield* Clock.currentTimeMillis const now = yield* Clock.currentTimeMillis
if (row.token_expiry && row.token_expiry > now + Duration.toMillis(eagerRefreshThreshold)) { if (isTokenFresh(row.token_expiry, now)) {
return row.access_token return row.access_token
} }

View File

@ -18,6 +18,9 @@ const truncate = Layer.effectDiscard(
const it = testEffect(Layer.merge(AccountRepo.layer, truncate)) const it = testEffect(Layer.merge(AccountRepo.layer, truncate))
const insideEagerRefreshWindow = Duration.toMillis(Duration.minutes(1))
const outsideEagerRefreshWindow = Duration.toMillis(Duration.minutes(10))
const live = (client: HttpClient.HttpClient) => const live = (client: HttpClient.HttpClient) =>
Account.layer.pipe(Layer.provide(Layer.succeed(HttpClient.HttpClient, client))) Account.layer.pipe(Layer.provide(Layer.succeed(HttpClient.HttpClient, client)))
@ -63,7 +66,7 @@ it.live("orgsByAccount groups orgs per account", () =>
url: "https://one.example.com", url: "https://one.example.com",
accessToken: AccessToken.make("at_1"), accessToken: AccessToken.make("at_1"),
refreshToken: RefreshToken.make("rt_1"), refreshToken: RefreshToken.make("rt_1"),
expiry: Date.now() + 10 * 60_000, expiry: Date.now() + outsideEagerRefreshWindow,
orgID: Option.none(), orgID: Option.none(),
}), }),
) )
@ -75,7 +78,7 @@ it.live("orgsByAccount groups orgs per account", () =>
url: "https://two.example.com", url: "https://two.example.com",
accessToken: AccessToken.make("at_2"), accessToken: AccessToken.make("at_2"),
refreshToken: RefreshToken.make("rt_2"), refreshToken: RefreshToken.make("rt_2"),
expiry: Date.now() + 10 * 60_000, expiry: Date.now() + outsideEagerRefreshWindow,
orgID: Option.none(), orgID: Option.none(),
}), }),
) )
@ -159,7 +162,7 @@ it.live("token refreshes before expiry when inside the eager refresh window", ()
url: "https://one.example.com", url: "https://one.example.com",
accessToken: AccessToken.make("at_old"), accessToken: AccessToken.make("at_old"),
refreshToken: RefreshToken.make("rt_old"), refreshToken: RefreshToken.make("rt_old"),
expiry: Date.now() + 60_000, expiry: Date.now() + insideEagerRefreshWindow,
orgID: Option.none(), orgID: Option.none(),
}), }),
) )
@ -267,7 +270,7 @@ it.live("config sends the selected org header", () =>
url: "https://one.example.com", url: "https://one.example.com",
accessToken: AccessToken.make("at_1"), accessToken: AccessToken.make("at_1"),
refreshToken: RefreshToken.make("rt_1"), refreshToken: RefreshToken.make("rt_1"),
expiry: Date.now() + 10 * 60_000, expiry: Date.now() + outsideEagerRefreshWindow,
orgID: Option.none(), orgID: Option.none(),
}), }),
) )