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 +75 -40
- package/dist/index.d.mts +51 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -105
- package/dist/index.mjs +1 -0
- package/package.json +9 -6
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
package/dist/index.d.mts
ADDED
|
@@ -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
|
-
|
|
13
|
+
interface VeripyConfig {
|
|
14
14
|
spamDetection?: boolean;
|
|
15
15
|
rateLimit?: boolean;
|
|
16
16
|
}
|
|
17
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Email verification wrapper for Veripy",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
7
8
|
"scripts": {
|
|
8
|
-
"build": "
|
|
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
|
-
"
|
|
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
|
+
}
|