lago-javascript-client 1.45.0 → 1.46.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/esm/mod.d.ts CHANGED
@@ -1,5 +1,15 @@
1
1
  import { Api, ApiConfig, HttpResponse } from "./openapi/client.js";
2
- export declare const Client: (apiKey: string, apiConfig?: ApiConfig) => Api<unknown>;
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
- ...apiConfig,
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";
@@ -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 {
@@ -4572,7 +4584,7 @@ export type AlertThresholdObject = AlertThresholdBaseObject & {
4572
4584
  */
4573
4585
  value: string;
4574
4586
  };
4575
- export interface AlertObject {
4587
+ export interface WalletAlertObject {
4576
4588
  /**
4577
4589
  * Unique identifier of the alert, created by Lago.
4578
4590
  * @format uuid
@@ -4585,46 +4597,36 @@ export interface AlertObject {
4585
4597
  * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
4586
4598
  */
4587
4599
  lago_organization_id: string;
4588
- /**
4589
- * The subscription external unique identifier (provided by your own application).
4590
- * @example "sub_1234567890"
4591
- */
4592
- external_subscription_id: string | null;
4593
- /**
4594
- * The customer external unique identifier (provided by your own application).
4595
- * @example "cus_0987654321"
4596
- */
4597
- external_customer_id: string;
4600
+ external_subscription_id: null;
4598
4601
  /**
4599
4602
  * Unique identifier of the wallet, created by Lago.
4600
4603
  * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
4601
4604
  */
4602
- lago_wallet_id?: string | null;
4605
+ lago_wallet_id?: string;
4603
4606
  /**
4604
4607
  * The wallet external unique identifier (provided by your own application).
4605
4608
  * @example "wallet_code"
4606
4609
  */
4607
- wallet_code?: string | null;
4608
- /** The billable metric associated with the alert. Only for alerts based on a billable metric. */
4609
- billable_metric: BillableMetricObject;
4610
+ wallet_code?: string;
4611
+ billable_metric: null;
4610
4612
  /**
4611
4613
  * The type of alert.
4612
- * @example "billable_metric_current_usage_amount"
4614
+ * @example "wallet_balance_amount"
4613
4615
  */
4614
- alert_type: "current_usage_amount" | "billable_metric_current_usage_amount" | "billable_metric_current_usage_units" | "lifetime_usage_amount";
4616
+ alert_type: "wallet_balance_amount" | "wallet_credits_balance" | "wallet_ongoing_balance_amount" | "wallet_credits_ongoing_balance";
4615
4617
  /**
4616
4618
  * Unique code used to identify the alert.
4617
- * @example "storage_threshold_alert"
4619
+ * @example "wallet_balance_alert"
4618
4620
  */
4619
4621
  code: string;
4620
4622
  /**
4621
4623
  * The name of the alert.
4622
- * @example "Storage Usage Alert"
4624
+ * @example "Wallet Balance Alert"
4623
4625
  */
4624
4626
  name: string | null;
4625
4627
  /**
4626
4628
  * Indicates whether the alert is triggered when the monitored metric goes above or below the threshold.
4627
- * @example "increasing"
4629
+ * @example "decreasing"
4628
4630
  */
4629
4631
  direction?: "increasing" | "decreasing";
4630
4632
  /**
@@ -4647,8 +4649,8 @@ export interface AlertObject {
4647
4649
  */
4648
4650
  created_at: string;
4649
4651
  }
4650
- export interface Alerts {
4651
- alerts: AlertObject[];
4652
+ export interface WalletAlerts {
4653
+ alerts: WalletAlertObject[];
4652
4654
  meta?: PaginationMeta;
4653
4655
  }
4654
4656
  export type AlertThresholdInput = AlertThresholdBaseObject & {
@@ -4678,7 +4680,7 @@ export interface WalletAlertCreateInput {
4678
4680
  * The type of alert.
4679
4681
  * @example "wallet_balance_amount"
4680
4682
  */
4681
- alert_type: "wallet_balance_amount" | "wallet_credits_balance";
4683
+ alert_type: "wallet_balance_amount" | "wallet_credits_balance" | "wallet_ongoing_balance_amount" | "wallet_credits_ongoing_balance";
4682
4684
  /**
4683
4685
  * Unique code used to identify the alert.
4684
4686
  * @example "wallet_balance_alert"
@@ -4698,7 +4700,7 @@ export interface WalletAlertBatchCreateInput {
4698
4700
  * The type of alert.
4699
4701
  * @example "wallet_balance_amount"
4700
4702
  */
4701
- alert_type: "wallet_balance_amount" | "wallet_credits_balance";
4703
+ alert_type: "wallet_balance_amount" | "wallet_credits_balance" | "wallet_ongoing_balance_amount" | "wallet_credits_ongoing_balance";
4702
4704
  /**
4703
4705
  * Unique code used to identify the alert.
4704
4706
  * @example "wallet_balance_alert"
@@ -4711,8 +4713,8 @@ export interface WalletAlertBatchCreateInput {
4711
4713
  name?: string | null;
4712
4714
  })[];
4713
4715
  }
4714
- export interface Alert {
4715
- alert: AlertObject;
4716
+ export interface WalletAlert {
4717
+ alert: WalletAlertObject;
4716
4718
  }
4717
4719
  export interface WalletAlertUpdateInput {
4718
4720
  alert: AlertBaseInput & {
@@ -7800,6 +7802,72 @@ export interface LifetimeUsageInput {
7800
7802
  external_historical_usage_amount_cents: number;
7801
7803
  };
7802
7804
  }
7805
+ export interface AlertObject {
7806
+ /**
7807
+ * Unique identifier of the alert, created by Lago.
7808
+ * @format uuid
7809
+ * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
7810
+ */
7811
+ lago_id: string;
7812
+ /**
7813
+ * Unique identifier of the organization, created by Lago.
7814
+ * @format uuid
7815
+ * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
7816
+ */
7817
+ lago_organization_id: string;
7818
+ /**
7819
+ * The subscription external unique identifier (provided by your own application).
7820
+ * @example "sub_1234567890"
7821
+ */
7822
+ external_subscription_id: string | null;
7823
+ lago_wallet_id?: null;
7824
+ wallet_code?: null;
7825
+ /** The billable metric associated with the alert. Only for alerts based on a billable metric. */
7826
+ billable_metric: BillableMetricObject;
7827
+ /**
7828
+ * The type of alert.
7829
+ * @example "billable_metric_current_usage_amount"
7830
+ */
7831
+ alert_type: "current_usage_amount" | "billable_metric_current_usage_amount" | "billable_metric_current_usage_units" | "lifetime_usage_amount";
7832
+ /**
7833
+ * Unique code used to identify the alert.
7834
+ * @example "storage_threshold_alert"
7835
+ */
7836
+ code: string;
7837
+ /**
7838
+ * The name of the alert.
7839
+ * @example "Storage Usage Alert"
7840
+ */
7841
+ name: string | null;
7842
+ /**
7843
+ * Indicates whether the alert is triggered when the monitored metric goes above or below the threshold.
7844
+ * @example "increasing"
7845
+ */
7846
+ direction?: "increasing" | "decreasing";
7847
+ /**
7848
+ * When the system checked if this alert should be triggered, this value was retrieved and checked against the thresholds.
7849
+ * @example 1000
7850
+ */
7851
+ previous_value: number;
7852
+ /**
7853
+ * The date and time in UTC (ISO 8601) when the system checked if this alert should be triggered. Null until it's processed for the first time.
7854
+ * @format date-time
7855
+ * @example "2025-05-19T10:04:21Z"
7856
+ */
7857
+ last_processed_at: string | null;
7858
+ /** Array of thresholds associated with the alert. */
7859
+ thresholds: AlertThresholdObject[];
7860
+ /**
7861
+ * The date and time in UTC (ISO 8601) when the alert was created.
7862
+ * @format date-time
7863
+ * @example "2025-03-20T10:00:00Z"
7864
+ */
7865
+ created_at: string;
7866
+ }
7867
+ export interface Alerts {
7868
+ alerts: AlertObject[];
7869
+ meta?: PaginationMeta;
7870
+ }
7803
7871
  export interface AlertCreateInput {
7804
7872
  alert: AlertBaseInput & {
7805
7873
  /**
@@ -7832,8 +7900,16 @@ export interface AlertBatchCreateInput {
7832
7900
  * @example "storage_threshold_alert"
7833
7901
  */
7834
7902
  code: string;
7903
+ /**
7904
+ * The code of the billable metric associated with the alert. Only for alerts based on a billable metric.
7905
+ * @example "storage_usage"
7906
+ */
7907
+ billable_metric_code?: string | null;
7835
7908
  })[];
7836
7909
  }
7910
+ export interface Alert {
7911
+ alert: AlertObject;
7912
+ }
7837
7913
  export interface AlertUpdateInput {
7838
7914
  alert: AlertBaseInput & {
7839
7915
  /**
@@ -10154,7 +10230,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10154
10230
  * @request GET:/customers/{external_customer_id}/wallets/{wallet_code}/alerts
10155
10231
  * @secure
10156
10232
  */
10157
- getCustomerWalletAlerts: (externalCustomerId: string, walletCode: string, params?: RequestParams) => Promise<HttpResponse<Alerts, ApiErrorUnauthorized | ApiErrorNotFound>>;
10233
+ getCustomerWalletAlerts: (externalCustomerId: string, walletCode: string, params?: RequestParams) => Promise<HttpResponse<WalletAlerts, ApiErrorUnauthorized | ApiErrorNotFound>>;
10158
10234
  /**
10159
10235
  * @description This endpoint allows you to create new alerts for a wallet. Send a single alert object wrapped in `alert` key to create one alert, or an array of alert objects wrapped in `alerts` key to create multiple alerts atomically.
10160
10236
  *
@@ -10164,7 +10240,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10164
10240
  * @request POST:/customers/{external_customer_id}/wallets/{wallet_code}/alerts
10165
10241
  * @secure
10166
10242
  */
10167
- createCustomerWalletAlert: (externalCustomerId: string, walletCode: string, data: WalletAlertCreateInput | WalletAlertBatchCreateInput, params?: RequestParams) => Promise<HttpResponse<Alerts | Alert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10243
+ createCustomerWalletAlert: (externalCustomerId: string, walletCode: string, data: WalletAlertCreateInput | WalletAlertBatchCreateInput, params?: RequestParams) => Promise<HttpResponse<WalletAlerts | WalletAlert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10168
10244
  /**
10169
10245
  * @description This endpoint allows you to delete all alerts for a wallet.
10170
10246
  *
@@ -10184,7 +10260,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10184
10260
  * @request GET:/customers/{external_customer_id}/wallets/{wallet_code}/alerts/{code}
10185
10261
  * @secure
10186
10262
  */
10187
- getCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<Alert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10263
+ getCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<WalletAlert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10188
10264
  /**
10189
10265
  * @description This endpoint allows you to update an existing alert for a wallet.
10190
10266
  *
@@ -10194,7 +10270,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10194
10270
  * @request PUT:/customers/{external_customer_id}/wallets/{wallet_code}/alerts/{code}
10195
10271
  * @secure
10196
10272
  */
10197
- updateCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, data: WalletAlertUpdateInput, params?: RequestParams) => Promise<HttpResponse<Alert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10273
+ updateCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, data: WalletAlertUpdateInput, params?: RequestParams) => Promise<HttpResponse<WalletAlert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10198
10274
  /**
10199
10275
  * @description This endpoint allows you to delete an existing alert for a wallet.
10200
10276
  *
@@ -10204,7 +10280,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10204
10280
  * @request DELETE:/customers/{external_customer_id}/wallets/{wallet_code}/alerts/{code}
10205
10281
  * @secure
10206
10282
  */
10207
- deleteCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<Alert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10283
+ deleteCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<WalletAlert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10208
10284
  /**
10209
10285
  * @description This endpoint enables the retrieval of the usage-based billing data for a customer within the current period.
10210
10286
  *
@@ -10316,7 +10392,9 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10316
10392
  * @request POST:/customers/{external_customer_id}/checkout_url
10317
10393
  * @secure
10318
10394
  */
10319
- generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<CustomerCheckoutUrl, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorNotFound>>;
10395
+ generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<{
10396
+ customer: CustomerCheckoutUrl;
10397
+ }, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorNotFound>>;
10320
10398
  /**
10321
10399
  * @description This endpoint retrieves all payment methods of a Customer.
10322
10400
  *
@@ -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
@@ -3,7 +3,7 @@
3
3
  "main": "./script/mod.js",
4
4
  "name": "lago-javascript-client",
5
5
  "sideEffects": false,
6
- "version": "v1.45.0",
6
+ "version": "v1.46.1",
7
7
  "description": "Lago JavaScript API Client",
8
8
  "repository": {
9
9
  "type": "git",
package/script/mod.d.ts CHANGED
@@ -1,5 +1,15 @@
1
1
  import { Api, ApiConfig, HttpResponse } from "./openapi/client.js";
2
- export declare const Client: (apiKey: string, apiConfig?: ApiConfig) => Api<unknown>;
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
- ...apiConfig,
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 {
@@ -4572,7 +4584,7 @@ export type AlertThresholdObject = AlertThresholdBaseObject & {
4572
4584
  */
4573
4585
  value: string;
4574
4586
  };
4575
- export interface AlertObject {
4587
+ export interface WalletAlertObject {
4576
4588
  /**
4577
4589
  * Unique identifier of the alert, created by Lago.
4578
4590
  * @format uuid
@@ -4585,46 +4597,36 @@ export interface AlertObject {
4585
4597
  * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
4586
4598
  */
4587
4599
  lago_organization_id: string;
4588
- /**
4589
- * The subscription external unique identifier (provided by your own application).
4590
- * @example "sub_1234567890"
4591
- */
4592
- external_subscription_id: string | null;
4593
- /**
4594
- * The customer external unique identifier (provided by your own application).
4595
- * @example "cus_0987654321"
4596
- */
4597
- external_customer_id: string;
4600
+ external_subscription_id: null;
4598
4601
  /**
4599
4602
  * Unique identifier of the wallet, created by Lago.
4600
4603
  * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
4601
4604
  */
4602
- lago_wallet_id?: string | null;
4605
+ lago_wallet_id?: string;
4603
4606
  /**
4604
4607
  * The wallet external unique identifier (provided by your own application).
4605
4608
  * @example "wallet_code"
4606
4609
  */
4607
- wallet_code?: string | null;
4608
- /** The billable metric associated with the alert. Only for alerts based on a billable metric. */
4609
- billable_metric: BillableMetricObject;
4610
+ wallet_code?: string;
4611
+ billable_metric: null;
4610
4612
  /**
4611
4613
  * The type of alert.
4612
- * @example "billable_metric_current_usage_amount"
4614
+ * @example "wallet_balance_amount"
4613
4615
  */
4614
- alert_type: "current_usage_amount" | "billable_metric_current_usage_amount" | "billable_metric_current_usage_units" | "lifetime_usage_amount";
4616
+ alert_type: "wallet_balance_amount" | "wallet_credits_balance" | "wallet_ongoing_balance_amount" | "wallet_credits_ongoing_balance";
4615
4617
  /**
4616
4618
  * Unique code used to identify the alert.
4617
- * @example "storage_threshold_alert"
4619
+ * @example "wallet_balance_alert"
4618
4620
  */
4619
4621
  code: string;
4620
4622
  /**
4621
4623
  * The name of the alert.
4622
- * @example "Storage Usage Alert"
4624
+ * @example "Wallet Balance Alert"
4623
4625
  */
4624
4626
  name: string | null;
4625
4627
  /**
4626
4628
  * Indicates whether the alert is triggered when the monitored metric goes above or below the threshold.
4627
- * @example "increasing"
4629
+ * @example "decreasing"
4628
4630
  */
4629
4631
  direction?: "increasing" | "decreasing";
4630
4632
  /**
@@ -4647,8 +4649,8 @@ export interface AlertObject {
4647
4649
  */
4648
4650
  created_at: string;
4649
4651
  }
4650
- export interface Alerts {
4651
- alerts: AlertObject[];
4652
+ export interface WalletAlerts {
4653
+ alerts: WalletAlertObject[];
4652
4654
  meta?: PaginationMeta;
4653
4655
  }
4654
4656
  export type AlertThresholdInput = AlertThresholdBaseObject & {
@@ -4678,7 +4680,7 @@ export interface WalletAlertCreateInput {
4678
4680
  * The type of alert.
4679
4681
  * @example "wallet_balance_amount"
4680
4682
  */
4681
- alert_type: "wallet_balance_amount" | "wallet_credits_balance";
4683
+ alert_type: "wallet_balance_amount" | "wallet_credits_balance" | "wallet_ongoing_balance_amount" | "wallet_credits_ongoing_balance";
4682
4684
  /**
4683
4685
  * Unique code used to identify the alert.
4684
4686
  * @example "wallet_balance_alert"
@@ -4698,7 +4700,7 @@ export interface WalletAlertBatchCreateInput {
4698
4700
  * The type of alert.
4699
4701
  * @example "wallet_balance_amount"
4700
4702
  */
4701
- alert_type: "wallet_balance_amount" | "wallet_credits_balance";
4703
+ alert_type: "wallet_balance_amount" | "wallet_credits_balance" | "wallet_ongoing_balance_amount" | "wallet_credits_ongoing_balance";
4702
4704
  /**
4703
4705
  * Unique code used to identify the alert.
4704
4706
  * @example "wallet_balance_alert"
@@ -4711,8 +4713,8 @@ export interface WalletAlertBatchCreateInput {
4711
4713
  name?: string | null;
4712
4714
  })[];
4713
4715
  }
4714
- export interface Alert {
4715
- alert: AlertObject;
4716
+ export interface WalletAlert {
4717
+ alert: WalletAlertObject;
4716
4718
  }
4717
4719
  export interface WalletAlertUpdateInput {
4718
4720
  alert: AlertBaseInput & {
@@ -7800,6 +7802,72 @@ export interface LifetimeUsageInput {
7800
7802
  external_historical_usage_amount_cents: number;
7801
7803
  };
7802
7804
  }
7805
+ export interface AlertObject {
7806
+ /**
7807
+ * Unique identifier of the alert, created by Lago.
7808
+ * @format uuid
7809
+ * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
7810
+ */
7811
+ lago_id: string;
7812
+ /**
7813
+ * Unique identifier of the organization, created by Lago.
7814
+ * @format uuid
7815
+ * @example "1a901a90-1a90-1a90-1a90-1a901a901a90"
7816
+ */
7817
+ lago_organization_id: string;
7818
+ /**
7819
+ * The subscription external unique identifier (provided by your own application).
7820
+ * @example "sub_1234567890"
7821
+ */
7822
+ external_subscription_id: string | null;
7823
+ lago_wallet_id?: null;
7824
+ wallet_code?: null;
7825
+ /** The billable metric associated with the alert. Only for alerts based on a billable metric. */
7826
+ billable_metric: BillableMetricObject;
7827
+ /**
7828
+ * The type of alert.
7829
+ * @example "billable_metric_current_usage_amount"
7830
+ */
7831
+ alert_type: "current_usage_amount" | "billable_metric_current_usage_amount" | "billable_metric_current_usage_units" | "lifetime_usage_amount";
7832
+ /**
7833
+ * Unique code used to identify the alert.
7834
+ * @example "storage_threshold_alert"
7835
+ */
7836
+ code: string;
7837
+ /**
7838
+ * The name of the alert.
7839
+ * @example "Storage Usage Alert"
7840
+ */
7841
+ name: string | null;
7842
+ /**
7843
+ * Indicates whether the alert is triggered when the monitored metric goes above or below the threshold.
7844
+ * @example "increasing"
7845
+ */
7846
+ direction?: "increasing" | "decreasing";
7847
+ /**
7848
+ * When the system checked if this alert should be triggered, this value was retrieved and checked against the thresholds.
7849
+ * @example 1000
7850
+ */
7851
+ previous_value: number;
7852
+ /**
7853
+ * The date and time in UTC (ISO 8601) when the system checked if this alert should be triggered. Null until it's processed for the first time.
7854
+ * @format date-time
7855
+ * @example "2025-05-19T10:04:21Z"
7856
+ */
7857
+ last_processed_at: string | null;
7858
+ /** Array of thresholds associated with the alert. */
7859
+ thresholds: AlertThresholdObject[];
7860
+ /**
7861
+ * The date and time in UTC (ISO 8601) when the alert was created.
7862
+ * @format date-time
7863
+ * @example "2025-03-20T10:00:00Z"
7864
+ */
7865
+ created_at: string;
7866
+ }
7867
+ export interface Alerts {
7868
+ alerts: AlertObject[];
7869
+ meta?: PaginationMeta;
7870
+ }
7803
7871
  export interface AlertCreateInput {
7804
7872
  alert: AlertBaseInput & {
7805
7873
  /**
@@ -7832,8 +7900,16 @@ export interface AlertBatchCreateInput {
7832
7900
  * @example "storage_threshold_alert"
7833
7901
  */
7834
7902
  code: string;
7903
+ /**
7904
+ * The code of the billable metric associated with the alert. Only for alerts based on a billable metric.
7905
+ * @example "storage_usage"
7906
+ */
7907
+ billable_metric_code?: string | null;
7835
7908
  })[];
7836
7909
  }
7910
+ export interface Alert {
7911
+ alert: AlertObject;
7912
+ }
7837
7913
  export interface AlertUpdateInput {
7838
7914
  alert: AlertBaseInput & {
7839
7915
  /**
@@ -10154,7 +10230,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10154
10230
  * @request GET:/customers/{external_customer_id}/wallets/{wallet_code}/alerts
10155
10231
  * @secure
10156
10232
  */
10157
- getCustomerWalletAlerts: (externalCustomerId: string, walletCode: string, params?: RequestParams) => Promise<HttpResponse<Alerts, ApiErrorUnauthorized | ApiErrorNotFound>>;
10233
+ getCustomerWalletAlerts: (externalCustomerId: string, walletCode: string, params?: RequestParams) => Promise<HttpResponse<WalletAlerts, ApiErrorUnauthorized | ApiErrorNotFound>>;
10158
10234
  /**
10159
10235
  * @description This endpoint allows you to create new alerts for a wallet. Send a single alert object wrapped in `alert` key to create one alert, or an array of alert objects wrapped in `alerts` key to create multiple alerts atomically.
10160
10236
  *
@@ -10164,7 +10240,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10164
10240
  * @request POST:/customers/{external_customer_id}/wallets/{wallet_code}/alerts
10165
10241
  * @secure
10166
10242
  */
10167
- createCustomerWalletAlert: (externalCustomerId: string, walletCode: string, data: WalletAlertCreateInput | WalletAlertBatchCreateInput, params?: RequestParams) => Promise<HttpResponse<Alerts | Alert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10243
+ createCustomerWalletAlert: (externalCustomerId: string, walletCode: string, data: WalletAlertCreateInput | WalletAlertBatchCreateInput, params?: RequestParams) => Promise<HttpResponse<WalletAlerts | WalletAlert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10168
10244
  /**
10169
10245
  * @description This endpoint allows you to delete all alerts for a wallet.
10170
10246
  *
@@ -10184,7 +10260,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10184
10260
  * @request GET:/customers/{external_customer_id}/wallets/{wallet_code}/alerts/{code}
10185
10261
  * @secure
10186
10262
  */
10187
- getCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<Alert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10263
+ getCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<WalletAlert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10188
10264
  /**
10189
10265
  * @description This endpoint allows you to update an existing alert for a wallet.
10190
10266
  *
@@ -10194,7 +10270,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10194
10270
  * @request PUT:/customers/{external_customer_id}/wallets/{wallet_code}/alerts/{code}
10195
10271
  * @secure
10196
10272
  */
10197
- updateCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, data: WalletAlertUpdateInput, params?: RequestParams) => Promise<HttpResponse<Alert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10273
+ updateCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, data: WalletAlertUpdateInput, params?: RequestParams) => Promise<HttpResponse<WalletAlert, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorBadRequest | ApiErrorNotFound>>;
10198
10274
  /**
10199
10275
  * @description This endpoint allows you to delete an existing alert for a wallet.
10200
10276
  *
@@ -10204,7 +10280,7 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10204
10280
  * @request DELETE:/customers/{external_customer_id}/wallets/{wallet_code}/alerts/{code}
10205
10281
  * @secure
10206
10282
  */
10207
- deleteCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<Alert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10283
+ deleteCustomerWalletAlert: (externalCustomerId: string, walletCode: string, code: string, params?: RequestParams) => Promise<HttpResponse<WalletAlert, ApiErrorUnauthorized | ApiErrorNotFound>>;
10208
10284
  /**
10209
10285
  * @description This endpoint enables the retrieval of the usage-based billing data for a customer within the current period.
10210
10286
  *
@@ -10316,7 +10392,9 @@ export declare class Api<SecurityDataType extends unknown> extends HttpClient<Se
10316
10392
  * @request POST:/customers/{external_customer_id}/checkout_url
10317
10393
  * @secure
10318
10394
  */
10319
- generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<CustomerCheckoutUrl, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorNotFound>>;
10395
+ generateCustomerCheckoutUrl: (externalCustomerId: string, params?: RequestParams) => Promise<HttpResponse<{
10396
+ customer: CustomerCheckoutUrl;
10397
+ }, ApiErrorUnauthorized | ApiErrorUnprocessableEntity | ApiErrorNotFound>>;
10320
10398
  /**
10321
10399
  * @description This endpoint retrieves all payment methods of a Customer.
10322
10400
  *
@@ -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
+ }