/** * The one place every Substack request goes through. * * Substack publishes no REST API. These are the same authenticated JSON * endpoints the web app calls, signed with a `connect.sid` session cookie, and * they can change without notice. Routing everything through one client means a * change upstream is fixed in one file. * * What this adds over a bare fetch: * - a real deadline. Node applies no request timeout, only a 10s connect * timeout, so a host that accepts the connection and then goes quiet would * otherwise hang a tool call forever. * - retries with backoff on 429 and 5xx, which are the two failures that * actually resolve by waiting. * - a floor on request spacing, so a model looping over 200 posts does not * get the account rate limited. * - a browser User-Agent and Referer, because publications on custom domains * sit behind Cloudflare and it blocks unrecognized clients with 1010. */ import type { Config, Credentials } from "../config.js"; export type FetchOptions = { method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; body?: unknown; /** Send the session cookie. Off for public endpoints. */ authenticated?: boolean; /** Override the credentials for this one call. */ creds?: Credentials; /** Accept a non-JSON response body without treating it as an error. */ raw?: boolean; /** Extra headers, merged last. */ headers?: Record; }; export declare class SubstackClient { private readonly config; private lastRequestAt; private queue; constructor(config: Config); /** `https://example.substack.com` for a set of credentials. */ baseUrl(creds: Credentials): string; /** `https://example.substack.com/api/v1` */ apiUrl(creds: Credentials): string; private headers; /** * Space requests out. Serialised through a promise chain so concurrent tool * calls queue rather than all firing at once. */ private throttle; request(url: string, opts?: FetchOptions): Promise; private attempt; /** GET that returns `null` instead of throwing on 404. */ tryRequest(url: string, opts?: FetchOptions): Promise; } /** * Strip anything sensitive out of a URL before it goes into an error message. * Error text reaches the model and often the user's screen. */ export declare function redactUrl(url: string): string;