UNPKG

@sudowealth/schwab-api

Version:

TypeScript client for Charles Schwab API with OAuth support, market data, trading functionality, and complete type safety

515 lines (514 loc) 18.7 kB
import { z } from 'zod'; /** * API-level error codes for different types of errors */ export declare enum ApiErrorCode { NETWORK = "NETWORK", RATE_LIMIT = "RATE_LIMIT", TIMEOUT = "TIMEOUT", UNAUTHORIZED = "UNAUTHORIZED", FORBIDDEN = "FORBIDDEN", INVALID_REQUEST = "INVALID_REQUEST", NOT_FOUND = "NOT_FOUND", SERVER_ERROR = "SERVER_ERROR", SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE", GATEWAY_ERROR = "GATEWAY_ERROR", UNKNOWN = "UNKNOWN" } /** * Authentication-specific error codes */ export declare enum AuthErrorCode { INVALID_CODE = "INVALID_CODE",// The authorization code is invalid UNAUTHORIZED = "UNAUTHORIZED",// The client credentials are invalid TOKEN_EXPIRED = "TOKEN_EXPIRED",// The refresh token has expired or is invalid (Schwab refresh tokens expire after 7 days) NETWORK = "NETWORK",// A network error occurred REFRESH_NEEDED = "REFRESH_NEEDED",// The access token has expired and needs to be refreshed UNKNOWN = "UNKNOWN",// An unknown error occurred TOKEN_PERSISTENCE_LOAD_FAILED = "TOKEN_PERSISTENCE_LOAD_FAILED",// Failed to load tokens from storage TOKEN_PERSISTENCE_SAVE_FAILED = "TOKEN_PERSISTENCE_SAVE_FAILED",// Failed to save tokens to storage TOKEN_VALIDATION_ERROR = "TOKEN_VALIDATION_ERROR",// Token validation failed PKCE_VERIFIER_MISSING = "PKCE_VERIFIER_MISSING",// PKCE code verifier is missing for token exchange TOKEN_ENDPOINT_CONFIG_ERROR = "TOKEN_ENDPOINT_CONFIG_ERROR",// Token endpoint configuration is missing or invalid INVALID_CONFIGURATION = "INVALID_CONFIGURATION" } /** * Common HTTP response headers that contain useful error metadata */ export interface ErrorResponseMetadata { /** * Suggested wait time in seconds before retrying (from Retry-After header) */ retryAfterSeconds?: number; /** * Timestamp indicating when to retry (from Retry-After header with HTTP date) */ retryAfterDate?: Date; /** * Rate limit information (from X-RateLimit-* headers) */ rateLimit?: { /** * Current rate limit in requests per time window */ limit?: number; /** * Remaining requests allowed in current time window */ remaining?: number; /** * When the rate limit window resets (Unix timestamp) */ reset?: number; }; /** * Request ID for tracing and debugging (from X-Request-ID header) * This is a unique identifier for the request that can be used * to trace the request through the API system for debugging */ requestId?: string; /** * The endpoint path that was requested * This helps identify which API endpoint generated the error */ endpointPath?: string; /** * HTTP method used for the request (GET, POST, etc.) */ requestMethod?: string; /** * Timestamp when the error occurred * Useful for correlating errors with logs */ timestamp?: Date; /** * Raw headers from the response for custom processing */ headers?: Record<string, string>; } export declare const ErrorSourceSchema: z.ZodObject<{ pointer: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; parameter: z.ZodOptional<z.ZodString>; header: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; }, { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; }>; export declare const ErrorSchema: z.ZodObject<{ id: z.ZodString; status: z.ZodEnum<["400", "401", "403", "404", "429", "500", "502", "503", "504"]>; title: z.ZodString; detail: z.ZodString; source: z.ZodOptional<z.ZodObject<{ pointer: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; parameter: z.ZodOptional<z.ZodString>; header: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; }, { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { status: "400" | "401" | "403" | "404" | "429" | "500" | "502" | "503" | "504"; id: string; title: string; detail: string; source?: { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; } | undefined; }, { status: "400" | "401" | "403" | "404" | "429" | "500" | "502" | "503" | "504"; id: string; title: string; detail: string; source?: { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; } | undefined; }>; export declare const ErrorResponseSchema: z.ZodObject<{ errors: z.ZodArray<z.ZodObject<{ id: z.ZodString; status: z.ZodEnum<["400", "401", "403", "404", "429", "500", "502", "503", "504"]>; title: z.ZodString; detail: z.ZodString; source: z.ZodOptional<z.ZodObject<{ pointer: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; parameter: z.ZodOptional<z.ZodString>; header: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; }, { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { status: "400" | "401" | "403" | "404" | "429" | "500" | "502" | "503" | "504"; id: string; title: string; detail: string; source?: { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; } | undefined; }, { status: "400" | "401" | "403" | "404" | "429" | "500" | "502" | "503" | "504"; id: string; title: string; detail: string; source?: { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; } | undefined; }>, "many">; }, "strip", z.ZodTypeAny, { errors: { status: "400" | "401" | "403" | "404" | "429" | "500" | "502" | "503" | "504"; id: string; title: string; detail: string; source?: { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; } | undefined; }[]; }, { errors: { status: "400" | "401" | "403" | "404" | "429" | "500" | "502" | "503" | "504"; id: string; title: string; detail: string; source?: { pointer?: string[] | undefined; parameter?: string | undefined; header?: string | undefined; } | undefined; }[]; }>; export type ErrorResponseSchema = z.infer<typeof ErrorResponseSchema>; export type ErrorObject = z.infer<typeof ErrorSchema>; /** * Base class for all Schwab API errors */ export declare class SchwabError extends Error { /** * Original error object if this error wraps another */ originalError?: unknown; constructor(message: string); /** * Determines if this error is retryable * Base implementation returns false - subclasses should override as needed * @returns boolean indicating if the request can be retried */ isRetryable(): boolean; } /** * Type guard to check if an error is an instance of SchwabError */ export declare function isSchwabError(e: unknown): e is SchwabError; /** * Error representing authentication failures */ export declare class SchwabAuthError extends SchwabError { /** * HTTP status code if available */ status?: number; /** * Auth-specific error code */ code: AuthErrorCode; /** * Response body or error details */ body?: unknown; constructor(code: AuthErrorCode, message?: string, status?: number, body?: unknown); /** * Determines if this authentication error is retryable * @returns boolean indicating if the auth error can be retried */ isRetryable(): boolean; } /** * Error representing failures from the API */ export declare class SchwabApiError extends SchwabError { /** * HTTP status code from the response */ status: number; /** * Response body or error details */ body?: unknown; /** * API error code */ code: ApiErrorCode; /** * Parsed error response conforming to the ErrorResponseSchema, if available */ parsedError?: ErrorResponseSchema; /** * Metadata extracted from the response headers */ metadata?: ErrorResponseMetadata; constructor(status: number, body?: unknown, message?: string, code?: ApiErrorCode, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); /** * Get a formatted summary of all error details */ getFormattedDetails(): string; /** * Checks if this error has retry information */ hasRetryInfo(): boolean; /** * Get the suggested retry delay in milliseconds * @returns Number of milliseconds to wait or null if no retry info */ getRetryDelayMs(): number | null; /** * Get the request ID for debugging purposes * @returns Request ID if available, undefined otherwise */ getRequestId(): string | undefined; /** * Get a debugging context string with request details * @returns A string with request details for debugging */ getDebugContext(): string; /** * Determines if this error is retryable based on status code and metadata * @param options Optional parameters to customize retry behavior * @returns boolean indicating if the request can be retried */ isRetryable(options?: { ignoreRetryAfter?: boolean; }): boolean; } /** * Type guard to check if an error is an instance of SchwabApiError */ export declare function isSchwabApiError(e: unknown): e is SchwabApiError; /** * Retryable API errors (network, server, gateway) */ export declare class RetryableApiError extends SchwabApiError { constructor(status: number, body?: unknown, message?: string, code?: ApiErrorCode, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); /** * All errors of this class are retryable by definition */ isRetryable(): boolean; } /** * 429 - Rate Limit Exceeded, with specialized methods for handling rate limits */ export declare class SchwabRateLimitError extends RetryableApiError { constructor(body?: unknown, message?: string, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); /** * Get the remaining requests allowed in the current window */ getRemainingRequests(): number | undefined; /** * Get the time when the rate limit will reset (in milliseconds) */ getResetTimeMs(): number | undefined; } /** * Client-side errors represent 4xx responses that are non-retryable */ export declare class ClientApiError extends SchwabApiError { constructor(status: number, body?: unknown, message?: string, code?: ApiErrorCode, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); /** * Client errors are generally not retryable */ isRetryable(): boolean; } /** * 401 - Unauthorized - Specifically maintained as a separate class due to * its importance in authentication flows */ export declare class SchwabAuthorizationError extends ClientApiError { constructor(body?: unknown, message?: string, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); } /** * Enum to indicate the specific type of server error */ export declare enum ServerErrorReason { /** * Generic internal server error (500) */ INTERNAL_ERROR = "INTERNAL_ERROR", /** * Bad gateway error (502) - upstream service returned invalid response */ BAD_GATEWAY = "BAD_GATEWAY", /** * Service unavailable (503) - server temporarily unavailable */ SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE", /** * Gateway timeout (504) - upstream service timed out */ GATEWAY_TIMEOUT = "GATEWAY_TIMEOUT" } /** * Unified server-side error class for all 5xx errors (500, 502, 503, 504) * Consolidates the functionality of SchwabServiceUnavailableError and SchwabGatewayError */ export declare class SchwabServerError extends RetryableApiError { /** * Specific reason for the server error */ reason: ServerErrorReason; constructor(status?: number, body?: unknown, message?: string, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); /** * Checks if this is a service unavailable error (503) */ isServiceUnavailable(): boolean; /** * Checks if this is a gateway error (502 or 504) */ isGatewayError(): boolean; } /** * Base class for communication-related errors (network, timeout) * This provides a common type for errors related to communication issues * * Note: This class inherits all the functionality of RetryableApiError including: * - isRetryable(): Always returns true * - All status, code, and metadata handling methods * * Use isCommunicationError() type guard to check if an error is related to network or timeout */ export declare class RetryableCommunicationError extends RetryableApiError { /** * Cause of the communication error */ cause: 'network' | 'timeout'; constructor(status: number, code: ApiErrorCode, cause: 'network' | 'timeout', body?: unknown, message?: string, metadata?: ErrorResponseMetadata); /** * Determines if this is a network error */ isNetworkError(): boolean; /** * Determines if this is a timeout error */ isTimeoutError(): boolean; } /** * Network error (no status code) */ export declare class SchwabNetworkError extends RetryableCommunicationError { constructor(body?: unknown, message?: string, metadata?: ErrorResponseMetadata); } /** * Timeout error */ export declare class SchwabTimeoutError extends RetryableCommunicationError { constructor(body?: unknown, message?: string, metadata?: ErrorResponseMetadata); } /** * Type guards for different error types * These functions allow for narrowing error types in catch blocks */ export declare function isRateLimitError(e: unknown): e is SchwabRateLimitError; export declare function isServerError(e: unknown): e is SchwabServerError; /** * Check if an error is a client error, optionally with a specific status code */ export declare function isClientError(e: unknown, status?: number): e is ClientApiError; /** * Check if an error is a server error with a specific status code */ export declare function isServerErrorWithStatus(e: unknown, status: number): e is SchwabServerError; /** * Check if an error is a server error with one of the provided status codes */ export declare function isServerErrorWithAnyStatus(e: unknown, statuses: number[]): e is SchwabServerError; /** * Check if an error is a server error with a specific reason */ export declare function isServerErrorWithReason(e: unknown, reason: ServerErrorReason): e is SchwabServerError; export declare function isAuthError(e: unknown): e is SchwabAuthError; /** * Type guard to check if an error is a communication error (network or timeout) */ export declare function isCommunicationError(e: unknown): e is RetryableCommunicationError; /** * Type guard to check if an error is a network error, either through direct instance * or through the RetryableCommunicationError with cause='network' */ export declare function isNetworkError(e: unknown): e is SchwabNetworkError | RetryableCommunicationError; /** * Type guard to check if an error is a timeout error, either through direct instance * or through the RetryableCommunicationError with cause='timeout' */ export declare function isTimeoutError(e: unknown): e is SchwabTimeoutError | RetryableCommunicationError; export declare function isUnauthorizedError(e: unknown): e is SchwabAuthorizationError; /** * Check for errors with specific status codes without requiring specific classes */ export declare function hasForbiddenStatus(e: unknown): boolean; export declare function hasNotFoundStatus(e: unknown): boolean; export declare function hasInvalidRequestStatus(e: unknown): boolean; export declare function isRetryableError(e: unknown): boolean; export declare function isServerSideError(e: unknown): e is RetryableApiError; export declare function getErrorStatusCode(e: unknown): number; export declare function getErrorCategory(e: unknown): string; /** * Extract error metadata from response headers * @param response The fetch Response object * @param request Optional original request for additional context * @returns Error metadata extracted from headers */ export declare function extractErrorMetadata(response: Response, request?: Request): ErrorResponseMetadata; /** * Parses and validates an error response against the ErrorResponseSchema * @param body Response body to parse * @returns Validated ErrorResponse object or undefined if validation fails */ export declare function parseErrorResponse(body: unknown): ErrorResponseSchema | undefined; /** * Factory function to create the appropriate error type based on status code and metadata * This is the RECOMMENDED way to create errors in the codebase to ensure consistency */ export declare function createSchwabApiError(status: number, body?: unknown, message?: string, metadata?: ErrorResponseMetadata): SchwabApiError; /** * 400 - Bad Request - Invalid request, missing required parameters, etc. * Used by create-api-client */ export declare class SchwabInvalidRequestError extends ClientApiError { constructor(body?: unknown, message?: string, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); } /** * 404 - Not Found Error * Used by create-api-client */ export declare class SchwabNotFoundError extends ClientApiError { constructor(body?: unknown, message?: string, parsedError?: ErrorResponseSchema, metadata?: ErrorResponseMetadata); } /** * Attempts to parse a response error and create the appropriate error * This provides consistent error handling across various parts of the SDK */ export declare function handleApiError(error: unknown, context?: string): never;