@debros/orama 0.122.4-nightly

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +665 -0
  3. package/dist/index.d.ts +1334 -0
  4. package/dist/index.js +2553 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +82 -0
  7. package/src/auth/client.ts +276 -0
  8. package/src/auth/index.ts +3 -0
  9. package/src/auth/types.ts +62 -0
  10. package/src/cache/client.ts +203 -0
  11. package/src/cache/index.ts +14 -0
  12. package/src/core/http.ts +541 -0
  13. package/src/core/index.ts +10 -0
  14. package/src/core/interfaces/IAuthStrategy.ts +28 -0
  15. package/src/core/interfaces/IHttpTransport.ts +73 -0
  16. package/src/core/interfaces/IRetryPolicy.ts +20 -0
  17. package/src/core/interfaces/IWebSocketClient.ts +60 -0
  18. package/src/core/interfaces/index.ts +4 -0
  19. package/src/core/transport/AuthHeaderStrategy.ts +108 -0
  20. package/src/core/transport/RequestLogger.ts +116 -0
  21. package/src/core/transport/RequestRetryPolicy.ts +53 -0
  22. package/src/core/transport/TLSConfiguration.ts +53 -0
  23. package/src/core/transport/index.ts +4 -0
  24. package/src/core/ws.ts +246 -0
  25. package/src/db/client.ts +126 -0
  26. package/src/db/index.ts +13 -0
  27. package/src/db/qb.ts +111 -0
  28. package/src/db/repository.ts +128 -0
  29. package/src/db/types.ts +67 -0
  30. package/src/errors.ts +38 -0
  31. package/src/functions/client.ts +62 -0
  32. package/src/functions/index.ts +2 -0
  33. package/src/functions/types.ts +21 -0
  34. package/src/index.ts +201 -0
  35. package/src/network/client.ts +119 -0
  36. package/src/network/index.ts +7 -0
  37. package/src/pubsub/client.ts +361 -0
  38. package/src/pubsub/index.ts +12 -0
  39. package/src/pubsub/types.ts +46 -0
  40. package/src/storage/client.ts +272 -0
  41. package/src/storage/index.ts +7 -0
  42. package/src/utils/codec.ts +68 -0
  43. package/src/utils/index.ts +3 -0
  44. package/src/utils/platform.ts +44 -0
  45. package/src/utils/retry.ts +58 -0
  46. package/src/vault/auth.ts +98 -0
  47. package/src/vault/client.ts +197 -0
  48. package/src/vault/crypto/aes.ts +271 -0
  49. package/src/vault/crypto/hkdf.ts +42 -0
  50. package/src/vault/crypto/index.ts +27 -0
  51. package/src/vault/crypto/shamir.ts +173 -0
  52. package/src/vault/index.ts +65 -0
  53. package/src/vault/quorum.ts +16 -0
  54. package/src/vault/transport/fanout.ts +94 -0
  55. package/src/vault/transport/guardian.ts +285 -0
  56. package/src/vault/transport/index.ts +19 -0
  57. package/src/vault/transport/types.ts +101 -0
  58. package/src/vault/types.ts +62 -0
