/** * Minimal HTTP client for the BFFless backend API (Phase 1 live `pull`/`push`/`diff`). * * Base-URL resolution: `--api-url` flag > `BFFLESS_API_URL` env > `apiUrl` in the nearest * `.bffless/config.json`. * * API-key resolution: `--api-key` flag > `BFFLESS_API_KEY` env > the `bffless login` * credential store (~/.config/bffless/credentials.json, keyed by normalized apiUrl) — * and NOTHING else. The key is deliberately never read from `.bffless/config.json`: * that file is meant to be committed to the repo (it carries `apiUrl`/`project`/ * `ruleSets` for the whole team), so supporting a key there would invite committing * credentials. Keys stay in flags/env/the out-of-repo store only. * * Auth is the backend's `ApiKeyGuard`, which reads the `X-API-Key` request header. * * The "how to fix it" half of every config/auth error comes from `deps.remediation`, so an * embedder that has no flags (the deploy-proxy-rules Action) can name its own knobs instead * of the CLI's — see api/remediation.ts. * * `fetch` is injectable for tests. No retries — a CI drift check or deploy push should fail * fast and loudly rather than mask a flaky endpoint. */ import { type BfflessConfig } from '../config.js'; import { type Remediation } from './remediation.js'; /** Error thrown for any failed request; `status` is 0 for network-level failures. */ export declare class ApiError extends Error { readonly status: number; readonly url: string; constructor(message: string, status: number, url: string); } export type FetchLike = (url: string, init?: RequestInit) => Promise; export interface ClientFlags { apiUrl?: string; apiKey?: string; } export interface ClientDeps { fetchImpl?: FetchLike; env?: Record; /** Pre-loaded config (tests / callers that already ran `findConfig`); when absent the * nearest `.bffless/config.json` above `cwd` is used. */ config?: BfflessConfig | null; /** Override the credential-store file path (tests). Default: `credentialsPath()`. */ credentialsFile?: string; /** Override the "how to fix it" wording of config/auth errors. Unset fields keep the * CLI's default phrasing — an embedder with no flags should override the ones its user * can actually act on. */ remediation?: Partial; } export declare class ApiClient { private readonly base; private readonly apiKey; private readonly fetchImpl; private readonly remediation; constructor(init: { apiUrl: string; apiKey: string; fetchImpl?: FetchLike; remediation?: Partial; }); /** Absolute URL for an API path (path must start with `/`). */ url(apiPath: string): string; /** * Perform a JSON request. `lookup` describes what a 404 means for this call so the error * says what was looked up (e.g. `rule set "studio" export`), not just the URL. */ private request; get(apiPath: string, lookup?: string): Promise; put(apiPath: string, body: unknown, lookup?: string): Promise; post(apiPath: string, body: unknown, lookup?: string): Promise; } /** * Build a client from CLI flags + env + config, applying the documented precedence. * Throws a plain Error (caller prints it, no stack) naming every source that was tried. */ export declare function createClient(flags: ClientFlags, cwd: string, deps?: ClientDeps): ApiClient;