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; }