lago-javascript-client 1.45.0 → 1.46.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.
- package/esm/mod.d.ts +11 -1
- package/esm/mod.js +12 -1
- package/esm/openapi/client.d.ts +21 -7
- package/esm/rate_limit_error.d.ts +10 -0
- package/esm/rate_limit_error.js +19 -0
- package/esm/rate_limit_fetch.d.ts +16 -0
- package/esm/rate_limit_fetch.js +76 -0
- package/esm/rate_limit_headers.d.ts +12 -0
- package/esm/rate_limit_headers.js +20 -0
- package/package.json +1 -1
- package/script/mod.d.ts +11 -1
- package/script/mod.js +16 -2
- package/script/openapi/client.d.ts +21 -7
- package/script/rate_limit_error.d.ts +10 -0
- package/script/rate_limit_error.js +23 -0
- package/script/rate_limit_fetch.d.ts +16 -0
- package/script/rate_limit_fetch.js +80 -0
- package/script/rate_limit_headers.d.ts +12 -0
- package/script/rate_limit_headers.js +24 -0
package/esm/mod.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Api, ApiConfig, HttpResponse } from "./openapi/client.js";
|
|
2
|
-
|
|
2
|
+
import type { RateLimitFetchConfig } from "./rate_limit_fetch.js";
|
|
3
|
+
export interface LagoClientConfig extends ApiConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Rate limit retry configuration
|
|
6
|
+
*/
|
|
7
|
+
rateLimitRetry?: RateLimitFetchConfig;
|
|
8
|
+
}
|
|
9
|
+
export declare const Client: (apiKey: string, apiConfig?: LagoClientConfig) => Api<unknown>;
|
|
3
10
|
type ExtractLagoError<E> = E extends (...args: any) => Promise<HttpResponse<infer T, infer P>> ? P : never;
|
|
4
11
|
export declare function getLagoError<T>(error: any): Promise<ExtractLagoError<T>>;
|
|
12
|
+
export { LagoRateLimitError } from "./rate_limit_error.js";
|
|
13
|
+
export { parseRateLimitHeaders, type RateLimitHeaders } from "./rate_limit_headers.js";
|
|
14
|
+
export { createRateLimitFetch, type RateLimitFetchConfig } from "./rate_limit_fetch.js";
|
|
5
15
|
export * from "./openapi/client.js";
|
package/esm/mod.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
2
|
import { Api } from "./openapi/client.js";
|
|
3
|
+
import { createRateLimitFetch } from "./rate_limit_fetch.js";
|
|
3
4
|
export const Client = (apiKey, apiConfig) => {
|
|
5
|
+
const { rateLimitRetry, ...restConfig } = apiConfig ?? {};
|
|
6
|
+
// Create rate-limit-aware fetch if configured
|
|
7
|
+
const customFetch = rateLimitRetry
|
|
8
|
+
? createRateLimitFetch((restConfig?.customFetch ?? globalThis.fetch), rateLimitRetry)
|
|
9
|
+
: restConfig?.customFetch;
|
|
4
10
|
const api = new Api({
|
|
5
11
|
securityWorker: (apiKey) => apiKey ? { headers: { Authorization: `Bearer ${apiKey}` } } : {},
|
|
6
12
|
// Cloudflare Workers doesn't support some options like credentials so need to override default
|
|
7
13
|
baseApiParams: {
|
|
8
14
|
redirect: "follow",
|
|
9
15
|
},
|
|
10
|
-
...
|
|
16
|
+
...restConfig,
|
|
17
|
+
...(customFetch && { customFetch }),
|
|
11
18
|
});
|
|
12
19
|
api.setSecurityData(apiKey);
|
|
13
20
|
return api;
|
|
@@ -30,4 +37,8 @@ export async function getLagoError(error) {
|
|
|
30
37
|
}
|
|
31
38
|
throw new Error(error);
|
|
32
39
|
}
|
|
40
|
+
// Rate limit exports
|
|
41
|
+
export { LagoRateLimitError } from "./rate_limit_error.js";
|
|
42
|
+
export { parseRateLimitHeaders } from "./rate_limit_headers.js";
|
|
43
|
+
export { createRateLimitFetch } from "./rate_limit_fetch.js";
|
|
33
44
|
export * from "./openapi/client.js";
|
package/esm/openapi/client.d.ts
CHANGED
|
@@ -2226,6 +2226,16 @@ export interface FeeObject {
|
|
|
2226
2226
|
* @example "0.0"
|
|
2227
2227
|
*/
|
|
2228
2228
|
precise_coupons_amount_cents?: string;
|
|
2229
|
+
/**
|
|
2230
|
+
* The sub total of the fee excluding taxes, in cents.
|
|
2231
|
+
* @example 100
|
|
2232
|
+
*/
|
|
2233
|
+
sub_total_excluding_taxes_amount_cents: number;
|
|
2234
|
+
/**
|
|
2235
|
+
* The sub total of the fee excluding taxes, with precision.
|
|
2236
|
+
* @example "100.0"
|
|
2237
|
+
*/
|
|
2238
|
+
sub_total_excluding_taxes_precise_amount_cents: string;
|
|
2229
2239
|
/** List of all unit amount details for calculating the fee. */
|
|
2230
2240
|
amount_details?: FeeAmountDetails;
|
|
2231
2241
|
/**
|
|
@@ -3837,18 +3847,20 @@ export interface AppliedInvoiceCustomSectionObject {
|
|
|
3837
3847
|
* @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
3838
3848
|
*/
|
|
3839
3849
|
lago_id: string;
|
|
3840
|
-
/**
|
|
3841
|
-
* Unique identifier of the invoice custom section that was applied.
|
|
3842
|
-
* @format uuid
|
|
3843
|
-
* @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
3844
|
-
*/
|
|
3845
|
-
invoice_custom_section_id: string;
|
|
3846
3850
|
/**
|
|
3847
3851
|
* The date of the applied invoice custom section creation, represented in ISO 8601 datetime format and expressed in Coordinated Universal Time (UTC).
|
|
3848
3852
|
* @format date-time
|
|
3849
3853
|
* @example "2023-07-06T14:35:58Z"
|
|
3850
3854
|
*/
|
|
3851
3855
|
created_at: string;
|
|
3856
|
+
/**
|
|
3857
|
+
* Unique identifier of the invoice custom section that was applied.
|
|
3858
|
+
* @deprecated
|
|
3859
|
+
* @format uuid
|
|
3860
|
+
* @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
3861
|
+
*/
|
|
3862
|
+
invoice_custom_section_id?: string;
|
|
3863
|
+
invoice_custom_section: InvoiceCustomSectionObject;
|
|
3852
3864
|
}
|
|
3853
3865
|
/** Reference to a specific payment method for processing the payment. */
|
|
3854
3866
|
export interface PaymentMethodReference {
|
|
@@ -10316,7 +10328,9 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
|
|
|
10316
10328
|
* @request POST:/customers/{external_customer_id}/checkout_url
|
|
10317
10329
|
* @secure
|
|
10318
10330
|
*/
|
|
10319
|
-
generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<
|
|
10331
|
+
generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<{
|
|
10332
|
+
customer: CustomerCheckoutUrl;
|
|
10333
|
+
}, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorNotFound>>;
|
|
10320
10334
|
/**
|
|
10321
10335
|
* @description This endpoint retrieves all payment methods of a Customer.
|
|
10322
10336
|
*
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error class for rate limit (HTTP 429) responses
|
|
3
|
+
*/
|
|
4
|
+
export declare class LagoRateLimitError extends Error {
|
|
5
|
+
readonly limit: number;
|
|
6
|
+
readonly remaining: number;
|
|
7
|
+
readonly reset: number;
|
|
8
|
+
readonly retryAfter: number;
|
|
9
|
+
constructor(limit: number, remaining: number, reset: number);
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error class for rate limit (HTTP 429) responses
|
|
3
|
+
*/
|
|
4
|
+
export class LagoRateLimitError extends Error {
|
|
5
|
+
limit;
|
|
6
|
+
remaining;
|
|
7
|
+
reset; // seconds until window resets
|
|
8
|
+
retryAfter; // milliseconds to wait before retrying
|
|
9
|
+
constructor(limit, remaining, reset) {
|
|
10
|
+
super(`Rate limit exceeded. Limit: ${limit}, Remaining: ${remaining}, Reset in: ${reset}s`);
|
|
11
|
+
this.name = "LagoRateLimitError";
|
|
12
|
+
this.limit = limit;
|
|
13
|
+
this.remaining = remaining;
|
|
14
|
+
this.reset = reset;
|
|
15
|
+
this.retryAfter = reset * 1000; // Convert seconds to milliseconds
|
|
16
|
+
// Maintain proper prototype chain for instanceof checks
|
|
17
|
+
Object.setPrototypeOf(this, LagoRateLimitError.prototype);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for rate limit fetch behavior
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitFetchConfig {
|
|
5
|
+
/** Maximum number of retries on 429 (default: 3) */
|
|
6
|
+
maxRetries?: number;
|
|
7
|
+
/** Whether to automatically retry on rate limit (default: true) */
|
|
8
|
+
retryOnRateLimit?: boolean;
|
|
9
|
+
/** Maximum delay in milliseconds before a retry (default: 20000) */
|
|
10
|
+
maxRetryDelay?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a fetch wrapper that handles rate limiting with automatic retry
|
|
14
|
+
* Compatible with both Node.js fetch and browser fetch APIs
|
|
15
|
+
*/
|
|
16
|
+
export declare function createRateLimitFetch(baseFetch: typeof fetch, config?: RateLimitFetchConfig): typeof fetch;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { LagoRateLimitError } from "./rate_limit_error.js";
|
|
2
|
+
import { parseRateLimitHeaders, } from "./rate_limit_headers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a fetch wrapper that handles rate limiting with automatic retry
|
|
5
|
+
* Compatible with both Node.js fetch and browser fetch APIs
|
|
6
|
+
*/
|
|
7
|
+
export function createRateLimitFetch(baseFetch, config = {}) {
|
|
8
|
+
const maxRetries = config.maxRetries ?? 3;
|
|
9
|
+
const retryOnRateLimit = config.retryOnRateLimit ?? true;
|
|
10
|
+
const maxRetryDelay = config.maxRetryDelay ?? 20_000;
|
|
11
|
+
return async function rateLimitFetch(input, init) {
|
|
12
|
+
let lastError;
|
|
13
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await baseFetch(input, init);
|
|
16
|
+
// Handle 429 responses
|
|
17
|
+
if (response.status === 429) {
|
|
18
|
+
const headers = parseRateLimitHeaders(response.headers);
|
|
19
|
+
const limit = headers.limit ?? -1;
|
|
20
|
+
const remaining = headers.remaining ?? 0;
|
|
21
|
+
const reset = headers.reset ?? -1;
|
|
22
|
+
const error = new LagoRateLimitError(limit, remaining, reset);
|
|
23
|
+
if (!retryOnRateLimit) {
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
if (attempt === maxRetries) {
|
|
27
|
+
throw error; // Max retries reached
|
|
28
|
+
}
|
|
29
|
+
// Wait before retry
|
|
30
|
+
const waitMs = getWaitTime(error, attempt, maxRetryDelay);
|
|
31
|
+
await sleep(waitMs);
|
|
32
|
+
continue; // Retry
|
|
33
|
+
}
|
|
34
|
+
// Success or non-rate-limit error - return the response
|
|
35
|
+
return response;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
lastError = error;
|
|
39
|
+
if (!(error instanceof LagoRateLimitError)) {
|
|
40
|
+
throw error; // Not a rate limit error, rethrow immediately
|
|
41
|
+
}
|
|
42
|
+
if (attempt === maxRetries) {
|
|
43
|
+
throw error; // Max retries reached
|
|
44
|
+
}
|
|
45
|
+
// Will retry on next iteration
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw lastError;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Calculate wait time before retry
|
|
53
|
+
* Uses the exact reset time from headers if available, otherwise exponential backoff
|
|
54
|
+
*/
|
|
55
|
+
function getWaitTime(error, attempt, maxRetryDelay) {
|
|
56
|
+
let waitMs;
|
|
57
|
+
if (error.reset > 0) {
|
|
58
|
+
// Use the exact reset time from the header
|
|
59
|
+
waitMs = error.retryAfter;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, etc.
|
|
63
|
+
waitMs = 1000 * Math.pow(2, attempt);
|
|
64
|
+
}
|
|
65
|
+
// Cap at maxRetryDelay
|
|
66
|
+
waitMs = Math.min(waitMs, maxRetryDelay);
|
|
67
|
+
// Add small jitter to prevent thundering herd (max 100ms)
|
|
68
|
+
const jitter = Math.random() * 100;
|
|
69
|
+
return waitMs + jitter;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Sleep for a specified number of milliseconds
|
|
73
|
+
*/
|
|
74
|
+
function sleep(ms) {
|
|
75
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
76
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses rate limit headers from HTTP responses
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitHeaders {
|
|
5
|
+
limit: number | null;
|
|
6
|
+
remaining: number | null;
|
|
7
|
+
reset: number | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract rate limit information from response headers
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseRateLimitHeaders(headers: Headers): RateLimitHeaders;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract rate limit information from response headers
|
|
3
|
+
*/
|
|
4
|
+
export function parseRateLimitHeaders(headers) {
|
|
5
|
+
return {
|
|
6
|
+
limit: parseHeaderAsNumber(headers, "x-ratelimit-limit"),
|
|
7
|
+
remaining: parseHeaderAsNumber(headers, "x-ratelimit-remaining"),
|
|
8
|
+
reset: parseHeaderAsNumber(headers, "x-ratelimit-reset"),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Helper to parse a header value as a number
|
|
13
|
+
*/
|
|
14
|
+
function parseHeaderAsNumber(headers, headerName) {
|
|
15
|
+
const value = headers.get(headerName);
|
|
16
|
+
if (value === null)
|
|
17
|
+
return null;
|
|
18
|
+
const num = parseInt(value, 10);
|
|
19
|
+
return isNaN(num) ? null : num;
|
|
20
|
+
}
|
package/package.json
CHANGED
package/script/mod.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Api, ApiConfig, HttpResponse } from "./openapi/client.js";
|
|
2
|
-
|
|
2
|
+
import type { RateLimitFetchConfig } from "./rate_limit_fetch.js";
|
|
3
|
+
export interface LagoClientConfig extends ApiConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Rate limit retry configuration
|
|
6
|
+
*/
|
|
7
|
+
rateLimitRetry?: RateLimitFetchConfig;
|
|
8
|
+
}
|
|
9
|
+
export declare const Client: (apiKey: string, apiConfig?: LagoClientConfig) => Api<unknown>;
|
|
3
10
|
type ExtractLagoError<E> = E extends (...args: any) => Promise<HttpResponse<infer T, infer P>> ? P : never;
|
|
4
11
|
export declare function getLagoError<T>(error: any): Promise<ExtractLagoError<T>>;
|
|
12
|
+
export { LagoRateLimitError } from "./rate_limit_error.js";
|
|
13
|
+
export { parseRateLimitHeaders, type RateLimitHeaders } from "./rate_limit_headers.js";
|
|
14
|
+
export { createRateLimitFetch, type RateLimitFetchConfig } from "./rate_limit_fetch.js";
|
|
5
15
|
export * from "./openapi/client.js";
|
package/script/mod.js
CHANGED
|
@@ -14,17 +14,24 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getLagoError = exports.Client = void 0;
|
|
17
|
+
exports.createRateLimitFetch = exports.parseRateLimitHeaders = exports.LagoRateLimitError = exports.getLagoError = exports.Client = void 0;
|
|
18
18
|
// deno-lint-ignore-file no-explicit-any
|
|
19
19
|
const client_js_1 = require("./openapi/client.js");
|
|
20
|
+
const rate_limit_fetch_js_1 = require("./rate_limit_fetch.js");
|
|
20
21
|
const Client = (apiKey, apiConfig) => {
|
|
22
|
+
const { rateLimitRetry, ...restConfig } = apiConfig ?? {};
|
|
23
|
+
// Create rate-limit-aware fetch if configured
|
|
24
|
+
const customFetch = rateLimitRetry
|
|
25
|
+
? (0, rate_limit_fetch_js_1.createRateLimitFetch)((restConfig?.customFetch ?? globalThis.fetch), rateLimitRetry)
|
|
26
|
+
: restConfig?.customFetch;
|
|
21
27
|
const api = new client_js_1.Api({
|
|
22
28
|
securityWorker: (apiKey) => apiKey ? { headers: { Authorization: `Bearer ${apiKey}` } } : {},
|
|
23
29
|
// Cloudflare Workers doesn't support some options like credentials so need to override default
|
|
24
30
|
baseApiParams: {
|
|
25
31
|
redirect: "follow",
|
|
26
32
|
},
|
|
27
|
-
...
|
|
33
|
+
...restConfig,
|
|
34
|
+
...(customFetch && { customFetch }),
|
|
28
35
|
});
|
|
29
36
|
api.setSecurityData(apiKey);
|
|
30
37
|
return api;
|
|
@@ -49,4 +56,11 @@ async function getLagoError(error) {
|
|
|
49
56
|
throw new Error(error);
|
|
50
57
|
}
|
|
51
58
|
exports.getLagoError = getLagoError;
|
|
59
|
+
// Rate limit exports
|
|
60
|
+
var rate_limit_error_js_1 = require("./rate_limit_error.js");
|
|
61
|
+
Object.defineProperty(exports, "LagoRateLimitError", { enumerable: true, get: function () { return rate_limit_error_js_1.LagoRateLimitError; } });
|
|
62
|
+
var rate_limit_headers_js_1 = require("./rate_limit_headers.js");
|
|
63
|
+
Object.defineProperty(exports, "parseRateLimitHeaders", { enumerable: true, get: function () { return rate_limit_headers_js_1.parseRateLimitHeaders; } });
|
|
64
|
+
var rate_limit_fetch_js_2 = require("./rate_limit_fetch.js");
|
|
65
|
+
Object.defineProperty(exports, "createRateLimitFetch", { enumerable: true, get: function () { return rate_limit_fetch_js_2.createRateLimitFetch; } });
|
|
52
66
|
__exportStar(require("./openapi/client.js"), exports);
|
|
@@ -2226,6 +2226,16 @@ export interface FeeObject {
|
|
|
2226
2226
|
* @example "0.0"
|
|
2227
2227
|
*/
|
|
2228
2228
|
precise_coupons_amount_cents?: string;
|
|
2229
|
+
/**
|
|
2230
|
+
* The sub total of the fee excluding taxes, in cents.
|
|
2231
|
+
* @example 100
|
|
2232
|
+
*/
|
|
2233
|
+
sub_total_excluding_taxes_amount_cents: number;
|
|
2234
|
+
/**
|
|
2235
|
+
* The sub total of the fee excluding taxes, with precision.
|
|
2236
|
+
* @example "100.0"
|
|
2237
|
+
*/
|
|
2238
|
+
sub_total_excluding_taxes_precise_amount_cents: string;
|
|
2229
2239
|
/** List of all unit amount details for calculating the fee. */
|
|
2230
2240
|
amount_details?: FeeAmountDetails;
|
|
2231
2241
|
/**
|
|
@@ -3837,18 +3847,20 @@ export interface AppliedInvoiceCustomSectionObject {
|
|
|
3837
3847
|
* @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
3838
3848
|
*/
|
|
3839
3849
|
lago_id: string;
|
|
3840
|
-
/**
|
|
3841
|
-
* Unique identifier of the invoice custom section that was applied.
|
|
3842
|
-
* @format uuid
|
|
3843
|
-
* @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
3844
|
-
*/
|
|
3845
|
-
invoice_custom_section_id: string;
|
|
3846
3850
|
/**
|
|
3847
3851
|
* The date of the applied invoice custom section creation, represented in ISO 8601 datetime format and expressed in Coordinated Universal Time (UTC).
|
|
3848
3852
|
* @format date-time
|
|
3849
3853
|
* @example "2023-07-06T14:35:58Z"
|
|
3850
3854
|
*/
|
|
3851
3855
|
created_at: string;
|
|
3856
|
+
/**
|
|
3857
|
+
* Unique identifier of the invoice custom section that was applied.
|
|
3858
|
+
* @deprecated
|
|
3859
|
+
* @format uuid
|
|
3860
|
+
* @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
|
|
3861
|
+
*/
|
|
3862
|
+
invoice_custom_section_id?: string;
|
|
3863
|
+
invoice_custom_section: InvoiceCustomSectionObject;
|
|
3852
3864
|
}
|
|
3853
3865
|
/** Reference to a specific payment method for processing the payment. */
|
|
3854
3866
|
export interface PaymentMethodReference {
|
|
@@ -10316,7 +10328,9 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
|
|
|
10316
10328
|
* @request POST:/customers/{external_customer_id}/checkout_url
|
|
10317
10329
|
* @secure
|
|
10318
10330
|
*/
|
|
10319
|
-
generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<
|
|
10331
|
+
generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<{
|
|
10332
|
+
customer: CustomerCheckoutUrl;
|
|
10333
|
+
}, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorNotFound>>;
|
|
10320
10334
|
/**
|
|
10321
10335
|
* @description This endpoint retrieves all payment methods of a Customer.
|
|
10322
10336
|
*
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error class for rate limit (HTTP 429) responses
|
|
3
|
+
*/
|
|
4
|
+
export declare class LagoRateLimitError extends Error {
|
|
5
|
+
readonly limit: number;
|
|
6
|
+
readonly remaining: number;
|
|
7
|
+
readonly reset: number;
|
|
8
|
+
readonly retryAfter: number;
|
|
9
|
+
constructor(limit: number, remaining: number, reset: number);
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LagoRateLimitError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Error class for rate limit (HTTP 429) responses
|
|
6
|
+
*/
|
|
7
|
+
class LagoRateLimitError extends Error {
|
|
8
|
+
limit;
|
|
9
|
+
remaining;
|
|
10
|
+
reset; // seconds until window resets
|
|
11
|
+
retryAfter; // milliseconds to wait before retrying
|
|
12
|
+
constructor(limit, remaining, reset) {
|
|
13
|
+
super(`Rate limit exceeded. Limit: ${limit}, Remaining: ${remaining}, Reset in: ${reset}s`);
|
|
14
|
+
this.name = "LagoRateLimitError";
|
|
15
|
+
this.limit = limit;
|
|
16
|
+
this.remaining = remaining;
|
|
17
|
+
this.reset = reset;
|
|
18
|
+
this.retryAfter = reset * 1000; // Convert seconds to milliseconds
|
|
19
|
+
// Maintain proper prototype chain for instanceof checks
|
|
20
|
+
Object.setPrototypeOf(this, LagoRateLimitError.prototype);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.LagoRateLimitError = LagoRateLimitError;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for rate limit fetch behavior
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitFetchConfig {
|
|
5
|
+
/** Maximum number of retries on 429 (default: 3) */
|
|
6
|
+
maxRetries?: number;
|
|
7
|
+
/** Whether to automatically retry on rate limit (default: true) */
|
|
8
|
+
retryOnRateLimit?: boolean;
|
|
9
|
+
/** Maximum delay in milliseconds before a retry (default: 20000) */
|
|
10
|
+
maxRetryDelay?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a fetch wrapper that handles rate limiting with automatic retry
|
|
14
|
+
* Compatible with both Node.js fetch and browser fetch APIs
|
|
15
|
+
*/
|
|
16
|
+
export declare function createRateLimitFetch(baseFetch: typeof fetch, config?: RateLimitFetchConfig): typeof fetch;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRateLimitFetch = void 0;
|
|
4
|
+
const rate_limit_error_js_1 = require("./rate_limit_error.js");
|
|
5
|
+
const rate_limit_headers_js_1 = require("./rate_limit_headers.js");
|
|
6
|
+
/**
|
|
7
|
+
* Creates a fetch wrapper that handles rate limiting with automatic retry
|
|
8
|
+
* Compatible with both Node.js fetch and browser fetch APIs
|
|
9
|
+
*/
|
|
10
|
+
function createRateLimitFetch(baseFetch, config = {}) {
|
|
11
|
+
const maxRetries = config.maxRetries ?? 3;
|
|
12
|
+
const retryOnRateLimit = config.retryOnRateLimit ?? true;
|
|
13
|
+
const maxRetryDelay = config.maxRetryDelay ?? 20_000;
|
|
14
|
+
return async function rateLimitFetch(input, init) {
|
|
15
|
+
let lastError;
|
|
16
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
17
|
+
try {
|
|
18
|
+
const response = await baseFetch(input, init);
|
|
19
|
+
// Handle 429 responses
|
|
20
|
+
if (response.status === 429) {
|
|
21
|
+
const headers = (0, rate_limit_headers_js_1.parseRateLimitHeaders)(response.headers);
|
|
22
|
+
const limit = headers.limit ?? -1;
|
|
23
|
+
const remaining = headers.remaining ?? 0;
|
|
24
|
+
const reset = headers.reset ?? -1;
|
|
25
|
+
const error = new rate_limit_error_js_1.LagoRateLimitError(limit, remaining, reset);
|
|
26
|
+
if (!retryOnRateLimit) {
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
if (attempt === maxRetries) {
|
|
30
|
+
throw error; // Max retries reached
|
|
31
|
+
}
|
|
32
|
+
// Wait before retry
|
|
33
|
+
const waitMs = getWaitTime(error, attempt, maxRetryDelay);
|
|
34
|
+
await sleep(waitMs);
|
|
35
|
+
continue; // Retry
|
|
36
|
+
}
|
|
37
|
+
// Success or non-rate-limit error - return the response
|
|
38
|
+
return response;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
lastError = error;
|
|
42
|
+
if (!(error instanceof rate_limit_error_js_1.LagoRateLimitError)) {
|
|
43
|
+
throw error; // Not a rate limit error, rethrow immediately
|
|
44
|
+
}
|
|
45
|
+
if (attempt === maxRetries) {
|
|
46
|
+
throw error; // Max retries reached
|
|
47
|
+
}
|
|
48
|
+
// Will retry on next iteration
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw lastError;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
exports.createRateLimitFetch = createRateLimitFetch;
|
|
55
|
+
/**
|
|
56
|
+
* Calculate wait time before retry
|
|
57
|
+
* Uses the exact reset time from headers if available, otherwise exponential backoff
|
|
58
|
+
*/
|
|
59
|
+
function getWaitTime(error, attempt, maxRetryDelay) {
|
|
60
|
+
let waitMs;
|
|
61
|
+
if (error.reset > 0) {
|
|
62
|
+
// Use the exact reset time from the header
|
|
63
|
+
waitMs = error.retryAfter;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, etc.
|
|
67
|
+
waitMs = 1000 * Math.pow(2, attempt);
|
|
68
|
+
}
|
|
69
|
+
// Cap at maxRetryDelay
|
|
70
|
+
waitMs = Math.min(waitMs, maxRetryDelay);
|
|
71
|
+
// Add small jitter to prevent thundering herd (max 100ms)
|
|
72
|
+
const jitter = Math.random() * 100;
|
|
73
|
+
return waitMs + jitter;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Sleep for a specified number of milliseconds
|
|
77
|
+
*/
|
|
78
|
+
function sleep(ms) {
|
|
79
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
80
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses rate limit headers from HTTP responses
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitHeaders {
|
|
5
|
+
limit: number | null;
|
|
6
|
+
remaining: number | null;
|
|
7
|
+
reset: number | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract rate limit information from response headers
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseRateLimitHeaders(headers: Headers): RateLimitHeaders;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseRateLimitHeaders = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Extract rate limit information from response headers
|
|
6
|
+
*/
|
|
7
|
+
function parseRateLimitHeaders(headers) {
|
|
8
|
+
return {
|
|
9
|
+
limit: parseHeaderAsNumber(headers, "x-ratelimit-limit"),
|
|
10
|
+
remaining: parseHeaderAsNumber(headers, "x-ratelimit-remaining"),
|
|
11
|
+
reset: parseHeaderAsNumber(headers, "x-ratelimit-reset"),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
exports.parseRateLimitHeaders = parseRateLimitHeaders;
|
|
15
|
+
/**
|
|
16
|
+
* Helper to parse a header value as a number
|
|
17
|
+
*/
|
|
18
|
+
function parseHeaderAsNumber(headers, headerName) {
|
|
19
|
+
const value = headers.get(headerName);
|
|
20
|
+
if (value === null)
|
|
21
|
+
return null;
|
|
22
|
+
const num = parseInt(value, 10);
|
|
23
|
+
return isNaN(num) ? null : num;
|
|
24
|
+
}
|