@follow-app/client-sdk
Version:
TypeScript client SDK for Follow RSS Server API
172 lines (148 loc) • 4.54 kB
text/typescript
import type { ModuleAPIs } from "../modules/registry"
import { moduleRegistry } from "../modules/registry"
import type { ClientConfig, RequestOptions } from "../types"
import { HttpClient } from "./base"
import type {
ErrorInterceptor,
RequestInterceptor,
ResponseInterceptor,
} from "./interceptors"
import { commonInterceptors } from "./interceptors"
import { createAPIProxy } from "./proxy"
/**
* Configuration options for the Follow Client
*/
export interface FollowClientConfig extends Partial<ClientConfig> {
// Additional SDK-specific configuration
enableDefaultInterceptors?: boolean
authToken?: string
}
export class FollowClient {
private httpClient: HttpClient
public api: ModuleAPIs = {} as ModuleAPIs
constructor(config: FollowClientConfig = {}) {
// Initialize HTTP client with proper defaults
this.httpClient = new HttpClient({
baseURL: config.baseURL || "https://api.folo.is",
timeout: config.timeout || 30000,
headers: config.headers || {},
credentials: config.credentials || "include",
fetch: config.fetch || globalThis.fetch.bind(globalThis),
})
// Initialize API route groups
this.initializeRoutes()
// Setup default interceptors if enabled
if (config.enableDefaultInterceptors) {
this.setupDefaultInterceptors()
}
// Set auth token if provided
if (config.authToken) {
this.setAuthToken(config.authToken)
}
}
/**
* Initialize API route groups with proper typing
*/
private initializeRoutes(): void {
// Automatically initialize all modules from registry
for (const [moduleName, moduleDefinition] of Object.entries(
moduleRegistry,
)) {
(this.api as any)[moduleName] = createAPIProxy(this.httpClient, moduleDefinition)
}
}
/**
* Setup default interceptors
*/
private setupDefaultInterceptors(): void {
const interceptors = this.httpClient.getInterceptors()
// Add default logging interceptor
interceptors.addRequestInterceptor(
commonInterceptors.logRequests({ log: console.info }),
)
interceptors.addResponseInterceptor(
commonInterceptors.logResponses({ log: console.info }),
)
}
/**
* Set authentication token for API requests
*/
setAuthToken(token: string): void {
this.httpClient.setHeaders({ Authorization: `Bearer ${token}` })
}
/**
* Remove authentication token
*/
removeAuthToken(): void {
const currentHeaders = this.httpClient.getConfig().headers
const { Authorization, ...newHeaders } = currentHeaders
this.httpClient.setHeaders(newHeaders)
}
/**
* Set custom headers for API requests
*/
setHeaders(headers: Record<string, string>): void {
this.httpClient.setHeaders(headers)
}
/**
* Set custom fetch instance
*/
setFetch(fetchInstance: typeof fetch): void {
this.httpClient.setFetch(fetchInstance)
}
/**
* Update client configuration
*/
updateConfig(config: Partial<FollowClientConfig>): void {
this.httpClient.setConfig(config)
}
/**
* Get current configuration (readonly)
*/
getConfig(): Readonly<Required<ClientConfig>> {
return this.httpClient.getConfig()
}
/**
* Make a custom HTTP request using the underlying HTTP client
* This allows direct access to the HTTP client for custom requests
*/
async request<T>(path: string, options?: RequestOptions): Promise<T> {
return this.httpClient.request<T>(path, options)
}
/**
* Batch multiple requests
*/
async batch<T extends readonly any[]>(
requests: readonly [...T],
): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
return Promise.all(requests) as any
}
/**
* Create a new instance with different configuration
*/
clone(config?: Partial<FollowClientConfig>): FollowClient {
const currentConfig = this.getConfig()
return new FollowClient({
...currentConfig,
...config,
})
}
/**
* Add request interceptor
*/
addRequestInterceptor(interceptor: RequestInterceptor): () => void {
return this.httpClient.getInterceptors().addRequestInterceptor(interceptor)
}
/**
* Add response interceptor
*/
addResponseInterceptor(interceptor: ResponseInterceptor): () => void {
return this.httpClient.getInterceptors().addResponseInterceptor(interceptor)
}
/**
* Add error interceptor
*/
addErrorInterceptor(interceptor: ErrorInterceptor): () => void {
return this.httpClient.getInterceptors().addErrorInterceptor(interceptor)
}
}