velora-node-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VeloraClient = void 0;
4
+ const CredentialManager_1 = require("../auth/CredentialManager");
5
+ const HttpClient_1 = require("../http/HttpClient");
6
+ const WebSocketClient_1 = require("../ws/WebSocketClient");
7
+ const errors_1 = require("../types/errors");
8
+ const events_1 = require("events");
9
+ class VeloraClient extends events_1.EventEmitter {
10
+ constructor(config) {
11
+ super();
12
+ this.wsClient = null;
13
+ const host = config?.host || '127.0.0.1';
14
+ const port = config?.port || 39031;
15
+ this.baseUrl = `http://${host}:${port}`;
16
+ this.wsBaseUrl = `ws://${host}:${port}`;
17
+ this.credentialManager = new CredentialManager_1.CredentialManager(this.baseUrl, config?.token);
18
+ this.httpClient = new HttpClient_1.HttpClient(this.baseUrl, this.credentialManager, config?.httpOptions);
19
+ if (config?.token) {
20
+ if (config.requestedPermissions) {
21
+ this.credentialManager.setToken(config.token, config.requestedPermissions);
22
+ }
23
+ }
24
+ }
25
+ isAuthenticated() {
26
+ return this.credentialManager.isAuthenticated();
27
+ }
28
+ async registerApp(appConfig) {
29
+ return this.credentialManager.register(appConfig);
30
+ }
31
+ async requestAccessToken(requestId, maxWaitMs) {
32
+ return this.credentialManager.requestAccessToken(requestId, maxWaitMs);
33
+ }
34
+ async getHealth() {
35
+ return this.httpClient.getHealth();
36
+ }
37
+ async getCurrentTrack() {
38
+ return this.httpClient.getCurrentTrack();
39
+ }
40
+ async getPlaybackState() {
41
+ return this.httpClient.getPlaybackState();
42
+ }
43
+ async getPlaybackQueue() {
44
+ return this.httpClient.getPlaybackQueue();
45
+ }
46
+ async getUserPublic() {
47
+ return this.httpClient.getUserPublic();
48
+ }
49
+ async play(trackId) {
50
+ return this.httpClient.play(trackId);
51
+ }
52
+ async pause() {
53
+ return this.httpClient.pause();
54
+ }
55
+ async next() {
56
+ return this.httpClient.next();
57
+ }
58
+ async seek(position) {
59
+ return this.httpClient.seek(position);
60
+ }
61
+ async toggle() {
62
+ return this.httpClient.toggle();
63
+ }
64
+ async connectWebSocket() {
65
+ if (!this.credentialManager.isAuthenticated()) {
66
+ throw new errors_1.AuthError('Cannot connect WebSocket without authentication');
67
+ }
68
+ if (this.wsClient === null) {
69
+ const wsOptions = {};
70
+ this.wsClient = new WebSocketClient_1.WebSocketClient(this.wsBaseUrl, this.credentialManager, 1000, 60000);
71
+ this.wsClient.on('connected', () => this.emit('ws:connected'));
72
+ this.wsClient.on('disconnected', () => this.emit('ws:disconnected'));
73
+ this.wsClient.on('connecting', () => this.emit('ws:connecting'));
74
+ this.wsClient.on('error', (error) => this.emit('ws:error', error));
75
+ this.wsClient.on('track_changed', (data) => this.emit('track_changed', data));
76
+ this.wsClient.on('playback_state_changed', (data) => this.emit('playback_state_changed', data));
77
+ this.wsClient.on('message', (data) => this.emit('ws:message', data));
78
+ }
79
+ await this.wsClient.connect();
80
+ }
81
+ async disconnectWebSocket() {
82
+ if (this.wsClient) {
83
+ this.wsClient.disconnect();
84
+ this.wsClient = null;
85
+ }
86
+ }
87
+ isWSConnected() {
88
+ return this.wsClient?.isWSConnected() || false;
89
+ }
90
+ async connect() {
91
+ try {
92
+ await this.getHealth();
93
+ }
94
+ catch (error) {
95
+ throw new errors_1.AuthError('Cannot reach Velora API');
96
+ }
97
+ if (this.credentialManager.isAuthenticated()) {
98
+ try {
99
+ await this.connectWebSocket();
100
+ }
101
+ catch (error) {
102
+ console.warn('WebSocket connection failed, but HTTP client is ready');
103
+ }
104
+ }
105
+ }
106
+ async disconnect() {
107
+ if (this.wsClient) {
108
+ await this.disconnectWebSocket();
109
+ }
110
+ }
111
+ clearCredentials() {
112
+ this.credentialManager.clear();
113
+ }
114
+ getToken() {
115
+ return this.credentialManager.getToken();
116
+ }
117
+ setToken(token, permissions) {
118
+ this.credentialManager.setToken(token, permissions);
119
+ }
120
+ getPermissions() {
121
+ return this.credentialManager.getPermissions();
122
+ }
123
+ }
124
+ exports.VeloraClient = VeloraClient;
@@ -0,0 +1 @@
1
+ export { VeloraClient } from './VeloraClient';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VeloraClient = void 0;
4
+ var VeloraClient_1 = require("./VeloraClient");
5
+ Object.defineProperty(exports, "VeloraClient", { enumerable: true, get: function () { return VeloraClient_1.VeloraClient; } });
@@ -0,0 +1,22 @@
1
+ import { CredentialManager } from '../auth/CredentialManager';
2
+ import { HealthResponse, CurrentTrackResponse, PlaybackStateResponse, PlaybackQueueResponse, UserPublicResponse, PlayResponse, PauseResponse, NextResponse, SeekResponse, ToggleResponse } from '../types/api';
3
+ import { HttpRequestOptions } from '../types/client';
4
+ export declare class HttpClient {
5
+ private readonly baseUrl;
6
+ private readonly credentialManager;
7
+ private readonly timeout;
8
+ private readonly retryManager;
9
+ constructor(baseUrl: string, credentialManager: CredentialManager, options?: HttpRequestOptions);
10
+ getHealth(): Promise<HealthResponse>;
11
+ getCurrentTrack(): Promise<CurrentTrackResponse>;
12
+ getPlaybackState(): Promise<PlaybackStateResponse>;
13
+ getPlaybackQueue(): Promise<PlaybackQueueResponse>;
14
+ getUserPublic(): Promise<UserPublicResponse>;
15
+ play(trackId?: string): Promise<PlayResponse>;
16
+ pause(): Promise<PauseResponse>;
17
+ next(): Promise<NextResponse>;
18
+ seek(position: number): Promise<SeekResponse>;
19
+ toggle(): Promise<ToggleResponse>;
20
+ private requirePermission;
21
+ private request;
22
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpClient = void 0;
4
+ const errors_1 = require("../types/errors");
5
+ const RetryManager_1 = require("./RetryManager");
6
+ class HttpClient {
7
+ constructor(baseUrl, credentialManager, options) {
8
+ this.baseUrl = baseUrl;
9
+ this.credentialManager = credentialManager;
10
+ this.timeout = options?.timeout || 30000;
11
+ this.retryManager = new RetryManager_1.RetryManager(1000, 32000, options?.maxRetries ?? 3);
12
+ }
13
+ async getHealth() {
14
+ return this.request('GET', '/health');
15
+ }
16
+ async getCurrentTrack() {
17
+ this.requirePermission('read');
18
+ return this.request('GET', '/current-track');
19
+ }
20
+ async getPlaybackState() {
21
+ this.requirePermission('read');
22
+ return this.request('GET', '/playback-state');
23
+ }
24
+ async getPlaybackQueue() {
25
+ this.requirePermission('read');
26
+ return this.request('GET', '/playback-queue');
27
+ }
28
+ async getUserPublic() {
29
+ this.requirePermission('read');
30
+ return this.request('GET', '/user/public');
31
+ }
32
+ async play(trackId) {
33
+ this.requirePermission('write');
34
+ const body = {};
35
+ if (trackId) {
36
+ body.track_id = trackId;
37
+ }
38
+ return this.request('POST', '/play', body);
39
+ }
40
+ async pause() {
41
+ this.requirePermission('write');
42
+ return this.request('POST', '/pause');
43
+ }
44
+ async next() {
45
+ this.requirePermission('write');
46
+ return this.request('POST', '/next');
47
+ }
48
+ async seek(position) {
49
+ this.requirePermission('write');
50
+ if (typeof position !== 'number' || position < 0) {
51
+ throw new errors_1.ValidationError('position must be a non-negative number (milliseconds)', 'position');
52
+ }
53
+ const body = { position };
54
+ return this.request('POST', '/seek', body);
55
+ }
56
+ async toggle() {
57
+ this.requirePermission('write');
58
+ return this.request('POST', '/toggle');
59
+ }
60
+ requirePermission(permission) {
61
+ if (!this.credentialManager.hasPermission(permission)) {
62
+ throw new errors_1.AuthError(`Missing ${permission} permission`);
63
+ }
64
+ }
65
+ async request(method, path, body) {
66
+ const makeRequest = async () => {
67
+ const url = `${this.baseUrl}${path}`;
68
+ const headers = {
69
+ 'Content-Type': 'application/json',
70
+ };
71
+ const authHeader = this.credentialManager.getAuthHeader();
72
+ if (authHeader) {
73
+ headers['Authorization'] = authHeader;
74
+ }
75
+ const controller = new AbortController();
76
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
77
+ try {
78
+ const response = await fetch(url, {
79
+ method,
80
+ headers,
81
+ body: body ? JSON.stringify(body) : undefined,
82
+ signal: controller.signal,
83
+ });
84
+ clearTimeout(timeoutId);
85
+ if (!response.ok) {
86
+ let errorMsg = `HTTP ${response.status}`;
87
+ try {
88
+ const errorBody = await response.json();
89
+ errorMsg = errorBody.error || errorMsg;
90
+ }
91
+ catch { }
92
+ if (response.status === 401) {
93
+ throw new errors_1.AuthError(errorMsg, response.status);
94
+ }
95
+ throw new errors_1.RequestError(errorMsg, response.status);
96
+ }
97
+ return await response.json();
98
+ }
99
+ catch (error) {
100
+ clearTimeout(timeoutId);
101
+ if (error instanceof errors_1.RequestError || error instanceof errors_1.AuthError) {
102
+ throw error;
103
+ }
104
+ if (error instanceof Error && error.name === 'AbortError') {
105
+ throw new errors_1.TimeoutError(`Request timeout after ${this.timeout}ms`, this.timeout);
106
+ }
107
+ throw (0, errors_1.classifyError)(error);
108
+ }
109
+ };
110
+ return this.retryManager.executeWithRetry(makeRequest, (attempt, backoffMs) => {
111
+ void backoffMs;
112
+ void attempt;
113
+ });
114
+ }
115
+ }
116
+ exports.HttpClient = HttpClient;
@@ -0,0 +1,9 @@
1
+ export declare class RetryManager {
2
+ private readonly initialBackoffMs;
3
+ private readonly maxBackoffMs;
4
+ private readonly maxRetries;
5
+ constructor(initialBackoffMs?: number, maxBackoffMs?: number, maxRetries?: number);
6
+ shouldRetry(statusCode: number): boolean;
7
+ getBackoffMs(attemptNumber: number): number;
8
+ executeWithRetry<T>(fn: () => Promise<T>, onRetry?: (attempt: number, backoffMs: number) => void): Promise<T>;
9
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetryManager = void 0;
4
+ class RetryManager {
5
+ constructor(initialBackoffMs = 1000, maxBackoffMs = 32000, maxRetries = 3) {
6
+ this.initialBackoffMs = initialBackoffMs;
7
+ this.maxBackoffMs = maxBackoffMs;
8
+ this.maxRetries = maxRetries;
9
+ }
10
+ shouldRetry(statusCode) {
11
+ if (statusCode >= 500 && statusCode < 600) {
12
+ return true;
13
+ }
14
+ if (statusCode === 408 || statusCode === 429) {
15
+ return true;
16
+ }
17
+ return false;
18
+ }
19
+ getBackoffMs(attemptNumber) {
20
+ const exponentialBackoff = this.initialBackoffMs * Math.pow(2, attemptNumber);
21
+ return Math.min(exponentialBackoff, this.maxBackoffMs);
22
+ }
23
+ async executeWithRetry(fn, onRetry) {
24
+ let lastError = null;
25
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
26
+ try {
27
+ return await fn();
28
+ }
29
+ catch (error) {
30
+ lastError = error instanceof Error ? error : new Error(String(error));
31
+ if (attempt < this.maxRetries) {
32
+ const backoffMs = this.getBackoffMs(attempt);
33
+ if (onRetry) {
34
+ onRetry(attempt + 1, backoffMs);
35
+ }
36
+ await new Promise((resolve) => setTimeout(resolve, backoffMs));
37
+ }
38
+ }
39
+ }
40
+ throw lastError || new Error('Request failed after retries');
41
+ }
42
+ }
43
+ exports.RetryManager = RetryManager;
@@ -0,0 +1,2 @@
1
+ export { HttpClient } from './HttpClient';
2
+ export { RetryManager } from './RetryManager';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetryManager = exports.HttpClient = void 0;
4
+ var HttpClient_1 = require("./HttpClient");
5
+ Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return HttpClient_1.HttpClient; } });
6
+ var RetryManager_1 = require("./RetryManager");
7
+ Object.defineProperty(exports, "RetryManager", { enumerable: true, get: function () { return RetryManager_1.RetryManager; } });
@@ -0,0 +1,3 @@
1
+ export { VeloraClient } from './client';
2
+ export type { HealthResponse, TrackInfo, CurrentTrackResponse, RepeatMode, QueueTrack, PlaybackQueueResponse, PlaybackStateResponse, UserPublic, UserPublicResponse, PlayRequest, PlayResponse, PauseResponse, NextResponse, SeekRequest, SeekResponse, ToggleResponse, AppMetadata, RegisterAppRequest, PermissionType, RequestStatusResponse, RequestStatusPending, RequestStatusApproved, RequestStatusDenied, WebSocketEventEnvelope, TrackChangedData, PlaybackStateChangedData, ErrorResponse, ClientConfig, HttpRequestOptions, RetryPolicy, WebSocketOptions, AuthRequest, CredentialConfig, } from './types';
3
+ export { VeloraError, NetworkError, AuthError, ValidationError, TimeoutError, RateLimitError, RequestError, classifyError, } from './types/errors';
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyError = exports.RequestError = exports.RateLimitError = exports.TimeoutError = exports.ValidationError = exports.AuthError = exports.NetworkError = exports.VeloraError = exports.VeloraClient = void 0;
4
+ var client_1 = require("./client");
5
+ Object.defineProperty(exports, "VeloraClient", { enumerable: true, get: function () { return client_1.VeloraClient; } });
6
+ var errors_1 = require("./types/errors");
7
+ Object.defineProperty(exports, "VeloraError", { enumerable: true, get: function () { return errors_1.VeloraError; } });
8
+ Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return errors_1.NetworkError; } });
9
+ Object.defineProperty(exports, "AuthError", { enumerable: true, get: function () { return errors_1.AuthError; } });
10
+ Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_1.ValidationError; } });
11
+ Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_1.TimeoutError; } });
12
+ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_1.RateLimitError; } });
13
+ Object.defineProperty(exports, "RequestError", { enumerable: true, get: function () { return errors_1.RequestError; } });
14
+ Object.defineProperty(exports, "classifyError", { enumerable: true, get: function () { return errors_1.classifyError; } });
@@ -0,0 +1,128 @@
1
+ export interface HealthResponse {
2
+ ok: boolean;
3
+ status: string;
4
+ port: number;
5
+ host: string;
6
+ }
7
+ export interface TrackInfo {
8
+ id: string;
9
+ title: string;
10
+ artist: string;
11
+ album: string;
12
+ cover_url: string;
13
+ thumbnail_url: string;
14
+ duration: number;
15
+ position: number;
16
+ is_playing: boolean;
17
+ }
18
+ export interface CurrentTrackResponse {
19
+ track: TrackInfo;
20
+ }
21
+ export type RepeatMode = 'off' | 'one' | 'all' | 'on';
22
+ export interface PlaybackStateResponse {
23
+ is_playing: boolean;
24
+ shuffle: boolean;
25
+ repeat: RepeatMode;
26
+ }
27
+ export interface QueueTrack {
28
+ id: string;
29
+ title: string;
30
+ artist: string;
31
+ duration: number;
32
+ cover_url: string;
33
+ thumbnail_url: string;
34
+ }
35
+ export interface PlaybackQueueResponse {
36
+ queue: QueueTrack[];
37
+ current_index: number;
38
+ current_track_id: string;
39
+ current_track_cover_url: string;
40
+ is_playing: boolean;
41
+ shuffle: boolean;
42
+ repeat: RepeatMode;
43
+ updated_at: string;
44
+ }
45
+ export interface UserPublic {
46
+ id: string;
47
+ username: string;
48
+ name: string;
49
+ profile_image_url: string;
50
+ }
51
+ export interface UserPublicResponse {
52
+ user: UserPublic;
53
+ updated_at: string;
54
+ }
55
+ export interface PlayRequest extends Record<string, unknown> {
56
+ track_id?: string;
57
+ }
58
+ export interface PlayResponse {
59
+ ok: boolean;
60
+ track_id: string;
61
+ }
62
+ export interface PauseResponse {
63
+ ok: boolean;
64
+ }
65
+ export interface NextResponse {
66
+ ok: boolean;
67
+ }
68
+ export interface SeekRequest extends Record<string, unknown> {
69
+ position: number;
70
+ }
71
+ export interface SeekResponse {
72
+ ok: boolean;
73
+ position: number;
74
+ }
75
+ export interface ToggleResponse {
76
+ ok: boolean;
77
+ is_playing: boolean;
78
+ }
79
+ export interface AppMetadata {
80
+ name: string;
81
+ description?: string;
82
+ developer?: string;
83
+ website?: string;
84
+ icon?: string;
85
+ }
86
+ export interface RegisterAppRequest {
87
+ app: AppMetadata;
88
+ permissions?: ('read' | 'write')[];
89
+ }
90
+ export interface RegisterResponse {
91
+ request_id: string;
92
+ status: 'pending';
93
+ }
94
+ export type PermissionType = 'read' | 'write';
95
+ export interface RequestStatusPending {
96
+ request_id: string;
97
+ status: 'pending';
98
+ }
99
+ export interface RequestStatusApproved {
100
+ request_id: string;
101
+ status: 'approved';
102
+ access_token: string;
103
+ token_type: string;
104
+ permissions: PermissionType[];
105
+ }
106
+ export interface RequestStatusDenied {
107
+ request_id: string;
108
+ status: 'denied';
109
+ }
110
+ export type RequestStatusResponse = RequestStatusPending | RequestStatusApproved | RequestStatusDenied;
111
+ export interface WebSocketEventEnvelope<T> {
112
+ event: string;
113
+ data: T;
114
+ }
115
+ export interface TrackChangedData {
116
+ id: string;
117
+ title: string;
118
+ artist: string;
119
+ cover_url: string;
120
+ }
121
+ export interface PlaybackStateChangedData {
122
+ is_playing: boolean;
123
+ shuffle: boolean;
124
+ repeat: RepeatMode;
125
+ }
126
+ export interface ErrorResponse {
127
+ error: string;
128
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,44 @@
1
+ import { AppMetadata, PermissionType } from './api';
2
+ export interface HttpRequestOptions {
3
+ timeout?: number;
4
+ maxRetries?: number;
5
+ }
6
+ export interface RetryPolicy {
7
+ maxRetries: number;
8
+ initialBackoffMs: number;
9
+ maxBackoffMs: number;
10
+ }
11
+ export interface WebSocketOptions {
12
+ initialBackoffMs?: number;
13
+ maxBackoffMs?: number;
14
+ autoConnect?: boolean;
15
+ }
16
+ export interface ClientConfig {
17
+ host?: string;
18
+ port?: number;
19
+ token?: string;
20
+ appRegistration?: AppMetadata;
21
+ requestedPermissions?: PermissionType[];
22
+ httpOptions?: HttpRequestOptions;
23
+ wsOptions?: WebSocketOptions;
24
+ }
25
+ export interface AuthRequest {
26
+ app: AppMetadata;
27
+ permissions: PermissionType[];
28
+ }
29
+ export interface CredentialConfig {
30
+ token?: string;
31
+ permissions?: PermissionType[];
32
+ appName?: string;
33
+ }
34
+ export interface WebSocketConfig {
35
+ host: string;
36
+ port: number;
37
+ token: string;
38
+ reconnectOptions: WebSocketReconnectOptions;
39
+ }
40
+ export interface WebSocketReconnectOptions {
41
+ initialBackoffMs: number;
42
+ maxBackoffMs: number;
43
+ maxAttempts?: number;
44
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,30 @@
1
+ export declare class VeloraError extends Error {
2
+ readonly code: string;
3
+ readonly statusCode?: number;
4
+ constructor(message: string, code: string, statusCode?: number);
5
+ }
6
+ export declare class NetworkError extends VeloraError {
7
+ readonly originalError?: Error | undefined;
8
+ constructor(message: string, originalError?: Error | undefined);
9
+ }
10
+ export declare class AuthError extends VeloraError {
11
+ constructor(message: string, statusCode?: number);
12
+ }
13
+ export declare class ValidationError extends VeloraError {
14
+ readonly field?: string | undefined;
15
+ constructor(message: string, field?: string | undefined);
16
+ }
17
+ export declare class TimeoutError extends VeloraError {
18
+ readonly timeoutMs: number;
19
+ constructor(message: string, timeoutMs: number);
20
+ }
21
+ export declare class RateLimitError extends VeloraError {
22
+ readonly retryAfterMs?: number | undefined;
23
+ constructor(message: string, retryAfterMs?: number | undefined);
24
+ }
25
+ export declare class RequestError extends VeloraError {
26
+ readonly statusCode: number;
27
+ readonly response?: Record<string, unknown> | undefined;
28
+ constructor(message: string, statusCode: number, response?: Record<string, unknown> | undefined);
29
+ }
30
+ export declare function classifyError(error: unknown, statusCode?: number): VeloraError;
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequestError = exports.RateLimitError = exports.TimeoutError = exports.ValidationError = exports.AuthError = exports.NetworkError = exports.VeloraError = void 0;
4
+ exports.classifyError = classifyError;
5
+ class VeloraError extends Error {
6
+ constructor(message, code, statusCode) {
7
+ super(message);
8
+ this.name = 'VeloraError';
9
+ this.code = code;
10
+ this.statusCode = statusCode;
11
+ Object.setPrototypeOf(this, VeloraError.prototype);
12
+ }
13
+ }
14
+ exports.VeloraError = VeloraError;
15
+ class NetworkError extends VeloraError {
16
+ constructor(message, originalError) {
17
+ super(message, 'NETWORK_ERROR');
18
+ this.originalError = originalError;
19
+ this.name = 'NetworkError';
20
+ Object.setPrototypeOf(this, NetworkError.prototype);
21
+ }
22
+ }
23
+ exports.NetworkError = NetworkError;
24
+ class AuthError extends VeloraError {
25
+ constructor(message, statusCode) {
26
+ super(message, 'AUTH_ERROR', statusCode);
27
+ this.name = 'AuthError';
28
+ Object.setPrototypeOf(this, AuthError.prototype);
29
+ }
30
+ }
31
+ exports.AuthError = AuthError;
32
+ class ValidationError extends VeloraError {
33
+ constructor(message, field) {
34
+ super(message, 'VALIDATION_ERROR', 400);
35
+ this.field = field;
36
+ this.name = 'ValidationError';
37
+ Object.setPrototypeOf(this, ValidationError.prototype);
38
+ }
39
+ }
40
+ exports.ValidationError = ValidationError;
41
+ class TimeoutError extends VeloraError {
42
+ constructor(message, timeoutMs) {
43
+ super(message, 'TIMEOUT_ERROR');
44
+ this.timeoutMs = timeoutMs;
45
+ this.name = 'TimeoutError';
46
+ Object.setPrototypeOf(this, TimeoutError.prototype);
47
+ }
48
+ }
49
+ exports.TimeoutError = TimeoutError;
50
+ class RateLimitError extends VeloraError {
51
+ constructor(message, retryAfterMs) {
52
+ super(message, 'RATE_LIMIT_ERROR', 429);
53
+ this.retryAfterMs = retryAfterMs;
54
+ this.name = 'RateLimitError';
55
+ Object.setPrototypeOf(this, RateLimitError.prototype);
56
+ }
57
+ }
58
+ exports.RateLimitError = RateLimitError;
59
+ class RequestError extends VeloraError {
60
+ constructor(message, statusCode, response) {
61
+ super(message, 'REQUEST_ERROR', statusCode);
62
+ this.statusCode = statusCode;
63
+ this.response = response;
64
+ this.name = 'RequestError';
65
+ Object.setPrototypeOf(this, RequestError.prototype);
66
+ }
67
+ }
68
+ exports.RequestError = RequestError;
69
+ function classifyError(error, statusCode) {
70
+ if (error instanceof VeloraError) {
71
+ return error;
72
+ }
73
+ if (error instanceof TypeError) {
74
+ if (error.message.includes('fetch') || error.message.includes('network')) {
75
+ return new NetworkError(error.message, error);
76
+ }
77
+ }
78
+ if (statusCode === 401 || statusCode === 403) {
79
+ return new AuthError(error instanceof Error ? error.message : 'Unauthorized', statusCode);
80
+ }
81
+ if (statusCode === 400) {
82
+ return new ValidationError(error instanceof Error ? error.message : 'Invalid request');
83
+ }
84
+ if (statusCode === 429) {
85
+ return new RateLimitError(error instanceof Error ? error.message : 'Rate limited');
86
+ }
87
+ if (statusCode && statusCode >= 500) {
88
+ let message = 'Server error';
89
+ if (error instanceof Error) {
90
+ message = error.message;
91
+ }
92
+ else if (typeof error === 'string') {
93
+ message = error;
94
+ }
95
+ return new RequestError(message, statusCode);
96
+ }
97
+ let fallbackMessage = 'Unknown error';
98
+ if (error instanceof Error) {
99
+ fallbackMessage = error.message;
100
+ }
101
+ else if (typeof error === 'string') {
102
+ fallbackMessage = error;
103
+ }
104
+ return new VeloraError(fallbackMessage, 'UNKNOWN_ERROR', statusCode);
105
+ }
@@ -0,0 +1,3 @@
1
+ export * from './api';
2
+ export * from './client';
3
+ export * from './errors';