@thred-apps/thred-track 1.2.1 → 1.2.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 +19 -230
- package/dist/core/tracker.d.ts +9 -0
- package/dist/core/tracker.d.ts.map +1 -1
- package/dist/index.esm.js +48 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +48 -1
- package/dist/index.js.map +1 -1
- package/dist/thred.umd.js +1 -1
- package/dist/thred.umd.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,106 +1,43 @@
|
|
|
1
1
|
# Thred SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **ChatGPT Detection** - Automatic detection of visitors from ChatGPT
|
|
8
|
-
- **Browser Fingerprinting** - Privacy-friendly visitor identification
|
|
9
|
-
- **Page View Tracking** - Automatic page view analytics
|
|
10
|
-
- **Form Tracking** - Automatic form submission tracking
|
|
11
|
-
- **Lead Enrichment** - Capture and enrich lead data
|
|
12
|
-
- **Zero Dependencies** - Lightweight with no external dependencies
|
|
13
|
-
- **Multiple Formats** - UMD, CommonJS, and ES modules
|
|
14
|
-
- **TypeScript Support** - Full TypeScript definitions included
|
|
3
|
+
Automatic browser tracking and lead enrichment for ChatGPT referrals.
|
|
15
4
|
|
|
16
5
|
## Installation
|
|
17
6
|
|
|
18
|
-
###
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install thred-track
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Yarn
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
yarn add thred-track
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### CDN (Script Tag)
|
|
7
|
+
### Script Tag
|
|
31
8
|
|
|
32
9
|
```html
|
|
33
10
|
<script src="https://cdn.thred.dev/thred-track.js?browserKey=YOUR_KEY"></script>
|
|
34
11
|
```
|
|
35
12
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
### Script Tag
|
|
39
|
-
|
|
40
|
-
Drop the script onto your page with your browser key. That's it — everything is automatic:
|
|
13
|
+
### NPM
|
|
41
14
|
|
|
42
|
-
```
|
|
43
|
-
|
|
15
|
+
```bash
|
|
16
|
+
npm install @thred-apps/thred-track
|
|
44
17
|
```
|
|
45
18
|
|
|
46
|
-
### Module Import
|
|
47
|
-
|
|
48
19
|
```typescript
|
|
49
|
-
import { ThredSDK } from 'thred-track';
|
|
20
|
+
import { ThredSDK } from '@thred-apps/thred-track';
|
|
50
21
|
|
|
51
22
|
new ThredSDK({ browserKey: 'YOUR_KEY' });
|
|
52
23
|
```
|
|
53
24
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
1. Detects ChatGPT referrals via UTM parameters
|
|
57
|
-
2. Generates a browser fingerprint
|
|
58
|
-
3. Tracks page views for ChatGPT visitors
|
|
59
|
-
4. Monitors and captures form submissions based on your API config
|
|
60
|
-
|
|
61
|
-
## What Happens on Initialization
|
|
62
|
-
|
|
63
|
-
When the SDK initializes, it follows the same flow as the original script:
|
|
64
|
-
|
|
65
|
-
1. **Fetches config** from the Thred API using your `browserKey`
|
|
66
|
-
2. **Checks eligibility** — tracking only runs if the config says `enabled: true` and the visitor has a ChatGPT session or arrived via ChatGPT UTM
|
|
67
|
-
3. **Tracks the page view** automatically for ChatGPT visitors
|
|
68
|
-
4. **Sets up form tracking** — either injecting fingerprints into hosted form links, or attaching submit listeners to your form (based on config `type`)
|
|
69
|
-
|
|
70
|
-
All of this happens without any manual method calls.
|
|
71
|
-
|
|
72
|
-
## Constructor Options
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
interface ThredOptions {
|
|
76
|
-
browserKey: string; // Required: Your Thred browser key
|
|
77
|
-
debug?: boolean; // Optional: Enable debug logging (default: false)
|
|
78
|
-
autoInit?: boolean; // Optional: Auto-initialize on construction (default: true)
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Usage Examples
|
|
83
|
-
|
|
84
|
-
### Basic HTML Page
|
|
25
|
+
That's it. The SDK automatically:
|
|
85
26
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<input type="text" name="company">
|
|
91
|
-
<button type="submit">Submit</button>
|
|
92
|
-
</form>
|
|
27
|
+
- Detects ChatGPT referrals
|
|
28
|
+
- Fingerprints the visitor
|
|
29
|
+
- Tracks page views
|
|
30
|
+
- Captures form submissions
|
|
93
31
|
|
|
94
|
-
|
|
95
|
-
```
|
|
32
|
+
No manual method calls needed.
|
|
96
33
|
|
|
97
|
-
|
|
34
|
+
## Framework Examples
|
|
98
35
|
|
|
99
36
|
### React
|
|
100
37
|
|
|
101
38
|
```typescript
|
|
102
39
|
import { useEffect, useRef } from 'react';
|
|
103
|
-
import { ThredSDK } from 'thred-track';
|
|
40
|
+
import { ThredSDK } from '@thred-apps/thred-track';
|
|
104
41
|
|
|
105
42
|
function App() {
|
|
106
43
|
const thredRef = useRef<ThredSDK | null>(null);
|
|
@@ -108,9 +45,7 @@ function App() {
|
|
|
108
45
|
useEffect(() => {
|
|
109
46
|
thredRef.current = new ThredSDK({
|
|
110
47
|
browserKey: process.env.REACT_APP_THRED_KEY!,
|
|
111
|
-
debug: process.env.NODE_ENV === 'development',
|
|
112
48
|
});
|
|
113
|
-
|
|
114
49
|
return () => thredRef.current?.destroy();
|
|
115
50
|
}, []);
|
|
116
51
|
|
|
@@ -124,7 +59,7 @@ function App() {
|
|
|
124
59
|
'use client';
|
|
125
60
|
|
|
126
61
|
import { useEffect, useRef } from 'react';
|
|
127
|
-
import { ThredSDK } from 'thred-track';
|
|
62
|
+
import { ThredSDK } from '@thred-apps/thred-track';
|
|
128
63
|
|
|
129
64
|
export function ThredProvider({ children }: { children: React.ReactNode }) {
|
|
130
65
|
const thredRef = useRef<ThredSDK | null>(null);
|
|
@@ -133,7 +68,6 @@ export function ThredProvider({ children }: { children: React.ReactNode }) {
|
|
|
133
68
|
thredRef.current = new ThredSDK({
|
|
134
69
|
browserKey: process.env.NEXT_PUBLIC_THRED_KEY!,
|
|
135
70
|
});
|
|
136
|
-
|
|
137
71
|
return () => thredRef.current?.destroy();
|
|
138
72
|
}, []);
|
|
139
73
|
|
|
@@ -141,162 +75,17 @@ export function ThredProvider({ children }: { children: React.ReactNode }) {
|
|
|
141
75
|
}
|
|
142
76
|
```
|
|
143
77
|
|
|
144
|
-
### Delayed Initialization
|
|
145
|
-
|
|
146
|
-
If you need to control when tracking starts (e.g., after user consent):
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
import { ThredSDK } from 'thred-track';
|
|
150
|
-
|
|
151
|
-
const thred = new ThredSDK({
|
|
152
|
-
browserKey: 'YOUR_KEY',
|
|
153
|
-
autoInit: false,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Later, when ready:
|
|
157
|
-
await thred.init();
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Advanced API
|
|
161
|
-
|
|
162
|
-
These methods are available for advanced use cases but are **not required** for typical usage — the SDK handles everything automatically.
|
|
163
|
-
|
|
164
|
-
#### `identify(leadData: LeadData): Promise<void>`
|
|
165
|
-
|
|
166
|
-
Manually identify a user outside of automatic form tracking (e.g., after OAuth or a custom flow):
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
await thred.identify({
|
|
170
|
-
name: 'John Doe',
|
|
171
|
-
email: 'john@example.com',
|
|
172
|
-
company: 'Acme Inc',
|
|
173
|
-
});
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
#### `destroy(): void`
|
|
177
|
-
|
|
178
|
-
Clean up the SDK instance and remove event listeners. Use this when unmounting in SPAs:
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
thred.destroy();
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Development
|
|
185
|
-
|
|
186
|
-
### Setup
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
npm install
|
|
190
|
-
npm run build
|
|
191
|
-
npm run dev # Watch mode
|
|
192
|
-
npm test
|
|
193
|
-
npm run lint
|
|
194
|
-
npm run format
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Project Structure
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
thred-track/
|
|
201
|
-
├── src/
|
|
202
|
-
│ ├── core/ # Core SDK functionality
|
|
203
|
-
│ │ ├── api.ts # API client
|
|
204
|
-
│ │ ├── fingerprint.ts # Fingerprint management
|
|
205
|
-
│ │ └── tracker.ts # Event tracking
|
|
206
|
-
│ ├── utils/ # Utility functions
|
|
207
|
-
│ │ ├── detector.ts # ChatGPT detection
|
|
208
|
-
│ │ └── logger.ts # Logging utility
|
|
209
|
-
│ ├── types/ # TypeScript definitions
|
|
210
|
-
│ │ └── index.ts
|
|
211
|
-
│ ├── __tests__/ # Test files
|
|
212
|
-
│ └── index.ts # Main entry point
|
|
213
|
-
├── dist/ # Build output
|
|
214
|
-
└── package.json
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Building
|
|
218
|
-
|
|
219
|
-
The SDK builds to multiple formats:
|
|
220
|
-
|
|
221
|
-
- **UMD** (`dist/thred-track.umd.js`) — For script tags
|
|
222
|
-
- **CommonJS** (`dist/index.js`) — For Node.js / bundlers
|
|
223
|
-
- **ES Module** (`dist/index.esm.js`) — For modern bundlers
|
|
224
|
-
- **TypeScript** (`dist/index.d.ts`) — Type definitions
|
|
225
|
-
|
|
226
|
-
### Testing
|
|
227
|
-
|
|
228
|
-
```bash
|
|
229
|
-
npm test
|
|
230
|
-
npm run test:watch
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Local Testing
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
npm run serve
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
Then open http://localhost:8080/basic.html?utm_source=chatgpt
|
|
240
|
-
|
|
241
|
-
## Configuration
|
|
242
|
-
|
|
243
|
-
The SDK fetches configuration from your Thred API automatically on init:
|
|
244
|
-
|
|
245
|
-
```json
|
|
246
|
-
{
|
|
247
|
-
"enabled": true,
|
|
248
|
-
"type": "custom",
|
|
249
|
-
"formId": "form",
|
|
250
|
-
"emailId": "email",
|
|
251
|
-
"nameId": "name",
|
|
252
|
-
"companyId": "company",
|
|
253
|
-
"hasChatSession": true
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
This config controls which form to track, which fields to extract, and whether the visitor has an existing ChatGPT session.
|
|
258
|
-
|
|
259
78
|
## Privacy & Security
|
|
260
79
|
|
|
261
|
-
- Only tracks visitors from ChatGPT
|
|
262
|
-
-
|
|
263
|
-
- All tracking controlled by API configuration
|
|
80
|
+
- Only tracks visitors arriving from ChatGPT
|
|
81
|
+
- Browser fingerprinting — no cookies required
|
|
264
82
|
- No PII collected without explicit user submission
|
|
265
|
-
-
|
|
83
|
+
- GDPR and CCPA compliant
|
|
266
84
|
|
|
267
85
|
## Browser Support
|
|
268
86
|
|
|
269
|
-
|
|
270
|
-
- Firefox (latest)
|
|
271
|
-
- Safari (latest)
|
|
272
|
-
- Opera (latest)
|
|
273
|
-
|
|
274
|
-
Requires ES2015+ support.
|
|
275
|
-
|
|
276
|
-
## API Endpoints
|
|
277
|
-
|
|
278
|
-
- **Config**: `GET /v1/config?browserKey={key}`
|
|
279
|
-
- **Page View**: `POST /v1/events/page-view?browserKey={key}`
|
|
280
|
-
- **Enrich**: `POST /v1/customers/enrich?browserKey={key}`
|
|
281
|
-
|
|
282
|
-
## TypeScript
|
|
283
|
-
|
|
284
|
-
Full TypeScript support with exported types:
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
import type {
|
|
288
|
-
ThredOptions,
|
|
289
|
-
ThredConfig,
|
|
290
|
-
LeadData,
|
|
291
|
-
PageViewPayload,
|
|
292
|
-
EnrichPayload,
|
|
293
|
-
} from 'thred-track';
|
|
294
|
-
```
|
|
87
|
+
Chrome, Firefox, Safari, Edge (latest versions). Requires ES2015+.
|
|
295
88
|
|
|
296
89
|
## License
|
|
297
90
|
|
|
298
91
|
MIT
|
|
299
|
-
|
|
300
|
-
## Support
|
|
301
|
-
|
|
302
|
-
For questions or issues, please open a GitHub issue or contact support@thred.dev.
|
package/dist/core/tracker.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export declare class Tracker {
|
|
|
8
8
|
private logger;
|
|
9
9
|
private config;
|
|
10
10
|
private formObserver;
|
|
11
|
+
private lastTrackedUrl;
|
|
12
|
+
private popstateHandler;
|
|
13
|
+
private originalPushState;
|
|
14
|
+
private originalReplaceState;
|
|
11
15
|
constructor(api: ThredAPI, fingerprint: FingerprintManager, logger: Logger);
|
|
12
16
|
/**
|
|
13
17
|
* Initialize tracker with config
|
|
@@ -25,6 +29,11 @@ export declare class Tracker {
|
|
|
25
29
|
* Identify user and enrich lead data
|
|
26
30
|
*/
|
|
27
31
|
identify(leadData: LeadData): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Track page views on SPA route changes by patching pushState/replaceState
|
|
34
|
+
* and listening for popstate.
|
|
35
|
+
*/
|
|
36
|
+
private setupRouteTracking;
|
|
28
37
|
/**
|
|
29
38
|
* Setup automatic form tracking
|
|
30
39
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,qBAAa,OAAO;IAClB,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAAiC;
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,qBAAa,OAAO;IAClB,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,oBAAoB,CAA4C;gBAGtE,GAAG,EAAE,QAAQ,EACb,WAAW,EAAE,kBAAkB,EAC/B,MAAM,EAAE,MAAM;IAOhB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBpC;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BxD;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjD;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA8B1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsGzB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAOlC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAuBjC;;OAEG;IACH,OAAO,IAAI,IAAI;CAqBhB"}
|
package/dist/index.esm.js
CHANGED
|
@@ -258,6 +258,10 @@ class Tracker {
|
|
|
258
258
|
constructor(api, fingerprint, logger) {
|
|
259
259
|
this.config = null;
|
|
260
260
|
this.formObserver = null;
|
|
261
|
+
this.lastTrackedUrl = null;
|
|
262
|
+
this.popstateHandler = null;
|
|
263
|
+
this.originalPushState = null;
|
|
264
|
+
this.originalReplaceState = null;
|
|
261
265
|
this.api = api;
|
|
262
266
|
this.fingerprint = fingerprint;
|
|
263
267
|
this.logger = logger;
|
|
@@ -283,13 +287,15 @@ class Tracker {
|
|
|
283
287
|
this.logger.log('No chat session for this fingerprint - exiting');
|
|
284
288
|
return;
|
|
285
289
|
}
|
|
286
|
-
//
|
|
290
|
+
// Track initial page view
|
|
287
291
|
if (isFromChatGPT()) {
|
|
288
292
|
yield this.trackPageView();
|
|
289
293
|
}
|
|
290
294
|
else {
|
|
291
295
|
this.logger.log('UTM not from ChatGPT - skipping page view');
|
|
292
296
|
}
|
|
297
|
+
// Track SPA route changes
|
|
298
|
+
this.setupRouteTracking();
|
|
293
299
|
// Setup form tracking
|
|
294
300
|
this.setupFormTracking();
|
|
295
301
|
});
|
|
@@ -358,6 +364,35 @@ class Tracker {
|
|
|
358
364
|
});
|
|
359
365
|
});
|
|
360
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Track page views on SPA route changes by patching pushState/replaceState
|
|
369
|
+
* and listening for popstate.
|
|
370
|
+
*/
|
|
371
|
+
setupRouteTracking() {
|
|
372
|
+
if (typeof window === 'undefined')
|
|
373
|
+
return;
|
|
374
|
+
this.lastTrackedUrl = window.location.href;
|
|
375
|
+
const onRouteChange = () => {
|
|
376
|
+
const currentUrl = window.location.href;
|
|
377
|
+
if (currentUrl === this.lastTrackedUrl)
|
|
378
|
+
return;
|
|
379
|
+
this.lastTrackedUrl = currentUrl;
|
|
380
|
+
this.logger.log('Route change detected:', currentUrl);
|
|
381
|
+
this.trackPageView();
|
|
382
|
+
};
|
|
383
|
+
this.popstateHandler = onRouteChange;
|
|
384
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
385
|
+
this.originalPushState = history.pushState.bind(history);
|
|
386
|
+
this.originalReplaceState = history.replaceState.bind(history);
|
|
387
|
+
history.pushState = (...args) => {
|
|
388
|
+
this.originalPushState(...args);
|
|
389
|
+
onRouteChange();
|
|
390
|
+
};
|
|
391
|
+
history.replaceState = (...args) => {
|
|
392
|
+
this.originalReplaceState(...args);
|
|
393
|
+
onRouteChange();
|
|
394
|
+
};
|
|
395
|
+
}
|
|
361
396
|
/**
|
|
362
397
|
* Setup automatic form tracking
|
|
363
398
|
*/
|
|
@@ -485,6 +520,18 @@ class Tracker {
|
|
|
485
520
|
this.formObserver.disconnect();
|
|
486
521
|
this.formObserver = null;
|
|
487
522
|
}
|
|
523
|
+
if (this.popstateHandler) {
|
|
524
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
525
|
+
this.popstateHandler = null;
|
|
526
|
+
}
|
|
527
|
+
if (this.originalPushState) {
|
|
528
|
+
history.pushState = this.originalPushState;
|
|
529
|
+
this.originalPushState = null;
|
|
530
|
+
}
|
|
531
|
+
if (this.originalReplaceState) {
|
|
532
|
+
history.replaceState = this.originalReplaceState;
|
|
533
|
+
this.originalReplaceState = null;
|
|
534
|
+
}
|
|
488
535
|
}
|
|
489
536
|
}
|
|
490
537
|
|