vesant-sdk 1.2.0 → 1.3.1
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/{client-BWp5FI3x.d.ts → client-B6fUFAUM.d.mts} +2 -1
- package/dist/{client-BIfLMfuC.d.mts → client-DoczGA6L.d.ts} +2 -1
- package/dist/client-DzElM7u-.d.mts +238 -0
- package/dist/client-DzElM7u-.d.ts +238 -0
- package/dist/compliance/index.d.mts +5 -4
- package/dist/compliance/index.d.ts +5 -4
- package/dist/compliance/index.js +306 -98
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +306 -98
- package/dist/compliance/index.mjs.map +1 -1
- package/dist/decisions/index.d.mts +100 -0
- package/dist/decisions/index.d.ts +100 -0
- package/dist/decisions/index.js +607 -0
- package/dist/decisions/index.js.map +1 -0
- package/dist/decisions/index.mjs +605 -0
- package/dist/decisions/index.mjs.map +1 -0
- package/dist/geolocation/index.d.mts +4 -3
- package/dist/geolocation/index.d.ts +4 -3
- package/dist/geolocation/index.js +306 -98
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +306 -98
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index.d.mts +15 -7
- package/dist/index.d.ts +15 -7
- package/dist/index.js +676 -90
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +667 -91
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +4 -3
- package/dist/kyc/core.d.ts +4 -3
- package/dist/kyc/core.js +284 -29
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +284 -29
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +46 -3
- package/dist/kyc/index.d.ts +46 -3
- package/dist/kyc/index.js +284 -29
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +284 -29
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +9 -5
- package/dist/react.d.ts +9 -5
- package/dist/react.js +422 -99
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +322 -4
- package/dist/react.mjs.map +1 -1
- package/dist/risk-profile/index.d.mts +4 -4
- package/dist/risk-profile/index.d.ts +4 -4
- package/dist/risk-profile/index.js +249 -29
- package/dist/risk-profile/index.js.map +1 -1
- package/dist/risk-profile/index.mjs +249 -29
- package/dist/risk-profile/index.mjs.map +1 -1
- package/dist/scores/index.d.mts +96 -0
- package/dist/scores/index.d.ts +96 -0
- package/dist/scores/index.js +594 -0
- package/dist/scores/index.js.map +1 -0
- package/dist/scores/index.mjs +591 -0
- package/dist/scores/index.mjs.map +1 -0
- package/dist/{types-DfHLp_tz.d.ts → types-DLC7Sfy5.d.ts} +1 -1
- package/dist/types-DZHongaK.d.mts +61 -0
- package/dist/types-DZHongaK.d.ts +61 -0
- package/dist/{types-DKCQN4C5.d.mts → types-jaLuzruy.d.mts} +1 -1
- package/dist/webhooks/index.d.mts +176 -0
- package/dist/webhooks/index.d.ts +176 -0
- package/dist/webhooks/index.js +193 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/index.mjs +188 -0
- package/dist/webhooks/index.mjs.map +1 -0
- package/package.json +25 -2
- package/dist/types-BpKxSXGF.d.mts +0 -177
- package/dist/types-BpKxSXGF.d.ts +0 -177
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var CGSError = class _CGSError extends Error {
|
|
3
|
+
constructor(message, code, statusCode, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = "CGSError";
|
|
9
|
+
Object.setPrototypeOf(this, _CGSError.prototype);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var NetworkError = class _NetworkError extends CGSError {
|
|
13
|
+
constructor(message, originalError) {
|
|
14
|
+
super(message, "NETWORK_ERROR", void 0, { originalError });
|
|
15
|
+
this.originalError = originalError;
|
|
16
|
+
this.name = "NetworkError";
|
|
17
|
+
Object.setPrototypeOf(this, _NetworkError.prototype);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var ValidationError = class _ValidationError extends CGSError {
|
|
21
|
+
constructor(message, fields) {
|
|
22
|
+
super(message, "VALIDATION_ERROR", 400, { fields });
|
|
23
|
+
this.name = "ValidationError";
|
|
24
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var ServiceUnavailableError = class _ServiceUnavailableError extends CGSError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(`${message} is unavailable`, "SERVICE_UNAVAILABLE", 503, { service: message });
|
|
30
|
+
this.name = "ServiceUnavailableError";
|
|
31
|
+
Object.setPrototypeOf(this, _ServiceUnavailableError.prototype);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var AuthenticationError = class _AuthenticationError extends CGSError {
|
|
35
|
+
constructor(message = "Authentication failed") {
|
|
36
|
+
super(message, "AUTHENTICATION_ERROR", 401);
|
|
37
|
+
this.name = "AuthenticationError";
|
|
38
|
+
Object.setPrototypeOf(this, _AuthenticationError.prototype);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var RateLimitError = class _RateLimitError extends CGSError {
|
|
42
|
+
constructor(retryAfter) {
|
|
43
|
+
super("Rate limit exceeded", "RATE_LIMIT_EXCEEDED", 429, { retryAfter });
|
|
44
|
+
this.retryAfter = retryAfter;
|
|
45
|
+
this.name = "RateLimitError";
|
|
46
|
+
Object.setPrototypeOf(this, _RateLimitError.prototype);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var TimeoutError = class _TimeoutError extends CGSError {
|
|
50
|
+
constructor(timeout) {
|
|
51
|
+
super(`Request timeout after ${timeout}ms`, "TIMEOUT", 408, { timeout });
|
|
52
|
+
this.timeout = timeout;
|
|
53
|
+
this.name = "TimeoutError";
|
|
54
|
+
Object.setPrototypeOf(this, _TimeoutError.prototype);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var CircuitBreakerOpenError = class _CircuitBreakerOpenError extends CGSError {
|
|
58
|
+
constructor() {
|
|
59
|
+
super("Circuit breaker is open \u2014 requests are temporarily blocked", "CIRCUIT_BREAKER_OPEN", 503);
|
|
60
|
+
this.name = "CircuitBreakerOpenError";
|
|
61
|
+
Object.setPrototypeOf(this, _CircuitBreakerOpenError.prototype);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/core/circuit-breaker.ts
|
|
66
|
+
var CircuitBreaker = class {
|
|
67
|
+
constructor(config = {}) {
|
|
68
|
+
this.state = "closed";
|
|
69
|
+
this.failures = 0;
|
|
70
|
+
this.successes = 0;
|
|
71
|
+
this.lastFailureTime = null;
|
|
72
|
+
this.failureThreshold = config.failureThreshold ?? 5;
|
|
73
|
+
this.resetTimeout = config.resetTimeout ?? 3e4;
|
|
74
|
+
this.successThreshold = config.successThreshold ?? 1;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a request can proceed through the circuit breaker.
|
|
78
|
+
*/
|
|
79
|
+
canExecute() {
|
|
80
|
+
if (this.state === "closed") {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (this.state === "open") {
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
if (this.lastFailureTime && now - this.lastFailureTime >= this.resetTimeout) {
|
|
86
|
+
this.state = "half-open";
|
|
87
|
+
this.successes = 0;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Record a successful request.
|
|
96
|
+
*/
|
|
97
|
+
onSuccess() {
|
|
98
|
+
if (this.state === "half-open") {
|
|
99
|
+
this.successes++;
|
|
100
|
+
if (this.successes >= this.successThreshold) {
|
|
101
|
+
this.state = "closed";
|
|
102
|
+
this.failures = 0;
|
|
103
|
+
this.successes = 0;
|
|
104
|
+
this.lastFailureTime = null;
|
|
105
|
+
}
|
|
106
|
+
} else if (this.state === "closed") {
|
|
107
|
+
this.failures = 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Record a failed request.
|
|
112
|
+
*/
|
|
113
|
+
onFailure() {
|
|
114
|
+
this.failures++;
|
|
115
|
+
this.lastFailureTime = Date.now();
|
|
116
|
+
if (this.state === "half-open") {
|
|
117
|
+
this.state = "open";
|
|
118
|
+
this.successes = 0;
|
|
119
|
+
} else if (this.state === "closed" && this.failures >= this.failureThreshold) {
|
|
120
|
+
this.state = "open";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get current circuit breaker status.
|
|
125
|
+
*/
|
|
126
|
+
getStatus() {
|
|
127
|
+
return {
|
|
128
|
+
state: this.state,
|
|
129
|
+
failures: this.failures,
|
|
130
|
+
successes: this.successes,
|
|
131
|
+
lastFailureTime: this.lastFailureTime,
|
|
132
|
+
nextRetryTime: this.state === "open" && this.lastFailureTime ? this.lastFailureTime + this.resetTimeout : null
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Reset the circuit breaker to its initial closed state.
|
|
137
|
+
*/
|
|
138
|
+
reset() {
|
|
139
|
+
this.state = "closed";
|
|
140
|
+
this.failures = 0;
|
|
141
|
+
this.successes = 0;
|
|
142
|
+
this.lastFailureTime = null;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// src/core/rate-limiter.ts
|
|
147
|
+
var RateLimitTracker = class {
|
|
148
|
+
constructor() {
|
|
149
|
+
this.limit = null;
|
|
150
|
+
this.remaining = null;
|
|
151
|
+
this.reset = null;
|
|
152
|
+
this.retryAfter = null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract rate limit information from response headers.
|
|
156
|
+
*/
|
|
157
|
+
updateFromHeaders(headers) {
|
|
158
|
+
const limit = headers.get("x-ratelimit-limit");
|
|
159
|
+
const remaining = headers.get("x-ratelimit-remaining");
|
|
160
|
+
const reset = headers.get("x-ratelimit-reset");
|
|
161
|
+
const retryAfter = headers.get("retry-after");
|
|
162
|
+
if (limit !== null) this.limit = parseInt(limit, 10);
|
|
163
|
+
if (remaining !== null) this.remaining = parseInt(remaining, 10);
|
|
164
|
+
if (reset !== null) this.reset = parseInt(reset, 10);
|
|
165
|
+
if (retryAfter !== null) this.retryAfter = parseInt(retryAfter, 10);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Check if the rate limit has been exceeded based on tracked headers.
|
|
169
|
+
*/
|
|
170
|
+
isLimitExceeded() {
|
|
171
|
+
if (this.remaining !== null && this.remaining <= 0) {
|
|
172
|
+
if (this.reset !== null) {
|
|
173
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
174
|
+
if (now >= this.reset) {
|
|
175
|
+
this.remaining = null;
|
|
176
|
+
this.reset = null;
|
|
177
|
+
this.retryAfter = null;
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get current rate limit status.
|
|
187
|
+
*/
|
|
188
|
+
getStatus() {
|
|
189
|
+
return {
|
|
190
|
+
limit: this.limit,
|
|
191
|
+
remaining: this.remaining,
|
|
192
|
+
reset: this.reset,
|
|
193
|
+
retryAfter: this.retryAfter
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// src/core/logger.ts
|
|
199
|
+
function createConsoleLogger() {
|
|
200
|
+
return {
|
|
201
|
+
debug(message, meta) {
|
|
202
|
+
console.log(`[CGS SDK] ${message}`, meta !== void 0 ? meta : "");
|
|
203
|
+
},
|
|
204
|
+
info(message, meta) {
|
|
205
|
+
console.info(`[CGS SDK] ${message}`, meta !== void 0 ? meta : "");
|
|
206
|
+
},
|
|
207
|
+
warn(message, meta) {
|
|
208
|
+
console.warn(`[CGS SDK] ${message}`, meta !== void 0 ? meta : "");
|
|
209
|
+
},
|
|
210
|
+
error(message, meta) {
|
|
211
|
+
console.error(`[CGS SDK] ${message}`, meta !== void 0 ? meta : "");
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/core/version.ts
|
|
217
|
+
var SDK_VERSION = "1.3.0";
|
|
218
|
+
|
|
219
|
+
// src/shared/browser-utils.ts
|
|
220
|
+
function generateUUID() {
|
|
221
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
222
|
+
return crypto.randomUUID();
|
|
223
|
+
}
|
|
224
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
225
|
+
const r = Math.random() * 16 | 0;
|
|
226
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
227
|
+
return v.toString(16);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/core/client.ts
|
|
232
|
+
var BaseClient = class {
|
|
233
|
+
constructor(config) {
|
|
234
|
+
this.circuitBreaker = null;
|
|
235
|
+
this.rateLimitTracker = null;
|
|
236
|
+
if (!config.baseURL?.trim()) {
|
|
237
|
+
throw new ValidationError("baseURL is required and must be a non-empty string", ["baseURL"]);
|
|
238
|
+
}
|
|
239
|
+
if (!config.tenantId?.trim()) {
|
|
240
|
+
throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
|
|
241
|
+
}
|
|
242
|
+
this.interceptors = config.interceptors || [];
|
|
243
|
+
this.logger = config.logger || createConsoleLogger();
|
|
244
|
+
this.config = {
|
|
245
|
+
...config,
|
|
246
|
+
apiKey: config.apiKey || "",
|
|
247
|
+
headers: config.headers || {},
|
|
248
|
+
timeout: config.timeout || 1e4,
|
|
249
|
+
retries: config.retries || 3,
|
|
250
|
+
debug: config.debug || false,
|
|
251
|
+
interceptors: this.interceptors,
|
|
252
|
+
logger: this.logger
|
|
253
|
+
};
|
|
254
|
+
if (config.circuitBreaker) {
|
|
255
|
+
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
|
|
256
|
+
}
|
|
257
|
+
if (config.enableRateLimitTracking) {
|
|
258
|
+
this.rateLimitTracker = new RateLimitTracker();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Make an HTTP request with timeout and error handling
|
|
263
|
+
*/
|
|
264
|
+
async request(endpoint, options = {}, serviceURL, requestOptions) {
|
|
265
|
+
const requestId = generateUUID();
|
|
266
|
+
if (this.circuitBreaker && !this.circuitBreaker.canExecute()) {
|
|
267
|
+
const error = new CircuitBreakerOpenError();
|
|
268
|
+
error.requestId = requestId;
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
if (this.rateLimitTracker && this.rateLimitTracker.isLimitExceeded()) {
|
|
272
|
+
const status = this.rateLimitTracker.getStatus();
|
|
273
|
+
const error = new RateLimitError(status.retryAfter ?? void 0);
|
|
274
|
+
error.requestId = requestId;
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
const baseURL = this.config.environment === "sandbox" && this.config.sandboxBaseURL ? this.config.sandboxBaseURL : serviceURL || this.config.baseURL;
|
|
278
|
+
const url = `${serviceURL || baseURL}${endpoint}`;
|
|
279
|
+
const headers = {
|
|
280
|
+
"Content-Type": "application/json",
|
|
281
|
+
"X-Tenant-ID": this.config.tenantId,
|
|
282
|
+
"X-SDK-Version": `vesant-sdk-ts/${SDK_VERSION}`,
|
|
283
|
+
"X-Request-ID": requestId,
|
|
284
|
+
...this.config.headers,
|
|
285
|
+
...options.headers || {}
|
|
286
|
+
};
|
|
287
|
+
if (this.config.apiKey) {
|
|
288
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
289
|
+
}
|
|
290
|
+
if (this.config.environment === "sandbox") {
|
|
291
|
+
headers["X-Sandbox"] = "true";
|
|
292
|
+
}
|
|
293
|
+
const method = (options.method || "GET").toUpperCase();
|
|
294
|
+
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
295
|
+
headers["Idempotency-Key"] = requestOptions?.idempotencyKey || generateUUID();
|
|
296
|
+
}
|
|
297
|
+
const controller = new AbortController();
|
|
298
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
299
|
+
if (requestOptions?.signal) {
|
|
300
|
+
if (requestOptions.signal.aborted) {
|
|
301
|
+
controller.abort();
|
|
302
|
+
} else {
|
|
303
|
+
requestOptions.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
let finalOptions = { ...options, headers };
|
|
308
|
+
for (const interceptor of this.interceptors) {
|
|
309
|
+
if (interceptor.onRequest) {
|
|
310
|
+
finalOptions = await interceptor.onRequest(url, finalOptions);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (this.config.debug) {
|
|
314
|
+
this.logger.debug(`${finalOptions.method || "GET"} ${url}`, {
|
|
315
|
+
headers: finalOptions.headers,
|
|
316
|
+
body: finalOptions.body
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
const response = await fetch(url, {
|
|
320
|
+
...finalOptions,
|
|
321
|
+
signal: controller.signal
|
|
322
|
+
});
|
|
323
|
+
clearTimeout(timeoutId);
|
|
324
|
+
if (this.rateLimitTracker) {
|
|
325
|
+
this.rateLimitTracker.updateFromHeaders(response.headers);
|
|
326
|
+
}
|
|
327
|
+
let data;
|
|
328
|
+
try {
|
|
329
|
+
data = await response.json();
|
|
330
|
+
} catch {
|
|
331
|
+
if (!response.ok) {
|
|
332
|
+
this.handleErrorResponse(response.status, {
|
|
333
|
+
error: `HTTP ${response.status}`,
|
|
334
|
+
message: response.statusText
|
|
335
|
+
}, requestId);
|
|
336
|
+
}
|
|
337
|
+
this.circuitBreaker?.onSuccess();
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
if (!response.ok) {
|
|
341
|
+
this.handleErrorResponse(response.status, data || {}, requestId);
|
|
342
|
+
}
|
|
343
|
+
this.circuitBreaker?.onSuccess();
|
|
344
|
+
let result = data;
|
|
345
|
+
for (const interceptor of this.interceptors) {
|
|
346
|
+
if (interceptor.onResponse) {
|
|
347
|
+
result = await interceptor.onResponse(url, result);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (this.config.debug) {
|
|
351
|
+
this.logger.debug("Response:", { data: result });
|
|
352
|
+
}
|
|
353
|
+
return result;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
clearTimeout(timeoutId);
|
|
356
|
+
if (error instanceof CGSError && error.statusCode && error.statusCode >= 500) {
|
|
357
|
+
this.circuitBreaker?.onFailure();
|
|
358
|
+
} else if (error instanceof NetworkError || error instanceof TimeoutError) {
|
|
359
|
+
this.circuitBreaker?.onFailure();
|
|
360
|
+
}
|
|
361
|
+
if (error instanceof CGSError && !error.requestId) {
|
|
362
|
+
error.requestId = requestId;
|
|
363
|
+
}
|
|
364
|
+
if (error instanceof Error) {
|
|
365
|
+
for (const interceptor of this.interceptors) {
|
|
366
|
+
if (interceptor.onError) {
|
|
367
|
+
await interceptor.onError(url, error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (error instanceof Error) {
|
|
372
|
+
if (error.name === "AbortError") {
|
|
373
|
+
if (requestOptions?.signal?.aborted) {
|
|
374
|
+
const abortError = new CGSError("Request aborted", "REQUEST_ABORTED");
|
|
375
|
+
abortError.requestId = requestId;
|
|
376
|
+
throw abortError;
|
|
377
|
+
}
|
|
378
|
+
this.circuitBreaker?.onFailure();
|
|
379
|
+
const timeoutError = new TimeoutError(this.config.timeout);
|
|
380
|
+
timeoutError.requestId = requestId;
|
|
381
|
+
throw timeoutError;
|
|
382
|
+
}
|
|
383
|
+
if (error instanceof CGSError) {
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const networkError = new NetworkError("Network request failed", error);
|
|
388
|
+
networkError.requestId = requestId;
|
|
389
|
+
this.circuitBreaker?.onFailure();
|
|
390
|
+
throw networkError;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Make an HTTP request with retry logic
|
|
395
|
+
*/
|
|
396
|
+
async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
|
|
397
|
+
let lastError;
|
|
398
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
399
|
+
try {
|
|
400
|
+
return await this.request(endpoint, options, serviceURL, requestOptions);
|
|
401
|
+
} catch (error) {
|
|
402
|
+
lastError = error instanceof Error ? error : new Error("Unknown error");
|
|
403
|
+
if (requestOptions?.signal?.aborted) {
|
|
404
|
+
throw lastError;
|
|
405
|
+
}
|
|
406
|
+
if (lastError instanceof CGSError && lastError.statusCode && lastError.statusCode >= 400 && lastError.statusCode < 500 && lastError.statusCode !== 429) {
|
|
407
|
+
throw lastError;
|
|
408
|
+
}
|
|
409
|
+
if (attempt === retries) {
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
let delay;
|
|
413
|
+
if (lastError instanceof RateLimitError && lastError.retryAfter) {
|
|
414
|
+
delay = lastError.retryAfter * 1e3;
|
|
415
|
+
} else {
|
|
416
|
+
delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 1e3, 1e4);
|
|
417
|
+
}
|
|
418
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
419
|
+
if (this.config.debug) {
|
|
420
|
+
this.logger.debug(`Retry attempt ${attempt + 1}/${retries} after ${delay.toFixed(0)}ms`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
throw new NetworkError(`Request failed after ${retries} retries`, lastError);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Handle error responses from API
|
|
428
|
+
*/
|
|
429
|
+
handleErrorResponse(status, data, requestId) {
|
|
430
|
+
const message = data.error || data.message || `HTTP ${status}`;
|
|
431
|
+
const createError = () => {
|
|
432
|
+
switch (status) {
|
|
433
|
+
case 400:
|
|
434
|
+
return new CGSError(message, "BAD_REQUEST", 400, data);
|
|
435
|
+
case 401:
|
|
436
|
+
return new AuthenticationError(message);
|
|
437
|
+
case 403:
|
|
438
|
+
return new CGSError(message, "FORBIDDEN", 403, data);
|
|
439
|
+
case 404:
|
|
440
|
+
return new CGSError(message, "NOT_FOUND", 404, data);
|
|
441
|
+
case 429: {
|
|
442
|
+
const retryAfter = data.retry_after || data.retryAfter;
|
|
443
|
+
return new RateLimitError(retryAfter);
|
|
444
|
+
}
|
|
445
|
+
case 500:
|
|
446
|
+
case 502:
|
|
447
|
+
case 503:
|
|
448
|
+
case 504:
|
|
449
|
+
return new ServiceUnavailableError(message);
|
|
450
|
+
default:
|
|
451
|
+
return new CGSError(message, "UNKNOWN_ERROR", status, data);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
const error = createError();
|
|
455
|
+
if (requestId) {
|
|
456
|
+
error.requestId = requestId;
|
|
457
|
+
}
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Build query string from parameters
|
|
462
|
+
*/
|
|
463
|
+
buildQueryString(params) {
|
|
464
|
+
const query = new URLSearchParams();
|
|
465
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
466
|
+
if (value !== void 0 && value !== null) {
|
|
467
|
+
if (Array.isArray(value)) {
|
|
468
|
+
value.forEach((item) => query.append(key, String(item)));
|
|
469
|
+
} else {
|
|
470
|
+
query.append(key, String(value));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
const queryString = query.toString();
|
|
475
|
+
return queryString ? `?${queryString}` : "";
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Update client configuration
|
|
479
|
+
*/
|
|
480
|
+
updateConfig(config) {
|
|
481
|
+
this.config = {
|
|
482
|
+
...this.config,
|
|
483
|
+
...config,
|
|
484
|
+
headers: {
|
|
485
|
+
...this.config.headers,
|
|
486
|
+
...config.headers || {}
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
if (config.logger) {
|
|
490
|
+
this.logger = config.logger;
|
|
491
|
+
}
|
|
492
|
+
if (config.interceptors) {
|
|
493
|
+
this.interceptors = config.interceptors;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Get current configuration (readonly)
|
|
498
|
+
*/
|
|
499
|
+
getConfig() {
|
|
500
|
+
return { ...this.config };
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Get rate limit status from tracked response headers.
|
|
504
|
+
*/
|
|
505
|
+
getRateLimitStatus() {
|
|
506
|
+
return this.rateLimitTracker?.getStatus() ?? null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Get circuit breaker status.
|
|
510
|
+
*/
|
|
511
|
+
getCircuitBreakerStatus() {
|
|
512
|
+
return this.circuitBreaker?.getStatus() ?? null;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Health check endpoint
|
|
516
|
+
*/
|
|
517
|
+
async healthCheck() {
|
|
518
|
+
return this.request("/api/v1/health");
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
// src/decisions/client.ts
|
|
523
|
+
var DecisionsClient = class extends BaseClient {
|
|
524
|
+
/**
|
|
525
|
+
* Record a new decision for a customer.
|
|
526
|
+
*/
|
|
527
|
+
async recordDecision(request, requestOptions) {
|
|
528
|
+
return this.requestWithRetry(
|
|
529
|
+
"/api/v1/decisions",
|
|
530
|
+
{ method: "POST", body: JSON.stringify(request) },
|
|
531
|
+
void 0,
|
|
532
|
+
void 0,
|
|
533
|
+
requestOptions
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get decisions for a customer.
|
|
538
|
+
*/
|
|
539
|
+
async getDecisions(customerId, filters, requestOptions) {
|
|
540
|
+
const queryString = this.buildQueryString({
|
|
541
|
+
customer_id: customerId,
|
|
542
|
+
...filters
|
|
543
|
+
});
|
|
544
|
+
return this.requestWithRetry(
|
|
545
|
+
`/api/v1/decisions${queryString}`,
|
|
546
|
+
{ method: "GET" },
|
|
547
|
+
void 0,
|
|
548
|
+
void 0,
|
|
549
|
+
requestOptions
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Get a specific decision by ID.
|
|
554
|
+
*/
|
|
555
|
+
async getDecision(decisionId, requestOptions) {
|
|
556
|
+
return this.requestWithRetry(
|
|
557
|
+
`/api/v1/decisions/${encodeURIComponent(decisionId)}`,
|
|
558
|
+
{ method: "GET" },
|
|
559
|
+
void 0,
|
|
560
|
+
void 0,
|
|
561
|
+
requestOptions
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Apply a label to a customer.
|
|
566
|
+
*/
|
|
567
|
+
async applyLabel(request, requestOptions) {
|
|
568
|
+
return this.requestWithRetry(
|
|
569
|
+
"/api/v1/labels",
|
|
570
|
+
{ method: "POST", body: JSON.stringify(request) },
|
|
571
|
+
void 0,
|
|
572
|
+
void 0,
|
|
573
|
+
requestOptions
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Remove a label from a customer.
|
|
578
|
+
*/
|
|
579
|
+
async removeLabel(customerId, requestOptions) {
|
|
580
|
+
return this.requestWithRetry(
|
|
581
|
+
`/api/v1/labels/${encodeURIComponent(customerId)}`,
|
|
582
|
+
{ method: "DELETE" },
|
|
583
|
+
void 0,
|
|
584
|
+
void 0,
|
|
585
|
+
requestOptions
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Get labels for a customer.
|
|
590
|
+
*/
|
|
591
|
+
async getLabels(customerId, requestOptions) {
|
|
592
|
+
const queryString = this.buildQueryString({ customer_id: customerId });
|
|
593
|
+
return this.requestWithRetry(
|
|
594
|
+
`/api/v1/labels${queryString}`,
|
|
595
|
+
{ method: "GET" },
|
|
596
|
+
void 0,
|
|
597
|
+
void 0,
|
|
598
|
+
requestOptions
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
export { DecisionsClient };
|
|
604
|
+
//# sourceMappingURL=index.mjs.map
|
|
605
|
+
//# sourceMappingURL=index.mjs.map
|