@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
TypeScript
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;