/**
* Luminork API Client
*
* A client for interacting with the Luminork API service.
* Provides methods for authentication and interacting with various endpoints.
*/
// Default configuration values
const DEFAULT_CONFIG = {
baseUrl: 'http://localhost:5380',
timeout: 30000,
};
// Types for request/response handling
export interface ApiClientConfig {
baseUrl: string;
timeout: number;
authToken?: string;
}
export interface RequestOptions {
method: string;
headers?: Record<string, string>;
body?: unknown;
timeout?: number;
}
export interface ApiResponse<T> {
status: number;
statusText: string;
data: T;
headers: Headers;
}
export class ApiError extends Error {
status: number;
statusText: string;
data: unknown;
constructor(
message: string,
status: number,
statusText: string,
data: unknown,
) {
super(message);
this.name = 'ApiError';
this.status = status;
this.statusText = statusText;
this.data = data;
}
}
export class ConfigError extends Error {
constructor(message: string) {
super(message);
this.name = 'ConfigError';
}
}
/**
* LuminorkClient provides a simple client for interacting with the Luminork API
*/
export class LuminorkClient {
private config: ApiClientConfig;
constructor(config: Partial<ApiClientConfig> = {}) {
this.config = { ...DEFAULT_CONFIG, ...config };
}
/**
* Set the authentication token for API requests
*/
setAuthToken(token: string): void {
this.config.authToken = token;
}
/**
* Update client configuration
*/
updateConfig(config: Partial<ApiClientConfig>): void {
this.config = { ...this.config, ...config };
}
/**
* Get the base URL
*/
getBaseUrl(): string {
return this.config.baseUrl;
}
/**
* Perform a request to the API
*/
async request<T = unknown>(
path: string,
options: RequestOptions,
): Promise<ApiResponse<T>> {
const url = new URL(path, this.config.baseUrl);
const headers: Record<string, string> = {
'Content-Type': 'application/json',
Accept: 'application/json',
...options.headers,
};
// Add auth token if available
if (this.config.authToken) {
headers['Authorization'] = `Bearer ${this.config.authToken}`;
}
// Prepare request options
const requestInit: RequestInit = {
method: options.method,
headers,
};
// Add body for non-GET requests
if (options.method !== 'GET' && options.body) {
requestInit.body = JSON.stringify(options.body);
}
// Handle timeout
const timeout = options.timeout || this.config.timeout;
try {
// Create abort controller for timeout handling
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
requestInit.signal = controller.signal;
const response = await fetch(url.toString(), requestInit);
clearTimeout(timeoutId);
// Parse response
let data: T;
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
data = (await response.json()) as T;
} else {
data = (await response.text()) as unknown as T;
}
if (!response.ok) {
throw new ApiError(
`API request failed: ${response.status} ${response.statusText}`,
response.status,
response.statusText,
data,
);
}
return {
status: response.status,
statusText: response.statusText,
data,
headers: response.headers,
};
} catch (error) {
if (error instanceof ApiError) {
throw error;
}
if (error instanceof DOMException && error.name === 'AbortError') {
throw new ApiError(
`Request timeout after ${timeout}ms`,
408,
'Request Timeout',
{ error: 'timeout' },
);
}
throw new ApiError(
`API request failed: ${error instanceof Error ? error.message : String(error)}`,
500,
'Internal Error',
{ error: String(error) },
);
}
}
/**
* GET request helper
*/
async get<T = unknown>(
path: string,
options: Omit<RequestOptions, 'method' | 'body'> = {},
): Promise<ApiResponse<T>> {
return this.request<T>(path, { ...options, method: 'GET' });
}
/**
* POST request helper
*/
async post<T = unknown>(
path: string,
body: unknown = undefined,
options: Omit<RequestOptions, 'method' | 'body'> = {},
): Promise<ApiResponse<T>> {
return this.request<T>(path, { ...options, method: 'POST', body });
}
/**
* PUT request helper
*/
async put<T = unknown>(
path: string,
body: unknown = undefined,
options: Omit<RequestOptions, 'method' | 'body'> = {},
): Promise<ApiResponse<T>> {
return this.request<T>(path, { ...options, method: 'PUT', body });
}
/**
* DELETE request helper
*/
async delete<T = unknown>(
path: string,
options: Omit<RequestOptions, 'method' | 'body'> = {},
): Promise<ApiResponse<T>> {
return this.request<T>(path, { ...options, method: 'DELETE' });
}
/**
* Check system status
*/
async getSystemStatus(): Promise<
ApiResponse<{ 'What is this?': string; 'API Documentation': string }>
> {
return this.get<{ 'What is this?': string; 'API Documentation': string }>('/');
}
/**
* Get current user information
*/
async whoami(): Promise<
ApiResponse<{
userId: string;
userEmail: string;
workspaceId: string;
token: {
iat: number;
exp: number;
sub: string;
jti: string;
version: string;
userId: string;
workspaceId: string;
role: string;
};
}>
> {
return this.get<{
userId: string;
userEmail: string;
workspaceId: string;
token: {
iat: number;
exp: number;
sub: string;
jti: string;
version: string;
userId: string;
workspaceId: string;
role: string;
};
}>('/whoami');
}
}