@@ -0,0 +1,541 @@
1
+ import { SDKError } from "../errors";
2
+
3
+ /**
4
+ * Context provided to the onNetworkError callback
5
+ */
6
+ export interface NetworkErrorContext {
7
+ method: "GET" | "POST" | "PUT" | "DELETE" | "WS";
8
+ path: string;
9
+ isRetry: boolean;
10
+ attempt: number;
11
+ }
12
+
13
+ /**
14
+ * Callback invoked when a network error occurs.
15
+ * Use this to trigger gateway failover or other error handling.
16
+ */
17
+ export type NetworkErrorCallback = (
18
+ error: SDKError,
19
+ context: NetworkErrorContext
20
+ ) => void;
21
+
22
+ export interface HttpClientConfig {
23
+ baseURL: string;
24
+ timeout?: number;
25
+ maxRetries?: number;
26
+ retryDelayMs?: number;
27
+ fetch?: typeof fetch;
28
+ /**
29
+ * Enable debug logging (includes full SQL queries and args). Default: false
30
+ */
31
+ debug?: boolean;
32
+ /**
33
+ * Callback invoked on network errors (after all retries exhausted).
34
+ * Use this to trigger gateway failover at the application layer.
35
+ */
36
+ onNetworkError?: NetworkErrorCallback;
37
+ }
38
+
39
+ /**
40
+ * Create a fetch function with proper TLS configuration for staging certificates
41
+ * In Node.js, we need to configure TLS to accept Let's Encrypt staging certificates
42
+ */
43
+ function createFetchWithTLSConfig(): typeof fetch {
44
+ // Check if we're in a Node.js environment
45
+ if (typeof process !== "undefined" && process.versions?.node) {
46
+ // For testing/staging/development: allow staging certificates
47
+ // Let's Encrypt staging certificates are self-signed and not trusted by default
48
+ const isDevelopmentOrStaging =
49
+ process.env.NODE_ENV !== "production" ||
50
+ process.env.DEBROS_ALLOW_STAGING_CERTS === "true" ||
51
+ process.env.DEBROS_USE_HTTPS === "true";
52
+
53
+ if (isDevelopmentOrStaging) {
54
+ // Allow self-signed/staging certificates
55
+ // WARNING: Only use this in development/testing environments
56
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
57
+ }
58
+ }
59
+ return globalThis.fetch;
60
+ }
61
+
62
+ export class HttpClient {
63
+ private baseURL: string;
64
+ private timeout: number;
65
+ private maxRetries: number;
66
+ private retryDelayMs: number;
67
+ private fetch: typeof fetch;
68
+ private apiKey?: string;
69
+ private jwt?: string;
70
+ private debug: boolean;
71
+ private onNetworkError?: NetworkErrorCallback;
72
+
73
+ constructor(config: HttpClientConfig) {
74
+ this.baseURL = config.baseURL.replace(/\/$/, "");
75
+ this.timeout = config.timeout ?? 60000;
76
+ this.maxRetries = config.maxRetries ?? 3;
77
+ this.retryDelayMs = config.retryDelayMs ?? 1000;
78
+ // Use provided fetch or create one with proper TLS configuration for staging certificates
79
+ this.fetch = config.fetch ?? createFetchWithTLSConfig();
80
+ this.debug = config.debug ?? false;
81
+ this.onNetworkError = config.onNetworkError;
82
+ }
83
+
84
+ /**
85
+ * Set the network error callback
86
+ */
87
+ setOnNetworkError(callback: NetworkErrorCallback | undefined): void {
88
+ this.onNetworkError = callback;
89
+ }
90
+
91
+ setApiKey(apiKey?: string) {
92
+ this.apiKey = apiKey;
93
+ // Don't clear JWT - allow both to coexist
94
+ }
95
+
96
+ setJwt(jwt?: string) {
97
+ this.jwt = jwt;
98
+ // Don't clear API key - allow both to coexist
99
+ if (typeof console !== "undefined") {
100
+ console.log(
101
+ "[HttpClient] JWT set:",
102
+ !!jwt,
103
+ "API key still present:",
104
+ !!this.apiKey
105
+ );
106
+ }
107
+ }
108
+
109
+ private getAuthHeaders(path: string): Record<string, string> {
110
+ const headers: Record<string, string> = {};
111
+
112
+ // For database, pubsub, proxy, and cache operations, ONLY use API key to avoid JWT user context
113
+ // interfering with namespace-level authorization
114
+ const isDbOperation = path.includes("/v1/rqlite/");
115
+ const isPubSubOperation = path.includes("/v1/pubsub/");
116
+ const isProxyOperation = path.includes("/v1/proxy/");
117
+ const isCacheOperation = path.includes("/v1/cache/");
118
+
119
+ // For auth operations, prefer API key over JWT to ensure proper authentication
120
+ const isAuthOperation = path.includes("/v1/auth/");
121
+
122
+ if (
123
+ isDbOperation ||
124
+ isPubSubOperation ||
125
+ isProxyOperation ||
126
+ isCacheOperation
127
+ ) {
128
+ // For database/pubsub/proxy/cache operations: use only API key (preferred for namespace operations)
129
+ if (this.apiKey) {
130
+ headers["X-API-Key"] = this.apiKey;
131
+ } else if (this.jwt) {
132
+ // Fallback to JWT if no API key
133
+ headers["Authorization"] = `Bearer ${this.jwt}`;
134
+ }
135
+ } else if (isAuthOperation) {
136
+ // For auth operations: prefer API key over JWT (auth endpoints should use explicit API key)
137
+ if (this.apiKey) {
138
+ headers["X-API-Key"] = this.apiKey;
139
+ }
140
+ if (this.jwt) {
141
+ headers["Authorization"] = `Bearer ${this.jwt}`;
142
+ }
143
+ } else {
144
+ // For other operations: send both JWT and API key
145
+ if (this.jwt) {
146
+ headers["Authorization"] = `Bearer ${this.jwt}`;
147
+ }
148
+ if (this.apiKey) {
149
+ headers["X-API-Key"] = this.apiKey;
150
+ }
151
+ }
152
+ return headers;
153
+ }
154
+
155
+ private getAuthToken(): string | undefined {
156
+ return this.jwt || this.apiKey;
157
+ }
158
+
159
+ getApiKey(): string | undefined {
160
+ return this.apiKey;
161
+ }
162
+
163
+ /**
164
+ * Get the base URL
165
+ */
166
+ getBaseURL(): string {
167
+ return this.baseURL;
168
+ }
169
+
170
+ async request<T = any>(
171
+ method: "GET" | "POST" | "PUT" | "DELETE",
172
+ path: string,
173
+ options: {
174
+ body?: any;
175
+ headers?: Record<string, string>;
176
+ query?: Record<string, string | number | boolean>;
177
+ timeout?: number; // Per-request timeout override
178
+ } = {}
179
+ ): Promise<T> {
180
+ const startTime = performance.now(); // Track request start time
181
+ const url = new URL(this.baseURL + path);
182
+ if (options.query) {
183
+ Object.entries(options.query).forEach(([key, value]) => {
184
+ url.searchParams.append(key, String(value));
185
+ });
186
+ }
187
+
188
+ const headers: Record<string, string> = {
189
+ "Content-Type": "application/json",
190
+ ...this.getAuthHeaders(path),
191
+ ...options.headers,
192
+ };
193
+
194
+ const controller = new AbortController();
195
+ const requestTimeout = options.timeout ?? this.timeout; // Use override or default
196
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
197
+
198
+ const fetchOptions: RequestInit = {
199
+ method,
200
+ headers,
201
+ signal: controller.signal,
202
+ };
203
+
204
+ if (options.body !== undefined) {
205
+ fetchOptions.body = JSON.stringify(options.body);
206
+ }
207
+
208
+ // Extract and log SQL query details for rqlite operations
209
+ const isRqliteOperation = path.includes("/v1/rqlite/");
210
+ let queryDetails: string | null = null;
211
+ if (isRqliteOperation && options.body) {
212
+ try {
213
+ const body =
214
+ typeof options.body === "string"
215
+ ? JSON.parse(options.body)
216
+ : options.body;
217
+
218
+ if (body.sql) {
219
+ // Direct SQL query (query/exec endpoints)
220
+ queryDetails = `SQL: ${body.sql}`;
221
+ if (body.args && body.args.length > 0) {
222
+ queryDetails += ` | Args: [${body.args
223
+ .map((a: any) => (typeof a === "string" ? `"${a}"` : a))
224
+ .join(", ")}]`;
225
+ }
226
+ } else if (body.table) {
227
+ // Table-based query (find/find-one/select endpoints)
228
+ queryDetails = `Table: ${body.table}`;
229
+ if (body.criteria && Object.keys(body.criteria).length > 0) {
230
+ queryDetails += ` | Criteria: ${JSON.stringify(body.criteria)}`;
231
+ }
232
+ if (body.options) {
233
+ queryDetails += ` | Options: ${JSON.stringify(body.options)}`;
234
+ }
235
+ if (body.select) {
236
+ queryDetails += ` | Select: ${JSON.stringify(body.select)}`;
237
+ }
238
+ if (body.where) {
239
+ queryDetails += ` | Where: ${JSON.stringify(body.where)}`;
240
+ }
241
+ if (body.limit) {
242
+ queryDetails += ` | Limit: ${body.limit}`;
243
+ }
244
+ if (body.offset) {
245
+ queryDetails += ` | Offset: ${body.offset}`;
246
+ }
247
+ }
248
+ } catch (e) {
249
+ // Failed to parse body, ignore
250
+ }
251
+ }
252
+
253
+ try {
254
+ const result = await this.requestWithRetry(
255
+ url.toString(),
256
+ fetchOptions,
257
+ 0,
258
+ startTime
259
+ );
260
+ const duration = performance.now() - startTime;
261
+ if (typeof console !== "undefined") {
262
+ const logMessage = `[HttpClient] ${method} ${path} completed in ${duration.toFixed(
263
+ 2
264
+ )}ms`;
265
+ if (queryDetails && this.debug) {
266
+ console.log(logMessage);
267
+ console.log(`[HttpClient] ${queryDetails}`);
268
+ } else {
269
+ console.log(logMessage);
270
+ }
271
+ }
272
+ return result;
273
+ } catch (error) {
274
+ const duration = performance.now() - startTime;
275
+ if (typeof console !== "undefined") {
276
+ // For 404 errors on find-one calls, log at warn level (not error) since "not found" is expected
277
+ // Application layer handles these cases in try-catch blocks
278
+ const is404FindOne =
279
+ path === "/v1/rqlite/find-one" &&
280
+ error instanceof SDKError &&
281
+ error.httpStatus === 404;
282
+
283
+ if (is404FindOne) {
284
+ // Log as warning for visibility, but not as error since it's expected behavior
285
+ console.warn(
286
+ `[HttpClient] ${method} ${path} returned 404 after ${duration.toFixed(
287
+ 2
288
+ )}ms (expected for optional lookups)`
289
+ );
290
+ } else {
291
+ const errorMessage = `[HttpClient] ${method} ${path} failed after ${duration.toFixed(
292
+ 2
293
+ )}ms:`;
294
+ console.error(errorMessage, error);
295
+ if (queryDetails && this.debug) {
296
+ console.error(`[HttpClient] ${queryDetails}`);
297
+ }
298
+ }
299
+ }
300
+
301
+ // Call the network error callback if configured
302
+ // This allows the app to trigger gateway failover
303
+ if (this.onNetworkError) {
304
+ // Convert native errors (TypeError, AbortError) to SDKError for the callback
305
+ const sdkError =
306
+ error instanceof SDKError
307
+ ? error
308
+ : new SDKError(
309
+ error instanceof Error ? error.message : String(error),
310
+ 0, // httpStatus 0 indicates network-level failure
311
+ "NETWORK_ERROR"
312
+ );
313
+ this.onNetworkError(sdkError, {
314
+ method,
315
+ path,
316
+ isRetry: false,
317
+ attempt: this.maxRetries, // All retries exhausted
318
+ });
319
+ }
320
+
321
+ throw error;
322
+ } finally {
323
+ clearTimeout(timeoutId);
324
+ }
325
+ }
326
+
327
+ private async requestWithRetry(
328
+ url: string,
329
+ options: RequestInit,
330
+ attempt: number = 0,
331
+ startTime?: number // Track start time for timing across retries
332
+ ): Promise<any> {
333
+ try {
334
+ const response = await this.fetch(url, options);
335
+
336
+ if (!response.ok) {
337
+ let body: any;
338
+ try {
339
+ body = await response.json();
340
+ } catch {
341
+ body = { error: response.statusText };
342
+ }
343
+ throw SDKError.fromResponse(response.status, body);
344
+ }
345
+
346
+ // Request succeeded - return response
347
+ const contentType = response.headers.get("content-type");
348
+ if (contentType?.includes("application/json")) {
349
+ return response.json();
350
+ }
351
+ return response.text();
352
+ } catch (error) {
353
+ const isRetryableError =
354
+ error instanceof SDKError &&
355
+ [408, 429, 500, 502, 503, 504].includes(error.httpStatus);
356
+
357
+ // Retry on same gateway for retryable HTTP errors
358
+ if (isRetryableError && attempt < this.maxRetries) {
359
+ if (typeof console !== "undefined") {
360
+ console.warn(
361
+ `[HttpClient] Retrying request (attempt ${attempt + 1}/${this.maxRetries})`
362
+ );
363
+ }
364
+ await new Promise((resolve) =>
365
+ setTimeout(resolve, this.retryDelayMs * (attempt + 1))
366
+ );
367
+ return this.requestWithRetry(url, options, attempt + 1, startTime);
368
+ }
369
+
370
+ // All retries exhausted - throw error for app to handle
371
+ throw error;
372
+ }
373
+ }
374
+
375
+ async get<T = any>(
376
+ path: string,
377
+ options?: Omit<Parameters<typeof this.request>[2], "body">
378
+ ): Promise<T> {
379
+ return this.request<T>("GET", path, options);
380
+ }
381
+
382
+ async post<T = any>(
383
+ path: string,
384
+ body?: any,
385
+ options?: Omit<Parameters<typeof this.request>[2], "body">
386
+ ): Promise<T> {
387
+ return this.request<T>("POST", path, { ...options, body });
388
+ }
389
+
390
+ async put<T = any>(
391
+ path: string,
392
+ body?: any,
393
+ options?: Omit<Parameters<typeof this.request>[2], "body">
394
+ ): Promise<T> {
395
+ return this.request<T>("PUT", path, { ...options, body });
396
+ }
397
+
398
+ async delete<T = any>(
399
+ path: string,
400
+ options?: Omit<Parameters<typeof this.request>[2], "body">
401
+ ): Promise<T> {
402
+ return this.request<T>("DELETE", path, options);
403
+ }
404
+
405
+ /**
406
+ * Upload a file using multipart/form-data
407
+ * This is a special method for file uploads that bypasses JSON serialization
408
+ */
409
+ async uploadFile<T = any>(
410
+ path: string,
411
+ formData: FormData,
412
+ options?: {
413
+ timeout?: number;
414
+ }
415
+ ): Promise<T> {
416
+ const startTime = performance.now(); // Track upload start time
417
+ const url = new URL(this.baseURL + path);
418
+ const headers: Record<string, string> = {
419
+ ...this.getAuthHeaders(path),
420
+ // Don't set Content-Type - browser will set it with boundary
421
+ };
422
+
423
+ const controller = new AbortController();
424
+ const requestTimeout = options?.timeout ?? this.timeout * 5; // 5x timeout for uploads
425
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
426
+
427
+ const fetchOptions: RequestInit = {
428
+ method: "POST",
429
+ headers,
430
+ body: formData,
431
+ signal: controller.signal,
432
+ };
433
+
434
+ try {
435
+ const result = await this.requestWithRetry(
436
+ url.toString(),
437
+ fetchOptions,
438
+ 0,
439
+ startTime
440
+ );
441
+ const duration = performance.now() - startTime;
442
+ if (typeof console !== "undefined") {
443
+ console.log(
444
+ `[HttpClient] POST ${path} (upload) completed in ${duration.toFixed(
445
+ 2
446
+ )}ms`
447
+ );
448
+ }
449
+ return result;
450
+ } catch (error) {
451
+ const duration = performance.now() - startTime;
452
+ if (typeof console !== "undefined") {
453
+ console.error(
454
+ `[HttpClient] POST ${path} (upload) failed after ${duration.toFixed(
455
+ 2
456
+ )}ms:`,
457
+ error
458
+ );
459
+ }
460
+
461
+ // Call the network error callback if configured
462
+ if (this.onNetworkError) {
463
+ const sdkError =
464
+ error instanceof SDKError
465
+ ? error
466
+ : new SDKError(
467
+ error instanceof Error ? error.message : String(error),
468
+ 0,
469
+ "NETWORK_ERROR"
470
+ );
471
+ this.onNetworkError(sdkError, {
472
+ method: "POST",
473
+ path,
474
+ isRetry: false,
475
+ attempt: this.maxRetries,
476
+ });
477
+ }
478
+
479
+ throw error;
480
+ } finally {
481
+ clearTimeout(timeoutId);
482
+ }
483
+ }
484
+
485
+ /**
486
+ * Get a binary response (returns Response object for streaming)
487
+ */
488
+ async getBinary(path: string): Promise<Response> {
489
+ const url = new URL(this.baseURL + path);
490
+ const headers: Record<string, string> = {
491
+ ...this.getAuthHeaders(path),
492
+ };
493
+
494
+ const controller = new AbortController();
495
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout * 5); // 5x timeout for downloads
496
+
497
+ const fetchOptions: RequestInit = {
498
+ method: "GET",
499
+ headers,
500
+ signal: controller.signal,
501
+ };
502
+
503
+ try {
504
+ const response = await this.fetch(url.toString(), fetchOptions);
505
+ if (!response.ok) {
506
+ clearTimeout(timeoutId);
507
+ const errorBody = await response.json().catch(() => ({
508
+ error: response.statusText,
509
+ }));
510
+ throw SDKError.fromResponse(response.status, errorBody);
511
+ }
512
+ return response;
513
+ } catch (error) {
514
+ clearTimeout(timeoutId);
515
+
516
+ // Call the network error callback if configured
517
+ if (this.onNetworkError) {
518
+ const sdkError =
519
+ error instanceof SDKError
520
+ ? error
521
+ : new SDKError(
522
+ error instanceof Error ? error.message : String(error),
523
+ 0,
524
+ "NETWORK_ERROR"
525
+ );
526
+ this.onNetworkError(sdkError, {
527
+ method: "GET",
528
+ path,
529
+ isRetry: false,
530
+ attempt: 0,
531
+ });
532
+ }
533
+
534
+ throw error;
535
+ }
536
+ }
537
+
538
+ getToken(): string | undefined {
539
+ return this.getAuthToken();
540
+ }
541
+ }
@@ -0,0 +1,10 @@
1
+ export { HttpClient, type HttpClientConfig, type NetworkErrorCallback, type NetworkErrorContext } from "./http";
2
+ export { WSClient, type WSClientConfig } from "./ws";
3
+ export type { IHttpTransport, RequestOptions } from "./interfaces/IHttpTransport";
4
+ export type { IWebSocketClient } from "./interfaces/IWebSocketClient";
5
+ export type { IAuthStrategy, RequestContext } from "./interfaces/IAuthStrategy";
6
+ export type { IRetryPolicy } from "./interfaces/IRetryPolicy";
7
+ export { PathBasedAuthStrategy } from "./transport/AuthHeaderStrategy";
8
+ export { ExponentialBackoffRetryPolicy } from "./transport/RequestRetryPolicy";
9
+ export { RequestLogger } from "./transport/RequestLogger";
10
+ export { TLSConfiguration } from "./transport/TLSConfiguration";
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Request context for authentication
3
+ */
4
+ export interface RequestContext {
5
+ path: string;
6
+ method: string;
7
+ }
8
+
9
+ /**
10
+ * Authentication strategy interface
11
+ * Provides abstraction for different authentication header strategies
12
+ */
13
+ export interface IAuthStrategy {
14
+ /**
15
+ * Get authentication headers for a request
16
+ */
17
+ getHeaders(context: RequestContext): Record<string, string>;
18
+
19
+ /**
20
+ * Set API key
21
+ */
22
+ setApiKey(apiKey?: string): void;
23
+
24
+ /**
25
+ * Set JWT token
26
+ */
27
+ setJwt(jwt?: string): void;
28
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * HTTP Request options
3
+ */
4
+ export interface RequestOptions {
5
+ headers?: Record<string, string>;
6
+ query?: Record<string, string | number | boolean>;
7
+ timeout?: number;
8
+ }
9
+
10
+ /**
11
+ * HTTP Transport abstraction interface
12
+ * Provides a testable abstraction layer for HTTP operations
13
+ */
14
+ export interface IHttpTransport {
15
+ /**
16
+ * Perform GET request
17
+ */
18
+ get<T = any>(path: string, options?: RequestOptions): Promise<T>;
19
+
20
+ /**
21
+ * Perform POST request
22
+ */
23
+ post<T = any>(path: string, body?: any, options?: RequestOptions): Promise<T>;
24
+
25
+ /**
26
+ * Perform PUT request
27
+ */
28
+ put<T = any>(path: string, body?: any, options?: RequestOptions): Promise<T>;
29
+
30
+ /**
31
+ * Perform DELETE request
32
+ */
33
+ delete<T = any>(path: string, options?: RequestOptions): Promise<T>;
34
+
35
+ /**
36
+ * Upload file using multipart/form-data
37
+ */
38
+ uploadFile<T = any>(
39
+ path: string,
40
+ formData: FormData,
41
+ options?: { timeout?: number }
42
+ ): Promise<T>;
43
+
44
+ /**
45
+ * Get binary response (returns Response object for streaming)
46
+ */
47
+ getBinary(path: string): Promise<Response>;
48
+
49
+ /**
50
+ * Get base URL
51
+ */
52
+ getBaseURL(): string;
53
+
54
+ /**
55
+ * Get API key
56
+ */
57
+ getApiKey(): string | undefined;
58
+
59
+ /**
60
+ * Get current token (JWT or API key)
61
+ */
62
+ getToken(): string | undefined;
63
+
64
+ /**
65
+ * Set API key for authentication
66
+ */
67
+ setApiKey(apiKey?: string): void;
68
+
69
+ /**
70
+ * Set JWT token for authentication
71
+ */
72
+ setJwt(jwt?: string): void;
73
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Retry policy interface
3
+ * Provides abstraction for retry logic and backoff strategies
4
+ */
5
+ export interface IRetryPolicy {
6
+ /**
7
+ * Determine if request should be retried
8
+ */
9
+ shouldRetry(error: any, attempt: number): boolean;
10
+
11
+ /**
12
+ * Get delay before next retry attempt (in milliseconds)
13
+ */
14
+ getDelay(attempt: number): number;
15
+
16
+ /**
17
+ * Get maximum number of retry attempts
18
+ */
19
+ getMaxRetries(): number;
20
+ }