import { keyFor } from "./SecureStore.js";
/** Default brokered-credential cache lifetime: 5 minutes. */
export const DEFAULT_CREDENTIAL_TTL_MS = 5 * 60 * 1000;
/**
 * Caches resolved provider secrets for a bounded TTL so a single run does not
 * re-hit the underlying store repeatedly.
 *
 * Zeroization scope (deliberate, not a half-measure): the cache is the only
 * place in this process that holds a secret as a long-lived, reused field, so
 * it is the only place where wiping is both meaningful and achievable. The
 * secret is stored as a `Uint8Array` and `.fill(0)`-wiped on `release`,
 * `clear`, and TTL-expiry. The persistent stores (OS keyring, encrypted
 * fallback, plaintext-dev) are intentionally NOT byte-typed: each must transit
 * the secret through an unwipeable JS string (a JSON envelope, the keyring's
 * string API, or on-disk JSON) on every call, so byte-typing them would be
 * cosmetic — it would signal "zeroized" without wiping anything. The final
 * injection boundary (an `OPENAI_API_KEY` env string) is likewise an
 * unavoidable, GC-owned, short-lived string; see RuntimeCredentialInjector.
 */
export class InMemoryCredentialBroker {
    vault;
    #cache = new Map();
    constructor(vault) {
        this.vault = vault;
    }
    async acquire(ref, ttlMs = DEFAULT_CREDENTIAL_TTL_MS) {
        const now = new Date();
        const cacheKey = keyFor(ref);
        const cached = this.#cache.get(cacheKey);
        if (cached?.expiresAt && new Date(cached.expiresAt) > now) {
            return cached;
        }
        // Expired-but-present entry: wipe its bytes before replacing it.
        if (cached) {
            this.#evict(cacheKey);
        }
        // The vault contract returns the secret as a string; copy it into a
        // wipeable byte buffer for the cache. The transient string itself cannot
        // be wiped (immutable), but it is dropped here and only the byte copy is
        // retained for the TTL window.
        const secretString = await this.vault.resolve(ref);
        const credential = {
            ref,
            secret: new TextEncoder().encode(secretString),
            acquiredAt: now.toISOString(),
            expiresAt: new Date(now.getTime() + ttlMs).toISOString(),
        };
        this.#cache.set(cacheKey, credential);
        return credential;
    }
    release(ref) {
        this.#evict(keyFor(ref));
    }
    clear() {
        for (const cacheKey of [...this.#cache.keys()]) {
            this.#evict(cacheKey);
        }
    }
    #evict(cacheKey) {
        const credential = this.#cache.get(cacheKey);
        if (!credential)
            return;
        credential.secret.fill(0);
        this.#cache.delete(cacheKey);
    }
}
