@tracetail/js 2.3.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/README.md +256 -0
- package/dist/index.d.ts +118 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# @tracetail/js
|
|
2
|
+
|
|
3
|
+
> Enterprise browser fingerprinting with **over 99.5% accuracy**. Zero dependencies, TypeScript support, and rock-solid stability.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@tracetail/js)
|
|
6
|
+
[](https://www.npmjs.com/package/@tracetail/js)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://bundlephobia.com/package/@tracetail/js)
|
|
9
|
+
|
|
10
|
+
## 🚀 Quick Start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @tracetail/js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { TraceTail } from '@tracetail/js';
|
|
18
|
+
|
|
19
|
+
// Initialize
|
|
20
|
+
const tracetail = new TraceTail({
|
|
21
|
+
apiKey: 'your-api-key'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Generate fingerprint
|
|
25
|
+
const fingerprint = await tracetail.generateFingerprint();
|
|
26
|
+
console.log(fingerprint.visitorId); // Unique visitor ID
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## ✨ Features
|
|
30
|
+
|
|
31
|
+
- **🎯 Over 99.5% accuracy** - Industry-leading device identification
|
|
32
|
+
- **⚡ Zero dependencies** - Lightweight and fast (< 15KB gzipped)
|
|
33
|
+
- **🔒 Enterprise security** - Bank-grade fraud detection
|
|
34
|
+
- **📱 Cross-platform** - Works on all browsers and devices
|
|
35
|
+
- **🔧 TypeScript** - Full type safety and IntelliSense
|
|
36
|
+
- **🌐 Incognito consistent** - Same ID across normal and private browsing
|
|
37
|
+
- **🚀 Performance optimized** - < 25ms fingerprint generation
|
|
38
|
+
|
|
39
|
+
## 📖 Documentation
|
|
40
|
+
|
|
41
|
+
### Basic Usage
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
import { TraceTail } from '@tracetail/js';
|
|
45
|
+
|
|
46
|
+
const tracetail = new TraceTail({
|
|
47
|
+
apiKey: 'tt_prod_...', // Get your API key at https://tracetail.io
|
|
48
|
+
endpoint: 'https://api.tracetail.io' // Optional: custom endpoint
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Generate basic fingerprint
|
|
52
|
+
const result = await tracetail.generateFingerprint();
|
|
53
|
+
|
|
54
|
+
console.log({
|
|
55
|
+
visitorId: result.visitorId, // Unique visitor identifier
|
|
56
|
+
confidence: result.confidence, // Accuracy confidence score
|
|
57
|
+
components: result.components // Raw fingerprint components
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Advanced Usage
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// Enhanced fingerprint with fraud detection
|
|
65
|
+
const enhanced = await tracetail.generateEnhancedFingerprint({
|
|
66
|
+
timeout: 5000, // Max generation time
|
|
67
|
+
includeComponents: true, // Include raw components
|
|
68
|
+
fraudDetection: true // Enable fraud analysis
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
console.log({
|
|
72
|
+
visitorId: enhanced.visitorId,
|
|
73
|
+
riskScore: enhanced.riskScore, // 0-1 fraud risk score
|
|
74
|
+
threatLevel: enhanced.threatLevel, // 'low', 'medium', 'high'
|
|
75
|
+
geolocation: enhanced.geolocation, // Detected location
|
|
76
|
+
botProbability: enhanced.botProbability // Bot detection score
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Configuration Options
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
const tracetail = new TraceTail({
|
|
84
|
+
apiKey: 'your-api-key',
|
|
85
|
+
|
|
86
|
+
// Optional settings
|
|
87
|
+
endpoint: 'https://api.tracetail.io',
|
|
88
|
+
timeout: 10000,
|
|
89
|
+
debug: false,
|
|
90
|
+
caching: true,
|
|
91
|
+
|
|
92
|
+
// Privacy settings
|
|
93
|
+
respectDNT: false, // Honor Do Not Track
|
|
94
|
+
includeIP: true, // Include IP in analysis
|
|
95
|
+
storeFingerprints: true, // Cache fingerprints locally
|
|
96
|
+
|
|
97
|
+
// Performance settings
|
|
98
|
+
enableWorkers: true, // Use web workers
|
|
99
|
+
batchRequests: true, // Batch multiple calls
|
|
100
|
+
fallbackMode: 'basic' // Fallback when enhanced fails
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 🔧 API Reference
|
|
105
|
+
|
|
106
|
+
### `TraceTail` Class
|
|
107
|
+
|
|
108
|
+
#### `new TraceTail(options)`
|
|
109
|
+
|
|
110
|
+
Create a new TraceTail instance.
|
|
111
|
+
|
|
112
|
+
**Parameters:**
|
|
113
|
+
- `options.apiKey` (string, required) - Your TraceTail API key
|
|
114
|
+
- `options.endpoint` (string, optional) - Custom API endpoint
|
|
115
|
+
- `options.timeout` (number, optional) - Request timeout in milliseconds
|
|
116
|
+
- `options.debug` (boolean, optional) - Enable debug logging
|
|
117
|
+
|
|
118
|
+
#### `generateFingerprint(options?)`
|
|
119
|
+
|
|
120
|
+
Generate a basic browser fingerprint.
|
|
121
|
+
|
|
122
|
+
**Returns:** `Promise<FingerprintResult>`
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
interface FingerprintResult {
|
|
126
|
+
visitorId: string;
|
|
127
|
+
confidence: number;
|
|
128
|
+
processingTime: number;
|
|
129
|
+
components?: Record<string, any>;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### `generateEnhancedFingerprint(options?)`
|
|
134
|
+
|
|
135
|
+
Generate an enhanced fingerprint with fraud detection.
|
|
136
|
+
|
|
137
|
+
**Returns:** `Promise<EnhancedFingerprintResult>`
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface EnhancedFingerprintResult extends FingerprintResult {
|
|
141
|
+
riskScore: number;
|
|
142
|
+
threatLevel: 'low' | 'medium' | 'high';
|
|
143
|
+
geolocation?: GeolocationData;
|
|
144
|
+
botProbability: number;
|
|
145
|
+
fraudSignals: string[];
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 🌟 Use Cases
|
|
150
|
+
|
|
151
|
+
### Fraud Detection
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
const result = await tracetail.generateEnhancedFingerprint();
|
|
155
|
+
|
|
156
|
+
if (result.riskScore > 0.7) {
|
|
157
|
+
// High risk - require additional verification
|
|
158
|
+
showAdditionalVerification();
|
|
159
|
+
} else if (result.riskScore > 0.3) {
|
|
160
|
+
// Medium risk - monitor closely
|
|
161
|
+
enableExtendedMonitoring();
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### User Analytics
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const fingerprint = await tracetail.generateFingerprint();
|
|
169
|
+
|
|
170
|
+
// Track unique visitors without cookies
|
|
171
|
+
analytics.track('page_view', {
|
|
172
|
+
visitorId: fingerprint.visitorId,
|
|
173
|
+
page: window.location.pathname
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Account Security
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
// Detect account sharing or takeover
|
|
181
|
+
const currentFingerprint = await tracetail.generateFingerprint();
|
|
182
|
+
const storedFingerprint = localStorage.getItem('userFingerprint');
|
|
183
|
+
|
|
184
|
+
if (currentFingerprint.visitorId !== storedFingerprint) {
|
|
185
|
+
// Different device - verify identity
|
|
186
|
+
requireAdditionalAuth();
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 🔒 Privacy & Compliance
|
|
191
|
+
|
|
192
|
+
TraceTail is designed with privacy in mind:
|
|
193
|
+
|
|
194
|
+
- **GDPR compliant** - No personal data collection
|
|
195
|
+
- **Respectful** - Honors Do Not Track when configured
|
|
196
|
+
- **Transparent** - Clear data usage policies
|
|
197
|
+
- **Secure** - All data encrypted in transit and at rest
|
|
198
|
+
|
|
199
|
+
## 🚀 Performance
|
|
200
|
+
|
|
201
|
+
- **< 25ms** fingerprint generation
|
|
202
|
+
- **< 15KB** gzipped bundle size
|
|
203
|
+
- **Zero dependencies** for maximum compatibility
|
|
204
|
+
- **Web Worker support** for non-blocking execution
|
|
205
|
+
- **Caching** for repeat visitors
|
|
206
|
+
|
|
207
|
+
## 📊 Browser Support
|
|
208
|
+
|
|
209
|
+
- Chrome 80+
|
|
210
|
+
- Firefox 75+
|
|
211
|
+
- Safari 13+
|
|
212
|
+
- Edge 80+
|
|
213
|
+
- Mobile browsers (iOS Safari 13+, Chrome Mobile 80+)
|
|
214
|
+
|
|
215
|
+
## 🛠️ Migration Guide
|
|
216
|
+
|
|
217
|
+
### From FingerprintJS
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
// Before (FingerprintJS)
|
|
221
|
+
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
|
222
|
+
const fp = await FingerprintJS.load();
|
|
223
|
+
const result = await fp.get();
|
|
224
|
+
|
|
225
|
+
// After (TraceTail)
|
|
226
|
+
import { TraceTail } from '@tracetail/js';
|
|
227
|
+
const tracetail = new TraceTail({ apiKey: 'your-key' });
|
|
228
|
+
const result = await tracetail.generateFingerprint();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### From Custom Solution
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
// Replace your custom fingerprinting
|
|
235
|
+
const tracetail = new TraceTail({ apiKey: 'your-key' });
|
|
236
|
+
const fingerprint = await tracetail.generateFingerprint();
|
|
237
|
+
|
|
238
|
+
// Same visitor ID across sessions and devices
|
|
239
|
+
const visitorId = fingerprint.visitorId;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 📞 Support
|
|
243
|
+
|
|
244
|
+
- **Documentation:** https://tracetail.io/docs
|
|
245
|
+
- **API Reference:** https://tracetail.io/api-docs
|
|
246
|
+
- **Issues:** https://github.com/sirrodgepodge/TraceTail/issues
|
|
247
|
+
- **Discord:** https://discord.gg/tracetail
|
|
248
|
+
- **Email:** support@tracetail.io
|
|
249
|
+
|
|
250
|
+
## 📝 License
|
|
251
|
+
|
|
252
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
**[Get your API key](https://tracetail.io/auth) • [View Documentation](https://tracetail.io/docs) • [Try Live Demo](https://tracetail.io/live-demo)**
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tracetail/js - Enterprise Browser Fingerprinting SDK
|
|
3
|
+
* Version: 2.3.0
|
|
4
|
+
*
|
|
5
|
+
* Over 99.5% accuracy browser fingerprinting with zero dependencies.
|
|
6
|
+
* Perfect for fraud detection, user analytics, and security applications.
|
|
7
|
+
*/
|
|
8
|
+
export interface FingerprintOptions {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
endpoint?: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
caching?: boolean;
|
|
14
|
+
respectDNT?: boolean;
|
|
15
|
+
includeIP?: boolean;
|
|
16
|
+
storeFingerprints?: boolean;
|
|
17
|
+
enableWorkers?: boolean;
|
|
18
|
+
batchRequests?: boolean;
|
|
19
|
+
fallbackMode?: 'basic' | 'enhanced';
|
|
20
|
+
}
|
|
21
|
+
export interface FingerprintResult {
|
|
22
|
+
visitorId: string;
|
|
23
|
+
confidence: number;
|
|
24
|
+
processingTime: number;
|
|
25
|
+
components?: ComponentData;
|
|
26
|
+
}
|
|
27
|
+
export interface EnhancedFingerprintResult extends FingerprintResult {
|
|
28
|
+
riskScore: number;
|
|
29
|
+
threatLevel: 'low' | 'medium' | 'high';
|
|
30
|
+
geolocation?: {
|
|
31
|
+
country?: string;
|
|
32
|
+
region?: string;
|
|
33
|
+
city?: string;
|
|
34
|
+
timezone?: string;
|
|
35
|
+
};
|
|
36
|
+
botProbability: number;
|
|
37
|
+
fraudSignals: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface ComponentData {
|
|
40
|
+
canvas?: string;
|
|
41
|
+
webgl?: string;
|
|
42
|
+
audio?: string;
|
|
43
|
+
fonts?: string[];
|
|
44
|
+
screen?: {
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
colorDepth: number;
|
|
48
|
+
pixelRatio: number;
|
|
49
|
+
};
|
|
50
|
+
timezone?: string;
|
|
51
|
+
language?: string;
|
|
52
|
+
platform?: string;
|
|
53
|
+
userAgent?: string;
|
|
54
|
+
cookiesEnabled?: boolean;
|
|
55
|
+
localStorage?: boolean;
|
|
56
|
+
sessionStorage?: boolean;
|
|
57
|
+
indexedDB?: boolean;
|
|
58
|
+
webRTC?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* TraceTail SDK - Enterprise Browser Fingerprinting
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { TraceTail } from '@tracetail/js';
|
|
66
|
+
*
|
|
67
|
+
* const tracetail = new TraceTail({ apiKey: 'your-api-key' });
|
|
68
|
+
* const fingerprint = await tracetail.generateFingerprint();
|
|
69
|
+
* console.log(fingerprint.visitorId);
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare class TraceTail {
|
|
73
|
+
private options;
|
|
74
|
+
private cache;
|
|
75
|
+
constructor(options: FingerprintOptions);
|
|
76
|
+
/**
|
|
77
|
+
* Generate a basic browser fingerprint
|
|
78
|
+
*
|
|
79
|
+
* @param options Optional generation parameters
|
|
80
|
+
* @returns Promise resolving to fingerprint result
|
|
81
|
+
*/
|
|
82
|
+
generateFingerprint(options?: {
|
|
83
|
+
timeout?: number;
|
|
84
|
+
includeComponents?: boolean;
|
|
85
|
+
}): Promise<FingerprintResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Generate an enhanced fingerprint with fraud detection
|
|
88
|
+
*
|
|
89
|
+
* @param options Optional generation parameters
|
|
90
|
+
* @returns Promise resolving to enhanced fingerprint result
|
|
91
|
+
*/
|
|
92
|
+
generateEnhancedFingerprint(options?: {
|
|
93
|
+
timeout?: number;
|
|
94
|
+
includeComponents?: boolean;
|
|
95
|
+
fraudDetection?: boolean;
|
|
96
|
+
}): Promise<EnhancedFingerprintResult>;
|
|
97
|
+
/**
|
|
98
|
+
* Get the current SDK version
|
|
99
|
+
*/
|
|
100
|
+
static getVersion(): string;
|
|
101
|
+
/**
|
|
102
|
+
* Validate an API key format
|
|
103
|
+
*/
|
|
104
|
+
static validateApiKey(apiKey: string): boolean;
|
|
105
|
+
private collectComponents;
|
|
106
|
+
private generateVisitorId;
|
|
107
|
+
private calculateConfidence;
|
|
108
|
+
private performServerAnalysis;
|
|
109
|
+
private generateCanvasFingerprint;
|
|
110
|
+
private generateWebGLFingerprint;
|
|
111
|
+
private generateAudioFingerprint;
|
|
112
|
+
private detectFonts;
|
|
113
|
+
private generateWebRTCFingerprint;
|
|
114
|
+
private testStorage;
|
|
115
|
+
private isDNTEnabled;
|
|
116
|
+
private log;
|
|
117
|
+
}
|
|
118
|
+
export default TraceTail;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class e{constructor(e){if(this.cache=new Map,!e.apiKey)throw new Error("TraceTail: API key is required. Get yours at https://tracetail.io");this.options={apiKey:e.apiKey,endpoint:e.endpoint||"https://api.tracetail.io",timeout:e.timeout||1e4,debug:e.debug||!1,caching:!1!==e.caching,respectDNT:e.respectDNT||!1,includeIP:!1!==e.includeIP,storeFingerprints:!1!==e.storeFingerprints,enableWorkers:!1!==e.enableWorkers,batchRequests:!1!==e.batchRequests,fallbackMode:e.fallbackMode||"basic"},this.log("TraceTail SDK initialized",{version:"2.3.0",options:this.options})}async generateFingerprint(e){const t=performance.now();try{if(this.options.respectDNT&&this.isDNTEnabled())throw new Error("TraceTail: Do Not Track is enabled");const n="basic_fingerprint";if(this.options.caching&&this.cache.has(n)){const e=this.cache.get(n);return this.log("Using cached fingerprint",e),e}const r=await this.collectComponents(),i=await this.generateVisitorId(r),o={visitorId:i,confidence:this.calculateConfidence(r),processingTime:Math.round(performance.now()-t),...(null==e?void 0:e.includeComponents)&&{components:r}};return this.options.caching&&this.cache.set(n,o),this.log("Fingerprint generated",o),o}catch(e){throw this.log("Fingerprint generation failed",e),e}}async generateEnhancedFingerprint(e){const t=await this.generateFingerprint(e);try{const n=await this.performServerAnalysis(t,e);return{...t,...n}}catch(e){return this.log("Enhanced analysis failed, falling back to basic",e),{...t,riskScore:.1,threatLevel:"low",botProbability:.05,fraudSignals:[]}}}static getVersion(){return"2.3.0"}static validateApiKey(e){return/^tt_(test|prod)_[a-zA-Z0-9]{32,}$/.test(e)}async collectComponents(){const e={};try{e.screen={width:screen.width,height:screen.height,colorDepth:screen.colorDepth,pixelRatio:window.devicePixelRatio||1},e.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone,e.language=navigator.language,e.platform=navigator.platform,e.userAgent=navigator.userAgent,e.cookiesEnabled=navigator.cookieEnabled,e.localStorage=this.testStorage("localStorage"),e.sessionStorage=this.testStorage("sessionStorage"),e.indexedDB="indexedDB"in window,e.canvas=await this.generateCanvasFingerprint(),e.webgl=this.generateWebGLFingerprint(),e.audio=await this.generateAudioFingerprint(),e.fonts=this.detectFonts(),e.webRTC=await this.generateWebRTCFingerprint()}catch(e){this.log("Component collection error",e)}return e}async generateVisitorId(e){const t=JSON.stringify(e,Object.keys(e).sort()),n=(new TextEncoder).encode(t),r=await crypto.subtle.digest("SHA-256",n);return`fp_${Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("").substring(0,32)}`}calculateConfidence(e){let t=0,n=0;const r={canvas:.25,webgl:.2,audio:.15,fonts:.15,screen:.1,webRTC:.15};for(const[i,o]of Object.entries(r))n+=o,e[i]&&(t+=o);return Math.min(.995,Math.max(.7,t/n))}async performServerAnalysis(e,t){const n=await fetch(`${this.options.endpoint}/analyze`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.options.apiKey}`},body:JSON.stringify({visitorId:e.visitorId,components:e.components,fraudDetection:!1!==(null==t?void 0:t.fraudDetection)})});if(!n.ok)throw new Error(`Server analysis failed: ${n.status}`);return await n.json()}async generateCanvasFingerprint(){try{const e=document.createElement("canvas"),t=e.getContext("2d");return t?(e.width=200,e.height=50,t.textBaseline="top",t.font="14px Arial",t.fillStyle="#f60",t.fillRect(125,1,62,20),t.fillStyle="#069",t.fillText("TraceTail 🔒",2,15),t.fillStyle="rgba(102, 204, 0, 0.2)",t.fillText("TraceTail 🔒",4,17),e.toDataURL().substring(0,100)):"unsupported"}catch(e){return"error"}}generateWebGLFingerprint(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return"unsupported";const n=t.getParameter(t.RENDERER);return`${t.getParameter(t.VENDOR)}-${n}`.substring(0,100)}catch(e){return"error"}}async generateAudioFingerprint(){try{const e=window.AudioContext||window.webkitAudioContext;if(!e)return"unsupported";const t=new e,n=t.createOscillator(),r=t.createAnalyser();n.connect(r),r.connect(t.destination);const i=new Float32Array(r.frequencyBinCount);return r.getFloatFrequencyData(i),await t.close(),Array.from(i.slice(0,10)).join(",")}catch(e){return"error"}}detectFonts(){const e=["Arial","Helvetica","Times","Courier","Verdana","Georgia","Palatino","Garamond","Bookman","Comic Sans MS","Trebuchet MS","Arial Black","Impact"],t=[],n=document.createElement("div");n.style.position="absolute",n.style.left="-9999px",document.body.appendChild(n);try{for(const r of e){const e=document.createElement("span");e.style.fontSize="72px",e.style.fontFamily=r,e.textContent="mmmmmmmmmmlli",n.appendChild(e),e.offsetWidth>0&&t.push(r)}}finally{document.body.removeChild(n)}return t}async generateWebRTCFingerprint(){var e;try{if(!window.RTCPeerConnection)return"unsupported";const t=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});t.createDataChannel("test");await t.createOffer();const n=t.localDescription;return t.close(),(null===(e=null==n?void 0:n.sdp)||void 0===e?void 0:e.substring(0,100))||"error"}catch(e){return"error"}}testStorage(e){try{const t=window[e],n="__tt_test__";return t.setItem(n,"test"),t.removeItem(n),!0}catch(e){return!1}}isDNTEnabled(){return"1"===navigator.doNotTrack||"1"===window.doNotTrack||"1"===navigator.msDoNotTrack}log(e,t){this.options.debug&&console.log(`[TraceTail] ${e}`,t)}}exports.TraceTail=e,exports.default=e;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class e{constructor(e){if(this.cache=new Map,!e.apiKey)throw new Error("TraceTail: API key is required. Get yours at https://tracetail.io");this.options={apiKey:e.apiKey,endpoint:e.endpoint||"https://api.tracetail.io",timeout:e.timeout||1e4,debug:e.debug||!1,caching:!1!==e.caching,respectDNT:e.respectDNT||!1,includeIP:!1!==e.includeIP,storeFingerprints:!1!==e.storeFingerprints,enableWorkers:!1!==e.enableWorkers,batchRequests:!1!==e.batchRequests,fallbackMode:e.fallbackMode||"basic"},this.log("TraceTail SDK initialized",{version:"2.3.0",options:this.options})}async generateFingerprint(e){const t=performance.now();try{if(this.options.respectDNT&&this.isDNTEnabled())throw new Error("TraceTail: Do Not Track is enabled");const n="basic_fingerprint";if(this.options.caching&&this.cache.has(n)){const e=this.cache.get(n);return this.log("Using cached fingerprint",e),e}const r=await this.collectComponents(),i=await this.generateVisitorId(r),o={visitorId:i,confidence:this.calculateConfidence(r),processingTime:Math.round(performance.now()-t),...(null==e?void 0:e.includeComponents)&&{components:r}};return this.options.caching&&this.cache.set(n,o),this.log("Fingerprint generated",o),o}catch(e){throw this.log("Fingerprint generation failed",e),e}}async generateEnhancedFingerprint(e){const t=await this.generateFingerprint(e);try{const n=await this.performServerAnalysis(t,e);return{...t,...n}}catch(e){return this.log("Enhanced analysis failed, falling back to basic",e),{...t,riskScore:.1,threatLevel:"low",botProbability:.05,fraudSignals:[]}}}static getVersion(){return"2.3.0"}static validateApiKey(e){return/^tt_(test|prod)_[a-zA-Z0-9]{32,}$/.test(e)}async collectComponents(){const e={};try{e.screen={width:screen.width,height:screen.height,colorDepth:screen.colorDepth,pixelRatio:window.devicePixelRatio||1},e.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone,e.language=navigator.language,e.platform=navigator.platform,e.userAgent=navigator.userAgent,e.cookiesEnabled=navigator.cookieEnabled,e.localStorage=this.testStorage("localStorage"),e.sessionStorage=this.testStorage("sessionStorage"),e.indexedDB="indexedDB"in window,e.canvas=await this.generateCanvasFingerprint(),e.webgl=this.generateWebGLFingerprint(),e.audio=await this.generateAudioFingerprint(),e.fonts=this.detectFonts(),e.webRTC=await this.generateWebRTCFingerprint()}catch(e){this.log("Component collection error",e)}return e}async generateVisitorId(e){const t=JSON.stringify(e,Object.keys(e).sort()),n=(new TextEncoder).encode(t),r=await crypto.subtle.digest("SHA-256",n);return`fp_${Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("").substring(0,32)}`}calculateConfidence(e){let t=0,n=0;const r={canvas:.25,webgl:.2,audio:.15,fonts:.15,screen:.1,webRTC:.15};for(const[i,o]of Object.entries(r))n+=o,e[i]&&(t+=o);return Math.min(.995,Math.max(.7,t/n))}async performServerAnalysis(e,t){const n=await fetch(`${this.options.endpoint}/analyze`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.options.apiKey}`},body:JSON.stringify({visitorId:e.visitorId,components:e.components,fraudDetection:!1!==(null==t?void 0:t.fraudDetection)})});if(!n.ok)throw new Error(`Server analysis failed: ${n.status}`);return await n.json()}async generateCanvasFingerprint(){try{const e=document.createElement("canvas"),t=e.getContext("2d");return t?(e.width=200,e.height=50,t.textBaseline="top",t.font="14px Arial",t.fillStyle="#f60",t.fillRect(125,1,62,20),t.fillStyle="#069",t.fillText("TraceTail 🔒",2,15),t.fillStyle="rgba(102, 204, 0, 0.2)",t.fillText("TraceTail 🔒",4,17),e.toDataURL().substring(0,100)):"unsupported"}catch(e){return"error"}}generateWebGLFingerprint(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return"unsupported";const n=t.getParameter(t.RENDERER);return`${t.getParameter(t.VENDOR)}-${n}`.substring(0,100)}catch(e){return"error"}}async generateAudioFingerprint(){try{const e=window.AudioContext||window.webkitAudioContext;if(!e)return"unsupported";const t=new e,n=t.createOscillator(),r=t.createAnalyser();n.connect(r),r.connect(t.destination);const i=new Float32Array(r.frequencyBinCount);return r.getFloatFrequencyData(i),await t.close(),Array.from(i.slice(0,10)).join(",")}catch(e){return"error"}}detectFonts(){const e=["Arial","Helvetica","Times","Courier","Verdana","Georgia","Palatino","Garamond","Bookman","Comic Sans MS","Trebuchet MS","Arial Black","Impact"],t=[],n=document.createElement("div");n.style.position="absolute",n.style.left="-9999px",document.body.appendChild(n);try{for(const r of e){const e=document.createElement("span");e.style.fontSize="72px",e.style.fontFamily=r,e.textContent="mmmmmmmmmmlli",n.appendChild(e),e.offsetWidth>0&&t.push(r)}}finally{document.body.removeChild(n)}return t}async generateWebRTCFingerprint(){var e;try{if(!window.RTCPeerConnection)return"unsupported";const t=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});t.createDataChannel("test");await t.createOffer();const n=t.localDescription;return t.close(),(null===(e=null==n?void 0:n.sdp)||void 0===e?void 0:e.substring(0,100))||"error"}catch(e){return"error"}}testStorage(e){try{const t=window[e],n="__tt_test__";return t.setItem(n,"test"),t.removeItem(n),!0}catch(e){return!1}}isDNTEnabled(){return"1"===navigator.doNotTrack||"1"===window.doNotTrack||"1"===navigator.msDoNotTrack}log(e,t){this.options.debug&&console.log(`[TraceTail] ${e}`,t)}}export{e as TraceTail,e as default};
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tracetail/js",
|
|
3
|
+
"version": "2.3.1",
|
|
4
|
+
"description": "TraceTail JavaScript SDK for browser fingerprinting",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "rollup -c",
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
},
|
|
15
|
+
"author": "TraceTail",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
19
|
+
"rollup": "^4.9.6",
|
|
20
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
21
|
+
"typescript": "^5.3.3"
|
|
22
|
+
}
|
|
23
|
+
}
|