import type { z } from "zod";
import type { TraceMetadata } from "../registry.js";
export type { TraceMetadata };
export const REST_API_RESPONSE_TYPES = ["binary", "json", "text"] as const;
export type RestApiResponseType = (typeof REST_API_RESPONSE_TYPES)[number];
export interface ApiRequestOptions
{
/**
* HTTP method for the request.
*/
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
/**
* API endpoint path (e.g., "/users", "/repos/owner/repo")
*/
path: string;
/**
* Optional request body (for POST, PUT, PATCH requests).
* Type is inferred from bodySchema parameter.
*/
body?: TBody;
/**
* Optional query parameters
*/
params?: Record;
/**
* Optional HTTP headers
*/
headers?: Record;
/**
* How the response body should be decoded. Defaults to `"json"`.
*
* Use `"text"` for endpoints that return non-JSON payloads such as
* XML. Use `"binary"` for PDFs and other byte payloads. When
* requesting a text or binary response, omit the response schema (or
* use one matching the decoded value, e.g. `z.string()` or
* `z.instanceof(Uint8Array)`).
*/
responseType?: RestApiResponseType;
}
export interface ApiRequestSchema {
/**
* Optional Zod schema for request body validation.
* When omitted, `options.body` is sent without validation.
*/
body?: z.ZodSchema;
/**
* Optional Zod schema for response validation.
* If not provided, response is returned without validation.
*/
response?: z.ZodSchema;
}
/**
* Interface for integration clients that support generic API requests.
*
* Providing a response schema gives type-safe, validated results. Omitting
* it returns the decoded response: `Uint8Array` for `responseType: "binary"`,
* or `unknown` for `responseType: "text"`. JSON still requires a schema.
* Object schemas only make sense for JSON-shaped results.
*/
export interface SupportsApiRequest {
/**
* Execute a generic API request with type-safe validation.
*
* @param options - Request configuration including method, path, params, and body
* @param schema - Zod schemas for request body and response validation
* @param metadata - Optional trace metadata for observability (label, description)
* @returns Validated response data
*
* @example
* ```typescript
* // Declare in api(): integrations: { slack: slack(INTEGRATION_ID) }
* // In run(), access via: ctx.integrations.
* const result = await ctx.integrations.slack.apiRequest(
* {
* method: 'POST',
* path: '/chat.postMessage',
* body: {
* channel: '#alerts',
* text: 'Hello!',
* },
* },
* {
* body: z.object({
* channel: z.string(),
* text: z.string(),
* }),
* response: ResponseSchema,
* },
* { label: 'slack.postMessage', description: 'Post a message to #alerts' }
* );
* // result is typed as { ts: string; channel: string }
* ```
*/
apiRequest(
options: ApiRequestOptions & {
responseType?: Exclude;
},
schema: ApiRequestSchema & {
response: z.ZodSchema;
},
metadata?: TraceMetadata,
): Promise;
/**
* Execute a generic API request that returns binary data.
*
* @param options - Request configuration; `responseType` must be "binary"
* @param schema - Optional Zod schemas for request body and decoded response validation
* @param metadata - Optional trace metadata for observability (label, description)
* @returns The decoded body as a `Uint8Array`
*
* @example
* ```typescript
* const pdf = await ctx.integrations.legacyApi.apiRequest({
* method: 'GET',
* path: '/file.pdf',
* responseType: 'binary',
* });
* // pdf is a Uint8Array
* ```
*/
apiRequest(
options: ApiRequestOptions & {
responseType: "binary";
},
schema?: ApiRequestSchema,
metadata?: TraceMetadata,
): Promise;
/**
* Execute a generic API request without response validation.
*
* This overload requires an explicit `responseType: "text"` - JSON
* responses must always be consumed through the schema overload above,
* so "unvalidated JSON" is unrepresentable. The decoded text
* response is returned as-is, typed `unknown`.
*
* Note: request-body validation is opt-in - it runs only when
* `schema.body` is provided. Omitting `schema` sends `options.body`
* without validation.
*
* @param options - Request configuration; `responseType` must be "text"
* @param schema - Optional Zod schema for request body validation
* @param metadata - Optional trace metadata for observability (label, description)
* @returns The raw response from the integration
*
* @example
* ```typescript
* const xml = await ctx.integrations.legacyApi.apiRequest({
* method: 'GET',
* path: '/report.xml',
* responseType: 'text',
* });
* // xml is the raw XML string
* ```
*/
apiRequest(
options: ApiRequestOptions & {
responseType: "text";
},
schema?: ApiRequestSchema,
metadata?: TraceMetadata,
): Promise;
}