veripy-sdk 1.0.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/LICENSE.md +15 -0
- package/README.md +40 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +105 -0
- package/package.json +43 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Veripy
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,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
|
+
## 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
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
export interface VeripyConfig {
|
|
14
|
+
spamDetection?: boolean;
|
|
15
|
+
rateLimit?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export 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
|
+
export default VeripyClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
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;
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "veripy-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Email verification wrapper for Veripy",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"test:watch": "vitest"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"verification",
|
|
17
|
+
"email",
|
|
18
|
+
"veripy",
|
|
19
|
+
"spam"
|
|
20
|
+
],
|
|
21
|
+
"author": "Richard Banguiz",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/your-org/veripy.git",
|
|
26
|
+
"directory": "sdk"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/your-org/veripy/tree/main/sdk#readme",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/your-org/veripy/issues"
|
|
31
|
+
},
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"typescript": "^5.0.0",
|
|
41
|
+
"vitest": "^4.1.0"
|
|
42
|
+
}
|
|
43
|
+
}
|