/** * Removing or replacing a stored Google credential, which never happens * without the owner saying yes first. * * The defect this exists to fix: mid-flow, with nothing asked and nothing * announced, the stored refresh token was deleted. The owner found out because * the connection stopped working. A refresh token is not a cache entry, it is * the thing that took a person through a consent screen to obtain, and on a * published app it is the only durable half of the credential. Deleting one * unprompted destroys work the machine cannot recreate on its own. * * So this module makes the destructive step impossible to reach by accident. * There is no function here that deletes on the first call. `planRemoval` * returns a sentence naming exactly what would go; only a second call carrying * an explicit yes actually removes anything, and the result states what was * removed rather than reporting a bare success. * * The same gate covers REPLACEMENT, because overwriting a refresh token with a * different one destroys the old one just as thoroughly as deleting it. */ import type { GoogleConfigPort, GoogleSecretPort } from './types.js'; /** * Secret storage that can also remove. Deliberately separate from * `GoogleSecretPort`: a store with no delete simply cannot perform a removal, * and that must be a typed refusal rather than a silent no-op. */ export interface GoogleRemovableSecretPort extends GoogleSecretPort { delete(key: string): Promise; } /** Which stored things a removal would touch. */ export type GoogleCredentialItem = 'refresh-token' | 'client-secret' | 'app-password' | 'calendar-address'; /** Nothing has been removed. This is the only thing a first call can return. */ export interface GoogleRemovalPlan { readonly confirmed: false; readonly removed: readonly []; /** The items that WOULD be removed, by safe label. */ readonly wouldRemove: readonly string[]; /** One sentence, ending in a question. Show this and wait for a yes. */ readonly prompt: string; } /** Something was actually removed, and this says exactly what. */ export interface GoogleRemovalDone { readonly confirmed: true; readonly removed: readonly string[]; readonly wouldRemove: readonly []; readonly detail: string; } /** The removal could not run at all. Nothing was touched. */ export interface GoogleRemovalRefused { readonly confirmed: false; readonly removed: readonly []; readonly wouldRemove: readonly []; readonly prompt: string; readonly refused: true; } export type GoogleRemovalResult = GoogleRemovalPlan | GoogleRemovalDone | GoogleRemovalRefused; export interface GoogleRemovalRequest { readonly items: readonly GoogleCredentialItem[]; /** * The owner's explicit yes, for this exact removal. * * Defaults to false and there is no way to default it to true. A caller that * wants a removal to happen has to pass this deliberately, which means the * decision is always written down at the call site. */ readonly confirmed?: boolean; /** Why the removal was proposed, quoted back in the prompt. */ readonly reason?: string; } export interface GoogleRemovalDeps { readonly secrets: GoogleSecretPort; readonly config: GoogleConfigPort; } /** * Remove stored Google credentials, but only with an explicit yes. * * Called without `confirmed`, this changes nothing and hands back the sentence * to put in front of the owner. Called with `confirmed: true`, it removes the * items that are actually present and reports each one by name. */ export declare function removeGoogleCredentials(deps: GoogleRemovalDeps, request: GoogleRemovalRequest): Promise; //# sourceMappingURL=credential-removal.d.ts.map