import {
  ConnectionBrokenError
} from "./chunk-2N4UG4VD.js";

// runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts
var REFRESH_SAFETY_MS = 5 * 60 * 1e3;
var OAuth2RefreshStrategy = class {
  connectionReader;
  tokenWriter;
  fetchImpl;
  now;
  constructor(opts) {
    this.connectionReader = opts.connectionReader;
    this.tokenWriter = opts.tokenWriter;
    this.fetchImpl = opts.fetch ?? fetch;
    this.now = opts.now ?? Date.now;
  }
  async resolve(connectionId, opts = {}) {
    const connection = await this.connectionReader.findByIdDecrypted(connectionId);
    if (!connection) {
      throw new Error(`Connection ${connectionId} not found`);
    }
    if (connection.provider !== this.provider) {
      throw new Error(
        `${this.constructor.name} called for non-${this.provider} connection ${connectionId} (provider=${connection.provider})`
      );
    }
    const needsRefresh = opts.forceRefresh || this.isExpiring(connection.expiresAt) || !connection.accessToken;
    if (!needsRefresh) {
      return this.buildCredentials(connection.accessToken, connection);
    }
    if (!connection.refreshToken) {
      throw new ConnectionBrokenError(
        connectionId,
        "no_refresh_token",
        "Connection has no refresh token; user must reconnect"
      );
    }
    const { parsed, raw } = await this.executeRefresh(
      connectionId,
      connection.refreshToken
    );
    const newExpiresAt = new Date(
      this.now() + (parsed.expiresInSec ?? this.defaultExpiresInSec) * 1e3
    );
    await this.tokenWriter.persistRefresh({
      connectionId,
      accessToken: parsed.accessToken,
      refreshToken: parsed.refreshToken ?? void 0,
      expiresAt: newExpiresAt
    });
    return this.buildCredentials(parsed.accessToken, connection, raw);
  }
  async executeRefresh(connectionId, refreshToken) {
    const body = new URLSearchParams({
      grant_type: "refresh_token",
      refresh_token: refreshToken,
      ...this.refreshBodyExtras()
    });
    const response = await this.fetchImpl(this.tokenEndpoint(), {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: body.toString()
    });
    if (!response.ok) {
      const err = await safeJson(response);
      if (response.status === 400 && (err.error === "invalid_grant" || err.error === "invalid_token")) {
        throw new ConnectionBrokenError(
          connectionId,
          err.error ?? "invalid_grant",
          err.error_description ?? err.message ?? "refresh token rejected"
        );
      }
      throw new Error(
        `${this.provider} token refresh failed: ${response.status} ${err.error ?? ""} ${err.error_description ?? err.message ?? ""}`.trim()
      );
    }
    const raw = await response.json();
    return { parsed: this.parseRefreshResponse(raw), raw };
  }
  isExpiring(expiresAt) {
    if (!expiresAt) return true;
    return expiresAt.getTime() - this.now() < REFRESH_SAFETY_MS;
  }
};
async function safeJson(response) {
  try {
    return await response.clone().json();
  } catch {
    return {};
  }
}

export {
  OAuth2RefreshStrategy
};
//# sourceMappingURL=chunk-M6QLSLPO.js.map