cyberdesk 2.2.4 → 2.2.6
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.
- package/dist/index.d.ts +19 -1
- package/dist/index.js +163 -5
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -29,20 +29,38 @@ import { type MachineResponse, type MachinePoolUpdate, type PoolResponse, type P
|
|
|
29
29
|
export * from './client/types.gen';
|
|
30
30
|
export * from './client/sdk.gen';
|
|
31
31
|
export * from './client/client.gen';
|
|
32
|
+
/**
|
|
33
|
+
* Client options for configuring retry behavior and other settings
|
|
34
|
+
*/
|
|
35
|
+
export interface CyberdeskClientOptions {
|
|
36
|
+
/** Maximum number of retry attempts (default: 2, meaning 3 total attempts) */
|
|
37
|
+
maxRetries?: number;
|
|
38
|
+
/** Custom fetch implementation (for testing or advanced use cases) */
|
|
39
|
+
fetch?: typeof fetch;
|
|
40
|
+
}
|
|
32
41
|
/**
|
|
33
42
|
* Create a Cyberdesk API client
|
|
34
43
|
*
|
|
35
44
|
* @param apiKey - Your Cyberdesk API key
|
|
36
45
|
* @param baseUrl - Optional API base URL (defaults to https://api.cyberdesk.io)
|
|
46
|
+
* @param options - Optional client configuration
|
|
47
|
+
* @param options.maxRetries - Maximum retry attempts for failed requests (default: 2)
|
|
37
48
|
* @returns Configured client with all API endpoints
|
|
38
49
|
*
|
|
39
50
|
* @example
|
|
40
51
|
* ```typescript
|
|
52
|
+
* // Basic usage
|
|
41
53
|
* const client = createCyberdeskClient('your-api-key');
|
|
42
54
|
* const machines = await client.machines.list();
|
|
55
|
+
*
|
|
56
|
+
* // With custom retry configuration
|
|
57
|
+
* const client = createCyberdeskClient('your-api-key', undefined, { maxRetries: 3 });
|
|
58
|
+
*
|
|
59
|
+
* // Disable retries
|
|
60
|
+
* const client = createCyberdeskClient('your-api-key', undefined, { maxRetries: 0 });
|
|
43
61
|
* ```
|
|
44
62
|
*/
|
|
45
|
-
export declare function createCyberdeskClient(apiKey: string, baseUrl?: string): {
|
|
63
|
+
export declare function createCyberdeskClient(apiKey: string, baseUrl?: string, options?: CyberdeskClientOptions): {
|
|
46
64
|
machines: {
|
|
47
65
|
/**
|
|
48
66
|
* List machines with optional filtering
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,150 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
53
|
exports.createCyberdeskClient = createCyberdeskClient;
|
|
54
54
|
const client_fetch_1 = require("@hey-api/client-fetch");
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Retry Configuration (Stripe SDK-style)
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/** Default number of retry attempts (2 retries = 3 total attempts, like Stripe) */
|
|
59
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
60
|
+
/** Initial retry delay in milliseconds */
|
|
61
|
+
const INITIAL_RETRY_DELAY_MS = 500;
|
|
62
|
+
/** Maximum retry delay in milliseconds */
|
|
63
|
+
const MAX_RETRY_DELAY_MS = 5000;
|
|
64
|
+
/** HTTP status codes that should trigger a retry */
|
|
65
|
+
const RETRYABLE_STATUS_CODES = new Set([
|
|
66
|
+
408, // Request Timeout
|
|
67
|
+
409, // Conflict (can be retried with idempotency)
|
|
68
|
+
429, // Too Many Requests
|
|
69
|
+
500, // Internal Server Error
|
|
70
|
+
502, // Bad Gateway
|
|
71
|
+
503, // Service Unavailable
|
|
72
|
+
504, // Gateway Timeout
|
|
73
|
+
]);
|
|
74
|
+
/**
|
|
75
|
+
* Determines if an error is a retryable network error
|
|
76
|
+
*
|
|
77
|
+
* Note: AbortError (from AbortController.abort()) is NOT retried because:
|
|
78
|
+
* - It indicates intentional cancellation by the caller
|
|
79
|
+
* - The abort signal remains aborted, so retries would fail immediately
|
|
80
|
+
* - Retrying would waste attempts and delay error propagation
|
|
81
|
+
*/
|
|
82
|
+
function isNetworkError(error) {
|
|
83
|
+
// AbortError means intentional cancellation - don't retry
|
|
84
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (error instanceof TypeError) {
|
|
88
|
+
const message = error.message.toLowerCase();
|
|
89
|
+
return (message.includes('fetch') ||
|
|
90
|
+
message.includes('network') ||
|
|
91
|
+
message.includes('failed') ||
|
|
92
|
+
message.includes('timeout'));
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Determines if a response status code should trigger a retry
|
|
98
|
+
*/
|
|
99
|
+
function shouldRetryResponse(response) {
|
|
100
|
+
return RETRYABLE_STATUS_CODES.has(response.status);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Calculate delay for exponential backoff with full jitter (Stripe-style)
|
|
104
|
+
*
|
|
105
|
+
* Formula: min(cap, random(0, base * 2^attempt))
|
|
106
|
+
* This provides better distribution than adding jitter to exponential backoff
|
|
107
|
+
*/
|
|
108
|
+
function calculateRetryDelay(attempt, retryAfterMs) {
|
|
109
|
+
if (retryAfterMs && retryAfterMs > 0) {
|
|
110
|
+
// Respect server's Retry-After header, but cap it
|
|
111
|
+
return Math.min(retryAfterMs, MAX_RETRY_DELAY_MS);
|
|
112
|
+
}
|
|
113
|
+
// Full jitter exponential backoff
|
|
114
|
+
const exponentialDelay = INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt);
|
|
115
|
+
const maxDelay = Math.min(exponentialDelay, MAX_RETRY_DELAY_MS);
|
|
116
|
+
return Math.random() * maxDelay;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Parse Retry-After header value to milliseconds
|
|
120
|
+
*/
|
|
121
|
+
function parseRetryAfter(response) {
|
|
122
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
123
|
+
if (!retryAfter)
|
|
124
|
+
return undefined;
|
|
125
|
+
// Try parsing as seconds (integer)
|
|
126
|
+
const seconds = parseInt(retryAfter, 10);
|
|
127
|
+
if (!isNaN(seconds)) {
|
|
128
|
+
return seconds * 1000;
|
|
129
|
+
}
|
|
130
|
+
// Try parsing as HTTP date
|
|
131
|
+
const date = Date.parse(retryAfter);
|
|
132
|
+
if (!isNaN(date)) {
|
|
133
|
+
return Math.max(0, date - Date.now());
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Sleep for a specified duration
|
|
139
|
+
*/
|
|
140
|
+
function sleep(ms) {
|
|
141
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Creates a fetch wrapper with automatic retry logic
|
|
145
|
+
*
|
|
146
|
+
* Implements Stripe-style retry behavior:
|
|
147
|
+
* - Retries on network errors (connection failures, timeouts)
|
|
148
|
+
* - Retries on 5xx server errors and 429 (rate limit)
|
|
149
|
+
* - Does NOT retry on 4xx client errors (except 408, 409, 429)
|
|
150
|
+
* - Uses exponential backoff with full jitter
|
|
151
|
+
* - Respects Retry-After headers
|
|
152
|
+
*/
|
|
153
|
+
function createRetryFetch(maxRetries = DEFAULT_MAX_RETRIES) {
|
|
154
|
+
return function retryFetch(input, init) {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
let lastError;
|
|
157
|
+
let lastResponse;
|
|
158
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
159
|
+
try {
|
|
160
|
+
// Use globalThis.fetch directly bound to avoid "Illegal invocation"
|
|
161
|
+
const response = yield globalThis.fetch(input, init);
|
|
162
|
+
// Success - return immediately
|
|
163
|
+
if (!shouldRetryResponse(response)) {
|
|
164
|
+
return response;
|
|
165
|
+
}
|
|
166
|
+
// Store response for potential retry
|
|
167
|
+
lastResponse = response;
|
|
168
|
+
// Don't retry on last attempt
|
|
169
|
+
if (attempt === maxRetries) {
|
|
170
|
+
return response;
|
|
171
|
+
}
|
|
172
|
+
// Calculate delay with Retry-After header support
|
|
173
|
+
const retryAfterMs = parseRetryAfter(response);
|
|
174
|
+
const delay = calculateRetryDelay(attempt, retryAfterMs);
|
|
175
|
+
yield sleep(delay);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
lastError = error;
|
|
179
|
+
// Only retry on network errors
|
|
180
|
+
if (!isNetworkError(error)) {
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
// Don't retry on last attempt
|
|
184
|
+
if (attempt === maxRetries) {
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
const delay = calculateRetryDelay(attempt);
|
|
188
|
+
yield sleep(delay);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// This should never be reached, but TypeScript needs it
|
|
192
|
+
if (lastResponse) {
|
|
193
|
+
return lastResponse;
|
|
194
|
+
}
|
|
195
|
+
throw lastError;
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
}
|
|
55
199
|
// Import SDK methods from sdk.gen
|
|
56
200
|
const sdk_gen_1 = require("./client/sdk.gen");
|
|
57
201
|
// Export all generated types and methods for direct use
|
|
@@ -62,14 +206,18 @@ __exportStar(require("./client/client.gen"), exports);
|
|
|
62
206
|
/** Default API base URL for Cyberdesk Cloud API */
|
|
63
207
|
const DEFAULT_API_BASE_URL = "https://api.cyberdesk.io";
|
|
64
208
|
/**
|
|
65
|
-
* Create a configured HTTP client with authentication
|
|
209
|
+
* Create a configured HTTP client with authentication and automatic retries
|
|
66
210
|
*
|
|
67
211
|
* @internal
|
|
68
212
|
* @param apiKey - Your Cyberdesk API key
|
|
69
213
|
* @param baseUrl - API base URL
|
|
70
|
-
* @
|
|
214
|
+
* @param options - Client configuration options
|
|
215
|
+
* @returns Configured HTTP client with retry logic
|
|
71
216
|
*/
|
|
72
|
-
function createApiClient(apiKey, baseUrl = DEFAULT_API_BASE_URL) {
|
|
217
|
+
function createApiClient(apiKey, baseUrl = DEFAULT_API_BASE_URL, options = {}) {
|
|
218
|
+
const { maxRetries = DEFAULT_MAX_RETRIES, fetch: customFetch } = options;
|
|
219
|
+
// Use custom fetch if provided, otherwise create retry-enabled fetch
|
|
220
|
+
const fetchWithRetry = customFetch !== null && customFetch !== void 0 ? customFetch : createRetryFetch(maxRetries);
|
|
73
221
|
return (0, client_fetch_1.createClient)({
|
|
74
222
|
baseUrl,
|
|
75
223
|
headers: {
|
|
@@ -77,6 +225,7 @@ function createApiClient(apiKey, baseUrl = DEFAULT_API_BASE_URL) {
|
|
|
77
225
|
'Authorization': `Bearer ${apiKey}`,
|
|
78
226
|
'Connection': 'keep-alive',
|
|
79
227
|
},
|
|
228
|
+
fetch: fetchWithRetry,
|
|
80
229
|
});
|
|
81
230
|
}
|
|
82
231
|
// Helpers
|
|
@@ -90,16 +239,25 @@ function toIsoUtc(value) {
|
|
|
90
239
|
*
|
|
91
240
|
* @param apiKey - Your Cyberdesk API key
|
|
92
241
|
* @param baseUrl - Optional API base URL (defaults to https://api.cyberdesk.io)
|
|
242
|
+
* @param options - Optional client configuration
|
|
243
|
+
* @param options.maxRetries - Maximum retry attempts for failed requests (default: 2)
|
|
93
244
|
* @returns Configured client with all API endpoints
|
|
94
245
|
*
|
|
95
246
|
* @example
|
|
96
247
|
* ```typescript
|
|
248
|
+
* // Basic usage
|
|
97
249
|
* const client = createCyberdeskClient('your-api-key');
|
|
98
250
|
* const machines = await client.machines.list();
|
|
251
|
+
*
|
|
252
|
+
* // With custom retry configuration
|
|
253
|
+
* const client = createCyberdeskClient('your-api-key', undefined, { maxRetries: 3 });
|
|
254
|
+
*
|
|
255
|
+
* // Disable retries
|
|
256
|
+
* const client = createCyberdeskClient('your-api-key', undefined, { maxRetries: 0 });
|
|
99
257
|
* ```
|
|
100
258
|
*/
|
|
101
|
-
function createCyberdeskClient(apiKey, baseUrl) {
|
|
102
|
-
const client = createApiClient(apiKey, baseUrl);
|
|
259
|
+
function createCyberdeskClient(apiKey, baseUrl, options) {
|
|
260
|
+
const client = createApiClient(apiKey, baseUrl, options);
|
|
103
261
|
return {
|
|
104
262
|
// Machine endpoints
|
|
105
263
|
machines: {
|