veripy-sdk 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -1,40 +1,75 @@
1
- # veripy-sdk
2
-
3
- The official Node.js wrapper for the Veripy Email Verification API.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install veripy-sdk
9
- ```
10
-
11
- ## Usage
12
-
13
- ```typescript
14
- import VeripyClient from 'veripy-sdk';
15
-
16
- const client = new VeripyClient({
17
- apiKey: 'vp_your_api_key_here'
18
- });
19
-
20
- async function run() {
21
- const result = await client.verify('test@example.com');
22
- console.log(result);
23
- /**
24
- * {
25
- * valid: true,
26
- * email: "test@example.com",
27
- * results: {
28
- * syntax: true,
29
- * disposable: false,
30
- * mx_records: true,
31
- * mailbox: true
32
- * },
33
- * score: 0.95,
34
- * timestamp: 1741512345678
35
- * }
36
- */
37
- }
38
-
39
- run();
40
- ```
1
+ # veripy-sdk
2
+
3
+ The official Node.js wrapper for the Veripy Email Verification API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install veripy-sdk
9
+ ```
10
+
11
+ ## Basic Usage
12
+
13
+ ```typescript
14
+ import VeripyClient from 'veripy-sdk';
15
+
16
+ const client = new VeripyClient({
17
+ apiKey: 'vp_your_api_key_here'
18
+ });
19
+
20
+ const result = await client.verify('test@example.com');
21
+ console.log(result);
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ You can configure local security features to prevent API abuse and save on credits.
27
+
28
+ ```typescript
29
+ const client = new VeripyClient({
30
+ apiKey: 'vp_your_api_key_here',
31
+ config: {
32
+ rateLimit: true, // Enable local token bucket rate limiting (Default: true)
33
+ spamDetection: true // Enable local similar-email detection (Default: true)
34
+ }
35
+ });
36
+ ```
37
+
38
+ ### Local Rate Limiting
39
+ The SDK includes a built-in token bucket rate limiter. By default, it allows a burst of **10 requests** and refills at a rate of **10 requests per minute**. This helps prevent accidental loops or spikes from consuming your API quota.
40
+
41
+ ### Local Spam Detection
42
+ The SDK monitors request patterns and blocks "highly similar" email variations (e.g., `user1@gmail.com`, `user2@gmail.com`) if more than **3 variations** are sent within a **1 minute window**.
43
+
44
+ ## Per-Call Overrides
45
+
46
+ You can override the global configuration for specific calls if needed.
47
+
48
+ ```typescript
49
+ // Bypass local checks for a specific verification
50
+ const result = await client.verify('test@example.com', {
51
+ rateLimit: false,
52
+ spamDetection: false
53
+ });
54
+ ```
55
+
56
+ ## Types
57
+
58
+ ### VerifyResult
59
+ ```typescript
60
+ interface VerifyResult {
61
+ valid: boolean;
62
+ email: string;
63
+ results: {
64
+ syntax: boolean;
65
+ disposable: boolean;
66
+ mx_records: boolean;
67
+ mailbox: boolean;
68
+ };
69
+ score: number; // 0 to 1
70
+ timestamp: number;
71
+ }
72
+ ```
73
+
74
+ ## License
75
+ ISC
@@ -0,0 +1,51 @@
1
+ interface VerifyResult {
2
+ valid: boolean;
3
+ email: string;
4
+ results: {
5
+ syntax: boolean;
6
+ disposable: boolean;
7
+ mx_records: boolean;
8
+ mailbox: boolean;
9
+ };
10
+ score: number;
11
+ timestamp: number;
12
+ }
13
+ interface VeripyConfig {
14
+ spamDetection?: boolean;
15
+ rateLimit?: boolean;
16
+ }
17
+ declare class VeripyClient {
18
+ private url;
19
+ private apiKey;
20
+ private globalConfig;
21
+ private history;
22
+ private readonly SPAM_WINDOW_MS;
23
+ private readonly MAX_SIMILAR_REQUESTS;
24
+ constructor(options: {
25
+ url?: string;
26
+ apiKey: string;
27
+ config?: VeripyConfig;
28
+ });
29
+ /**
30
+ * Helper to detect if two emails are highly similar variations
31
+ * (e.g., test1@example.com and test2@example.com)
32
+ */
33
+ private isSimilar;
34
+ /**
35
+ * Helper for built in rate limiter (Token Bucket)
36
+ */
37
+ private tokens;
38
+ private lastRefill;
39
+ private readonly MAX_TOKENS;
40
+ private readonly REFILL_RATE_PER_MS;
41
+ private checkRateLimit;
42
+ /**
43
+ * Verifies an email address.
44
+ * @param email The email address to verify.
45
+ * @param config Optional configuration to override global settings.
46
+ * @returns A Promise resolving to the verification result.
47
+ */
48
+ verify(email: string, config?: VeripyConfig): Promise<VerifyResult>;
49
+ }
50
+
51
+ export { VeripyClient, type VeripyConfig, VeripyClient as default };
package/dist/index.d.ts CHANGED
@@ -10,11 +10,11 @@ interface VerifyResult {
10
10
  score: number;
11
11
  timestamp: number;
12
12
  }
13
- export interface VeripyConfig {
13
+ interface VeripyConfig {
14
14
  spamDetection?: boolean;
15
15
  rateLimit?: boolean;
16
16
  }
17
- export declare class VeripyClient {
17
+ declare class VeripyClient {
18
18
  private url;
19
19
  private apiKey;
20
20
  private globalConfig;
@@ -47,4 +47,5 @@ export declare class VeripyClient {
47
47
  */
48
48
  verify(email: string, config?: VeripyConfig): Promise<VerifyResult>;
49
49
  }
50
- export default VeripyClient;
50
+
51
+ export { VeripyClient, type VeripyConfig, VeripyClient as default };
package/dist/index.js CHANGED
@@ -1,105 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VeripyClient = void 0;
4
- class VeripyClient {
5
- url;
6
- apiKey;
7
- globalConfig;
8
- // Local memory cache for spam detection
9
- history = [];
10
- SPAM_WINDOW_MS = 60000; // 1 minute window
11
- MAX_SIMILAR_REQUESTS = 3; // Block after 3 similar variations
12
- constructor(options) {
13
- this.url =
14
- options.url || "https://lovable-alpaca-951.eu-west-1.convex.site";
15
- if (!options.apiKey) {
16
- throw new Error("VeripyClient requires an apiKey.");
17
- }
18
- this.apiKey = options.apiKey;
19
- this.globalConfig = {
20
- spamDetection: options.config?.spamDetection ?? true,
21
- rateLimit: options.config?.rateLimit ?? true,
22
- };
23
- }
24
- /**
25
- * Helper to detect if two emails are highly similar variations
26
- * (e.g., test1@example.com and test2@example.com)
27
- */
28
- isSimilar(email1, email2) {
29
- const [local1, domain1] = email1.toLowerCase().split("@");
30
- const [local2, domain2] = email2.toLowerCase().split("@");
31
- if (domain1 !== domain2)
32
- return false;
33
- // Strip trailing numbers from the local part (e.g. 'test12' -> 'test')
34
- const base1 = local1.replace(/\d+$/, "");
35
- const base2 = local2.replace(/\d+$/, "");
36
- // Also catch "plus addressing" variations (e.g. user+1@gmail.com and user+2@gmail.com)
37
- const plusBase1 = local1.split("+")[0];
38
- const plusBase2 = local2.split("+")[0];
39
- return base1 === base2 || plusBase1 === plusBase2;
40
- }
41
- /**
42
- * Helper for built in rate limiter (Token Bucket)
43
- */
44
- tokens = 10; // default starting burst
45
- lastRefill = Date.now();
46
- MAX_TOKENS = 10;
47
- REFILL_RATE_PER_MS = 10 / (60 * 1000); // 10 tokens per minute
48
- checkRateLimit() {
49
- const now = Date.now();
50
- const timePassed = now - this.lastRefill;
51
- // Refill tokens based on time passed
52
- this.tokens = Math.min(this.MAX_TOKENS, this.tokens + timePassed * this.REFILL_RATE_PER_MS);
53
- this.lastRefill = now;
54
- if (this.tokens < 1) {
55
- throw new Error("Veripy Error: Rate limit exceeded locally. Slow down your requests.");
56
- }
57
- this.tokens -= 1;
58
- }
59
- /**
60
- * Verifies an email address.
61
- * @param email The email address to verify.
62
- * @param config Optional configuration to override global settings.
63
- * @returns A Promise resolving to the verification result.
64
- */
65
- async verify(email, config) {
66
- if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
67
- throw new Error("Veripy Error: Invalid email format provided to SDK.");
68
- }
69
- const activeConfig = { ...this.globalConfig, ...config };
70
- const now = Date.now();
71
- // 1. Local Rate Limiting
72
- if (activeConfig.rateLimit) {
73
- this.checkRateLimit();
74
- }
75
- // 2. Local Spam Detection
76
- if (activeConfig.spamDetection) {
77
- // Clean up history older than our window
78
- this.history = this.history.filter((req) => now - req.timestamp < this.SPAM_WINDOW_MS);
79
- // Check for spam behavior locally
80
- const similarCount = this.history.filter((req) => this.isSimilar(req.email, email)).length;
81
- if (similarCount >= this.MAX_SIMILAR_REQUESTS) {
82
- throw new Error("Veripy Error: Spam behavior detected. Too many highly similar requests locally.");
83
- }
84
- // Add to local history
85
- this.history.push({ email, timestamp: now });
86
- }
87
- const response = await fetch(`${this.url}/v1/verify`, {
88
- method: "POST",
89
- headers: {
90
- "Content-Type": "application/json",
91
- "x-api-key": this.apiKey,
92
- },
93
- body: JSON.stringify({ email }),
94
- });
95
- if (!response.ok) {
96
- const error = await response
97
- .json()
98
- .catch(() => ({ error: response.statusText }));
99
- throw new Error(`Veripy API Error: ${response.status} - ${error.error || response.statusText}`);
100
- }
101
- return (await response.json());
102
- }
103
- }
104
- exports.VeripyClient = VeripyClient;
105
- exports.default = VeripyClient;
1
+ "use strict";var p=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var d=(s,t)=>{for(var e in t)p(s,e,{get:t[e],enumerable:!0})},g=(s,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of f(t))!u.call(s,i)&&i!==e&&p(s,i,{get:()=>t[i],enumerable:!(o=y(t,i))||o.enumerable});return s};var b=s=>g(p({},"__esModule",{value:!0}),s);var w={};d(w,{VeripyClient:()=>l,default:()=>v});module.exports=b(w);var l=class{url;apiKey;globalConfig;history=[];SPAM_WINDOW_MS=6e4;MAX_SIMILAR_REQUESTS=3;constructor(t){if(this.url=t.url||"https://lovable-alpaca-951.eu-west-1.convex.site",!t.apiKey)throw new Error("VeripyClient requires an apiKey.");this.apiKey=t.apiKey,this.globalConfig={spamDetection:t.config?.spamDetection??!0,rateLimit:t.config?.rateLimit??!0}}isSimilar(t,e){let[o,i]=t.toLowerCase().split("@"),[r,n]=e.toLowerCase().split("@");if(i!==n)return!1;let a=o.replace(/\d+$/,""),c=r.replace(/\d+$/,""),h=o.split("+")[0],m=r.split("+")[0];return a===c||h===m}tokens=10;lastRefill=Date.now();MAX_TOKENS=10;REFILL_RATE_PER_MS=10/(60*1e3);checkRateLimit(){let t=Date.now(),e=t-this.lastRefill;if(this.tokens=Math.min(this.MAX_TOKENS,this.tokens+e*this.REFILL_RATE_PER_MS),this.lastRefill=t,this.tokens<1)throw new Error("Veripy Error: Rate limit exceeded locally. Slow down your requests.");this.tokens-=1}async verify(t,e){if(!t||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t))throw new Error("Veripy Error: Invalid email format provided to SDK.");let o={...this.globalConfig,...e},i=Date.now();if(o.rateLimit&&this.checkRateLimit(),o.spamDetection){if(this.history=this.history.filter(a=>i-a.timestamp<this.SPAM_WINDOW_MS),this.history.filter(a=>this.isSimilar(a.email,t)).length>=this.MAX_SIMILAR_REQUESTS)throw new Error("Veripy Error: Spam behavior detected. Too many highly similar requests locally.");this.history.push({email:t,timestamp:i})}let r=await fetch(`${this.url}/v1/verify`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({email:t})});if(!r.ok){let n=await r.json().catch(()=>({error:r.statusText}));throw new Error(`Veripy API Error: ${r.status} - ${n.error||r.statusText}`)}return await r.json()}},v=l;0&&(module.exports={VeripyClient});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var n=class{url;apiKey;globalConfig;history=[];SPAM_WINDOW_MS=6e4;MAX_SIMILAR_REQUESTS=3;constructor(t){if(this.url=t.url||"https://lovable-alpaca-951.eu-west-1.convex.site",!t.apiKey)throw new Error("VeripyClient requires an apiKey.");this.apiKey=t.apiKey,this.globalConfig={spamDetection:t.config?.spamDetection??!0,rateLimit:t.config?.rateLimit??!0}}isSimilar(t,i){let[r,o]=t.toLowerCase().split("@"),[e,a]=i.toLowerCase().split("@");if(o!==a)return!1;let s=r.replace(/\d+$/,""),l=e.replace(/\d+$/,""),p=r.split("+")[0],c=e.split("+")[0];return s===l||p===c}tokens=10;lastRefill=Date.now();MAX_TOKENS=10;REFILL_RATE_PER_MS=10/(60*1e3);checkRateLimit(){let t=Date.now(),i=t-this.lastRefill;if(this.tokens=Math.min(this.MAX_TOKENS,this.tokens+i*this.REFILL_RATE_PER_MS),this.lastRefill=t,this.tokens<1)throw new Error("Veripy Error: Rate limit exceeded locally. Slow down your requests.");this.tokens-=1}async verify(t,i){if(!t||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t))throw new Error("Veripy Error: Invalid email format provided to SDK.");let r={...this.globalConfig,...i},o=Date.now();if(r.rateLimit&&this.checkRateLimit(),r.spamDetection){if(this.history=this.history.filter(s=>o-s.timestamp<this.SPAM_WINDOW_MS),this.history.filter(s=>this.isSimilar(s.email,t)).length>=this.MAX_SIMILAR_REQUESTS)throw new Error("Veripy Error: Spam behavior detected. Too many highly similar requests locally.");this.history.push({email:t,timestamp:o})}let e=await fetch(`${this.url}/v1/verify`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({email:t})});if(!e.ok){let a=await e.json().catch(()=>({error:e.statusText}));throw new Error(`Veripy API Error: ${e.status} - ${a.error||e.statusText}`)}return await e.json()}},m=n;export{n as VeripyClient,m as default};
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "veripy-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Email verification wrapper for Veripy",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
7
8
  "scripts": {
8
- "build": "tsc",
9
+ "build": "tsup src/index.ts --format cjs,esm --dts --minify --clean",
9
10
  "test": "vitest run",
10
11
  "test:watch": "vitest"
11
12
  },
@@ -32,12 +33,14 @@
32
33
  "exports": {
33
34
  ".": {
34
35
  "types": "./dist/index.d.ts",
35
- "default": "./dist/index.js"
36
+ "import": "./dist/index.mjs",
37
+ "require": "./dist/index.js"
36
38
  }
37
39
  },
38
40
  "devDependencies": {
39
41
  "@types/node": "^20.0.0",
42
+ "tsup": "^8.5.1",
40
43
  "typescript": "^5.0.0",
41
44
  "vitest": "^4.1.0"
42
45
  }
43
- }
46
+ }