dymo-api 1.2.29 → 1.2.31

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.
@@ -0,0 +1,131 @@
1
+ class RateLimitManager {
2
+ constructor() {
3
+ this.tracker = {};
4
+ }
5
+ static getInstance() {
6
+ if (!RateLimitManager.instance)
7
+ RateLimitManager.instance = new RateLimitManager();
8
+ return RateLimitManager.instance;
9
+ }
10
+ ;
11
+ updateRateLimit(clientId, headers) {
12
+ if (!this.tracker[clientId])
13
+ this.tracker[clientId] = {};
14
+ const limitInfo = this.tracker[clientId];
15
+ if (headers["x-ratelimit-limit-requests"])
16
+ limitInfo.limit = parseInt(headers["x-ratelimit-limit-requests"]);
17
+ if (headers["x-ratelimit-remaining-requests"])
18
+ limitInfo.remaining = parseInt(headers["x-ratelimit-remaining-requests"]);
19
+ if (headers["x-ratelimit-reset-requests"])
20
+ limitInfo.resetTime = headers["x-ratelimit-reset-requests"];
21
+ if (headers["retry-after"])
22
+ limitInfo.retryAfter = parseInt(headers["retry-after"]);
23
+ limitInfo.lastUpdated = Date.now();
24
+ }
25
+ ;
26
+ isRateLimited(clientId) {
27
+ const limitInfo = this.tracker[clientId];
28
+ return limitInfo?.remaining !== undefined && limitInfo.remaining <= 0;
29
+ }
30
+ ;
31
+ getRetryAfter(clientId) {
32
+ return this.tracker[clientId]?.retryAfter;
33
+ }
34
+ ;
35
+ clearExpiredLimits() {
36
+ const now = Date.now();
37
+ Object.keys(this.tracker).forEach(clientId => {
38
+ const limitInfo = this.tracker[clientId];
39
+ if (limitInfo.lastUpdated && (now - limitInfo.lastUpdated) > 300000)
40
+ delete this.tracker[clientId];
41
+ });
42
+ }
43
+ ;
44
+ }
45
+ ;
46
+ export class ResilienceManager {
47
+ constructor(config = {}, clientId = "default") {
48
+ this.config = {
49
+ fallbackEnabled: config.fallbackEnabled ?? false,
50
+ retryAttempts: Math.max(0, config.retryAttempts ?? 2), // Number of additional retries
51
+ retryDelay: Math.max(0, config.retryDelay ?? 1000)
52
+ };
53
+ this.clientId = clientId;
54
+ this.rateLimitManager = RateLimitManager.getInstance();
55
+ }
56
+ ;
57
+ async executeWithResilience(axiosClient, requestConfig, fallbackData) {
58
+ let lastError;
59
+ const totalAttempts = 1 + this.config.retryAttempts; // 1 normal + N retries
60
+ // Clean up expired rate limits periodically
61
+ this.rateLimitManager.clearExpiredLimits();
62
+ // Check if client is currently rate limited
63
+ if (this.rateLimitManager.isRateLimited(this.clientId)) {
64
+ const retryAfter = this.rateLimitManager.getRetryAfter(this.clientId);
65
+ if (retryAfter) {
66
+ console.warn(`[Dymo API] Client ${this.clientId} is rate limited. Waiting ${retryAfter} seconds...`);
67
+ await this.sleep(retryAfter * 1000);
68
+ }
69
+ }
70
+ for (let attempt = 1; attempt <= totalAttempts; attempt++) {
71
+ try {
72
+ const response = await axiosClient.request(requestConfig);
73
+ // Update rate limit tracking
74
+ this.rateLimitManager.updateRateLimit(this.clientId, response.headers);
75
+ // Check for rate limiting
76
+ if (response.status === 429) {
77
+ const retryAfter = this.rateLimitManager.getRetryAfter(this.clientId);
78
+ if (retryAfter) {
79
+ console.warn(`[Dymo API] Rate limited. Waiting ${retryAfter} seconds (no retries)`);
80
+ await this.sleep(retryAfter * 1000);
81
+ }
82
+ throw new Error(`Rate limited (429) - not retrying`);
83
+ }
84
+ return response.data;
85
+ }
86
+ catch (error) {
87
+ lastError = error;
88
+ let shouldRetry = this.shouldRetry(error);
89
+ const isLastAttempt = attempt === totalAttempts;
90
+ // Don't retry on rate limiting (429)
91
+ if (error.response?.status === 429)
92
+ shouldRetry = false;
93
+ if (!shouldRetry || isLastAttempt) {
94
+ if (this.config.fallbackEnabled && fallbackData) {
95
+ console.warn(`[Dymo API] Request failed after ${attempt} attempts. Using fallback data.`);
96
+ return fallbackData;
97
+ }
98
+ throw error;
99
+ }
100
+ const delay = this.config.retryDelay * Math.pow(2, attempt - 1);
101
+ console.warn(`[Dymo API] Attempt ${attempt} failed. Retrying in ${delay}ms...`);
102
+ await this.sleep(delay);
103
+ }
104
+ }
105
+ throw lastError;
106
+ }
107
+ ;
108
+ shouldRetry(error) {
109
+ const statusCode = error.response?.status;
110
+ const isNetworkError = !error.response && error.code !== "ECONNABORTED";
111
+ const isServerError = statusCode && statusCode >= 500;
112
+ // Retry on: network errors, server errors (5xx)
113
+ // DON'T retry on: rate limiting (429) - handled separately
114
+ // DON'T retry on: client errors (4xx except 429)
115
+ return isNetworkError || isServerError;
116
+ }
117
+ ;
118
+ sleep(ms) {
119
+ return new Promise(resolve => setTimeout(resolve, ms));
120
+ }
121
+ ;
122
+ getConfig() {
123
+ return { ...this.config };
124
+ }
125
+ ;
126
+ getClientId() {
127
+ return this.clientId;
128
+ }
129
+ ;
130
+ }
131
+ ;
@@ -6,30 +6,35 @@ declare class DymoAPI {
6
6
  private rules;
7
7
  private baseUrl;
8
8
  private axiosClient;
9
+ private resilienceManager;
9
10
  /**
10
- * @param {Object} options - Options to create the DymoAPI instance.
11
- * @param {string} [options.rootApiKey] - The root API key.
12
- * @param {string} [options.apiKey] - The API key.
13
- * @param {string} [options.baseUrl] - Whether to use a local server instead of the cloud server.
14
- * @param {Object} [options.serverEmailConfig] - The server email config.
15
- * @description
16
- * This is the main class to interact with the Dymo API. It should be
17
- * instantiated with the root API key and the API key. The root API key is
18
- * used to fetch the tokens and the API key is used to authenticate the
19
- * requests.
20
- * @example
21
- * const dymoApi = new DymoAPI({
22
- * rootApiKey: "6bfb7675-6b69-4f8d-9f43-5a6f7f02c6c5",
23
- * apiKey: "dm_4c8b7675-6b69-4f8d-9f43-5a6f7f02c6c5"
24
- * });
25
- */
26
- constructor({ rootApiKey, apiKey, baseUrl, serverEmailConfig, rules }?: {
11
+ * @param {Object} options - Options to create the DymoAPI instance.
12
+ * @param {string} [options.rootApiKey] - The root API key.
13
+ * @param {string} [options.apiKey] - The API key.
14
+ * @param {string} [options.baseUrl] - Whether to use a local server instead of the cloud server.
15
+ * @param {Object} [options.serverEmailConfig] - The server email config.
16
+ * @param {Object} [options.rules] - The rules.
17
+ * @param {Object} [options.resilience] - The resilience config.
18
+ * @description
19
+ * This is the main class to interact with the Dymo API. It should be
20
+ * instantiated with the root API key and the API key. The root API key is
21
+ * used to fetch the tokens and the API key is used to authenticate the
22
+ * requests. Requests are retried once by default with exponential backoff.
23
+ * @example
24
+ * const dymoApi = new DymoAPI({
25
+ * rootApiKey: "6bfb7675-6b69-4f8d-9f43-5a6f7f02c6c5",
26
+ * apiKey: "dm_4c8b7675-6b69-4f8d-9f43-5a6f7f02c6c5"
27
+ * });
28
+ */
29
+ constructor({ rootApiKey, apiKey, baseUrl, serverEmailConfig, rules, resilience }?: {
27
30
  rootApiKey?: string | null;
28
31
  apiKey?: string | null;
29
32
  baseUrl?: string;
30
33
  serverEmailConfig?: Interfaces.ServerEmailConfig;
31
34
  rules?: Interfaces.Rules;
35
+ resilience?: Interfaces.ResilienceConfig;
32
36
  });
37
+ private getEmailPlugins;
33
38
  /**
34
39
  * Validates the given data against the configured validation settings.
35
40
  *
@@ -0,0 +1,26 @@
1
+ export declare class FallbackDataGenerator {
2
+ static generateFallbackData<T>(method: string, inputData?: any): T;
3
+ private static validateURL;
4
+ private static validateEmail;
5
+ private static validateDomain;
6
+ private static validateCreditCard;
7
+ private static validateIP;
8
+ private static validatePhone;
9
+ private static validateWallet;
10
+ private static validateIBAN;
11
+ private static extractDomain;
12
+ private static generateDataValidationAnalysis;
13
+ private static generateEmailValidatorResponse;
14
+ private static generateEmailDataAnalysis;
15
+ private static generateIPValidatorResponse;
16
+ private static generateIPDataAnalysis;
17
+ private static generatePhoneValidatorResponse;
18
+ private static generatePhoneDataAnalysis;
19
+ private static generateHTTPRequest;
20
+ private static generateEmailStatus;
21
+ private static generateSRNSummary;
22
+ private static generateExtractWithTextly;
23
+ private static generatePrayerTimes;
24
+ private static generateSatinizedInputAnalysis;
25
+ private static generatePasswordValidationResult;
26
+ }
@@ -0,0 +1,13 @@
1
+ import { ResilienceConfig } from "../types/interfaces";
2
+ import { AxiosInstance, AxiosRequestConfig } from "axios";
3
+ export declare class ResilienceManager {
4
+ private config;
5
+ private clientId;
6
+ private rateLimitManager;
7
+ constructor(config?: ResilienceConfig, clientId?: string);
8
+ executeWithResilience<T>(axiosClient: AxiosInstance, requestConfig: AxiosRequestConfig, fallbackData?: T): Promise<T>;
9
+ private shouldRetry;
10
+ private sleep;
11
+ getConfig(): Required<ResilienceConfig>;
12
+ getClientId(): string;
13
+ }
@@ -1,5 +1,5 @@
1
1
  import { MxRecord } from "dns";
2
- import { Email, Phone, CreditCard } from "./primitives";
2
+ import { Email, Phone, CreditCard, Char } from "./primitives";
3
3
  export type VerifyPlugins = "blocklist" | "gravatar" | "compromiseDetector" | "mxRecords" | "nsfw" | "reputation" | "riskScore" | "torNetwork" | "typosquatting" | "urlShortener";
4
4
  export type ReputationPlugin = "low" | "medium" | "high" | "very-high" | "education" | "governmental" | "unknown";
5
5
  export type TyposquattingPlugin = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
@@ -69,10 +69,10 @@ export type IPValidator = string;
69
69
  * @typedef {"FRAUD"|"INVALID"|"HIGH_RISK_SCORE"} NegativeIPRules
70
70
  *
71
71
  * @description
72
- * Values indicating why an email is considered negative.
72
+ * Values indicating why an IP is considered negative.
73
73
  * ⚠️ TOR_NETWORK and HIGH_RISK_SCORE are premium features.
74
74
  */
75
- export type NegativeIPRules = "FRAUD" | "INVALID" | "TOR_NETWORK" | "HIGH_RISK_SCORE" | `COUNTRY:${string}${string}`;
75
+ export type NegativeIPRules = "FRAUD" | "INVALID" | "TOR_NETWORK" | "HIGH_RISK_SCORE" | `COUNTRY:${Char}${Char}`;
76
76
  export type PhoneValidator = Phone;
77
77
  /**
78
78
  * @typedef {"FRAUD"|"INVALID"|"HIGH_RISK_SCORE"} NegativePhoneRules
@@ -80,7 +80,7 @@ export type PhoneValidator = Phone;
80
80
  * @description
81
81
  * Values indicating why an phone is considered negative.
82
82
  */
83
- export type NegativePhoneRules = "FRAUD" | "INVALID" | "HIGH_RISK_SCORE";
83
+ export type NegativePhoneRules = "FRAUD" | "INVALID" | "HIGH_RISK_SCORE" | `COUNTRY:${Char}${Char}`;
84
84
  export type NegativeSensitiveInfoRules = "EMAIL" | "PHONE" | "CREDIT_CARD" | "URL" | "DOMAIN" | "IP" | "WALLET" | "USER_AGENT";
85
85
  /**
86
86
  * Response returned by the email validator.
@@ -162,6 +162,21 @@ export interface ExtractWithTextly {
162
162
  [key: string]: JsonSchemaProperty;
163
163
  };
164
164
  }
165
+ export interface RateLimitInfo {
166
+ limit?: number;
167
+ remaining?: number;
168
+ resetTime?: string;
169
+ retryAfter?: number;
170
+ lastUpdated?: number;
171
+ }
172
+ export interface RateLimitTracker {
173
+ [clientId: string]: RateLimitInfo;
174
+ }
175
+ export interface ResilienceConfig {
176
+ fallbackEnabled?: boolean;
177
+ retryAttempts?: number;
178
+ retryDelay?: number;
179
+ }
165
180
  export * from "./rules";
166
181
  export * from "./primitives";
167
182
  export * from "./data-verifier";
@@ -1,5 +1,6 @@
1
1
  export type Email = `${string}@${string}.${string}` | string;
2
2
  type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
3
+ export type Char = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z";
3
4
  export type Phone = {
4
5
  /** The country code of the phone number. */
5
6
  iso: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dymo-api",
3
- "version": "1.2.29",
3
+ "version": "1.2.31",
4
4
  "description": "Flow system for Dymo API.",
5
5
  "main": "dist/cjs/dymo-api.js",
6
6
  "module": "dist/esm/dymo-api.js",