@callforge/tracking-client 0.0.1 → 0.2.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/README.md +84 -0
- package/dist/index.d.mts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +100 -26
- package/dist/index.mjs +103 -26
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -51,6 +51,29 @@ client.onReady((session) => {
|
|
|
51
51
|
});
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
### 3. Track conversion parameters (optional)
|
|
55
|
+
|
|
56
|
+
The client automatically captures ad platform click IDs from the URL:
|
|
57
|
+
|
|
58
|
+
- `gclid` - Google Ads
|
|
59
|
+
- `gbraid` - Google app-to-web (iOS 14+)
|
|
60
|
+
- `wbraid` - Google web-to-app
|
|
61
|
+
- `msclkid` - Microsoft/Bing Ads
|
|
62
|
+
- `fbclid` - Facebook/Meta Ads
|
|
63
|
+
|
|
64
|
+
You can also add custom parameters:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Add custom tracking parameters
|
|
68
|
+
client.setParams({
|
|
69
|
+
customerId: '456',
|
|
70
|
+
landingPage: 'pricing',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Parameters are automatically sent with every API request
|
|
74
|
+
const session = await client.getSession();
|
|
75
|
+
```
|
|
76
|
+
|
|
54
77
|
## API Reference
|
|
55
78
|
|
|
56
79
|
### `CallForge.init(config)`
|
|
@@ -96,6 +119,24 @@ client.onReady((session) => {
|
|
|
96
119
|
});
|
|
97
120
|
```
|
|
98
121
|
|
|
122
|
+
### `client.setParams(params)`
|
|
123
|
+
|
|
124
|
+
Set custom tracking parameters for conversion attribution.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
client.setParams({
|
|
128
|
+
customerId: '456',
|
|
129
|
+
landingPage: 'pricing',
|
|
130
|
+
campaign: 'summer-sale',
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Behavior:**
|
|
135
|
+
- Merges with existing params (later calls override earlier values)
|
|
136
|
+
- Parameters are sent with every `getSession()` API request
|
|
137
|
+
- Persisted in localStorage alongside session data
|
|
138
|
+
- Auto-captured params (gclid, etc.) are included automatically
|
|
139
|
+
|
|
99
140
|
### `getPreloadSnippet(config)`
|
|
100
141
|
|
|
101
142
|
Generate HTML snippet for preloading tracking data.
|
|
@@ -109,6 +150,35 @@ const html = getPreloadSnippet({
|
|
|
109
150
|
});
|
|
110
151
|
```
|
|
111
152
|
|
|
153
|
+
## Tracking Parameters
|
|
154
|
+
|
|
155
|
+
### Auto-Capture
|
|
156
|
+
|
|
157
|
+
The client automatically extracts these parameters from the URL on first visit:
|
|
158
|
+
|
|
159
|
+
| Parameter | Source |
|
|
160
|
+
|-----------|--------|
|
|
161
|
+
| `gclid` | Google Ads Click ID |
|
|
162
|
+
| `gbraid` | Google app-to-web (iOS 14+) |
|
|
163
|
+
| `wbraid` | Google web-to-app |
|
|
164
|
+
| `msclkid` | Microsoft/Bing Ads |
|
|
165
|
+
| `fbclid` | Facebook/Meta Ads |
|
|
166
|
+
|
|
167
|
+
### Persistence
|
|
168
|
+
|
|
169
|
+
- Parameters are stored in localStorage alongside the session
|
|
170
|
+
- On subsequent visits, cached params are used (URL may no longer have them)
|
|
171
|
+
- Custom params via `setParams()` merge with auto-captured params
|
|
172
|
+
- Later values override earlier ones (custom > cached > auto-captured)
|
|
173
|
+
|
|
174
|
+
### API Request Format
|
|
175
|
+
|
|
176
|
+
Parameters are sent as sorted query string for cache consistency:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
/v1/tracking/session?categoryId=cat-123&fbclid=456&gclid=abc&loc_physical_ms=1014221
|
|
180
|
+
```
|
|
181
|
+
|
|
112
182
|
## Caching Behavior
|
|
113
183
|
|
|
114
184
|
- **Cache key:** `loc_physical_ms` parameter from URL
|
|
@@ -141,10 +211,24 @@ import type {
|
|
|
141
211
|
CallForgeConfig,
|
|
142
212
|
TrackingSession,
|
|
143
213
|
TrackingLocation,
|
|
214
|
+
TrackingParams,
|
|
144
215
|
ReadyCallback,
|
|
145
216
|
} from '@callforge/tracking-client';
|
|
146
217
|
```
|
|
147
218
|
|
|
219
|
+
### TrackingParams
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
interface TrackingParams {
|
|
223
|
+
gclid?: string; // Google Ads Click ID
|
|
224
|
+
gbraid?: string; // Google app-to-web (iOS 14+)
|
|
225
|
+
wbraid?: string; // Google web-to-app
|
|
226
|
+
msclkid?: string; // Microsoft/Bing Ads
|
|
227
|
+
fbclid?: string; // Facebook/Meta Ads
|
|
228
|
+
[key: string]: string | undefined; // Custom params
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
148
232
|
## Environment URLs
|
|
149
233
|
|
|
150
234
|
| Environment | Endpoint |
|
package/dist/index.d.mts
CHANGED
|
@@ -18,6 +18,23 @@ interface TrackingLocation {
|
|
|
18
18
|
/** State abbreviation (e.g., "GA") */
|
|
19
19
|
stateCode: string;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Tracking parameters for attribution (auto-captured + custom).
|
|
23
|
+
*/
|
|
24
|
+
interface TrackingParams {
|
|
25
|
+
/** Google Ads Click ID */
|
|
26
|
+
gclid?: string;
|
|
27
|
+
/** Google app-to-web (iOS 14+) */
|
|
28
|
+
gbraid?: string;
|
|
29
|
+
/** Google web-to-app */
|
|
30
|
+
wbraid?: string;
|
|
31
|
+
/** Microsoft/Bing Ads Click ID */
|
|
32
|
+
msclkid?: string;
|
|
33
|
+
/** Facebook/Meta Ads Click ID */
|
|
34
|
+
fbclid?: string;
|
|
35
|
+
/** Custom parameters */
|
|
36
|
+
[key: string]: string | undefined;
|
|
37
|
+
}
|
|
21
38
|
/**
|
|
22
39
|
* Session data returned by getSession().
|
|
23
40
|
*/
|
|
@@ -59,6 +76,7 @@ declare class CallForge {
|
|
|
59
76
|
private readonly config;
|
|
60
77
|
private readonly cache;
|
|
61
78
|
private sessionPromise;
|
|
79
|
+
private customParams;
|
|
62
80
|
private constructor();
|
|
63
81
|
/**
|
|
64
82
|
* Initialize the CallForge tracking client.
|
|
@@ -74,12 +92,19 @@ declare class CallForge {
|
|
|
74
92
|
* Callback is called once session data is available.
|
|
75
93
|
*/
|
|
76
94
|
onReady(callback: ReadyCallback): void;
|
|
95
|
+
/**
|
|
96
|
+
* Set custom tracking parameters.
|
|
97
|
+
* Merges with existing params (new values override).
|
|
98
|
+
*/
|
|
99
|
+
setParams(params: Record<string, string>): void;
|
|
77
100
|
private fetchSession;
|
|
78
101
|
private getLocationId;
|
|
102
|
+
private getAutoParams;
|
|
79
103
|
private fetchFromApi;
|
|
80
104
|
private saveToCache;
|
|
81
105
|
private formatSession;
|
|
82
106
|
private formatApiResponse;
|
|
107
|
+
private buildUrl;
|
|
83
108
|
}
|
|
84
109
|
|
|
85
110
|
/**
|
|
@@ -99,4 +124,4 @@ declare class CallForge {
|
|
|
99
124
|
*/
|
|
100
125
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
101
126
|
|
|
102
|
-
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingSession, getPreloadSnippet };
|
|
127
|
+
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,23 @@ interface TrackingLocation {
|
|
|
18
18
|
/** State abbreviation (e.g., "GA") */
|
|
19
19
|
stateCode: string;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Tracking parameters for attribution (auto-captured + custom).
|
|
23
|
+
*/
|
|
24
|
+
interface TrackingParams {
|
|
25
|
+
/** Google Ads Click ID */
|
|
26
|
+
gclid?: string;
|
|
27
|
+
/** Google app-to-web (iOS 14+) */
|
|
28
|
+
gbraid?: string;
|
|
29
|
+
/** Google web-to-app */
|
|
30
|
+
wbraid?: string;
|
|
31
|
+
/** Microsoft/Bing Ads Click ID */
|
|
32
|
+
msclkid?: string;
|
|
33
|
+
/** Facebook/Meta Ads Click ID */
|
|
34
|
+
fbclid?: string;
|
|
35
|
+
/** Custom parameters */
|
|
36
|
+
[key: string]: string | undefined;
|
|
37
|
+
}
|
|
21
38
|
/**
|
|
22
39
|
* Session data returned by getSession().
|
|
23
40
|
*/
|
|
@@ -59,6 +76,7 @@ declare class CallForge {
|
|
|
59
76
|
private readonly config;
|
|
60
77
|
private readonly cache;
|
|
61
78
|
private sessionPromise;
|
|
79
|
+
private customParams;
|
|
62
80
|
private constructor();
|
|
63
81
|
/**
|
|
64
82
|
* Initialize the CallForge tracking client.
|
|
@@ -74,12 +92,19 @@ declare class CallForge {
|
|
|
74
92
|
* Callback is called once session data is available.
|
|
75
93
|
*/
|
|
76
94
|
onReady(callback: ReadyCallback): void;
|
|
95
|
+
/**
|
|
96
|
+
* Set custom tracking parameters.
|
|
97
|
+
* Merges with existing params (new values override).
|
|
98
|
+
*/
|
|
99
|
+
setParams(params: Record<string, string>): void;
|
|
77
100
|
private fetchSession;
|
|
78
101
|
private getLocationId;
|
|
102
|
+
private getAutoParams;
|
|
79
103
|
private fetchFromApi;
|
|
80
104
|
private saveToCache;
|
|
81
105
|
private formatSession;
|
|
82
106
|
private formatApiResponse;
|
|
107
|
+
private buildUrl;
|
|
83
108
|
}
|
|
84
109
|
|
|
85
110
|
/**
|
|
@@ -99,4 +124,4 @@ declare class CallForge {
|
|
|
99
124
|
*/
|
|
100
125
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
101
126
|
|
|
102
|
-
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingSession, getPreloadSnippet };
|
|
127
|
+
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
6
20
|
var __export = (target, all) => {
|
|
7
21
|
for (var name in all)
|
|
8
22
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -64,6 +78,15 @@ var TrackingCache = class {
|
|
|
64
78
|
if (cached.locId !== locationId) return null;
|
|
65
79
|
return cached.sessionId;
|
|
66
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Get cached params (for merging with new params).
|
|
83
|
+
* Returns empty object if no cache or no params.
|
|
84
|
+
*/
|
|
85
|
+
getParams() {
|
|
86
|
+
var _a;
|
|
87
|
+
const cached = this.read();
|
|
88
|
+
return (_a = cached == null ? void 0 : cached.params) != null ? _a : {};
|
|
89
|
+
}
|
|
67
90
|
/**
|
|
68
91
|
* Store session data in cache.
|
|
69
92
|
*/
|
|
@@ -107,9 +130,11 @@ var TrackingCache = class {
|
|
|
107
130
|
// src/client.ts
|
|
108
131
|
var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
|
|
109
132
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
133
|
+
var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid"];
|
|
110
134
|
var CallForge = class _CallForge {
|
|
111
135
|
constructor(config) {
|
|
112
136
|
this.sessionPromise = null;
|
|
137
|
+
this.customParams = {};
|
|
113
138
|
this.config = {
|
|
114
139
|
categoryId: config.categoryId,
|
|
115
140
|
endpoint: config.endpoint || DEFAULT_ENDPOINT
|
|
@@ -141,26 +166,37 @@ var CallForge = class _CallForge {
|
|
|
141
166
|
this.getSession().then(callback).catch(() => {
|
|
142
167
|
});
|
|
143
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Set custom tracking parameters.
|
|
171
|
+
* Merges with existing params (new values override).
|
|
172
|
+
*/
|
|
173
|
+
setParams(params) {
|
|
174
|
+
this.customParams = __spreadValues(__spreadValues({}, this.customParams), params);
|
|
175
|
+
}
|
|
144
176
|
async fetchSession() {
|
|
145
177
|
const locationId = this.getLocationId();
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return this.formatSession(cached);
|
|
178
|
+
if (locationId) {
|
|
179
|
+
const cached = this.cache.get(locationId);
|
|
180
|
+
if (cached) {
|
|
181
|
+
return this.formatSession(cached);
|
|
182
|
+
}
|
|
152
183
|
}
|
|
153
|
-
|
|
184
|
+
const autoParams = this.getAutoParams();
|
|
185
|
+
const cachedParams = this.cache.getParams();
|
|
186
|
+
const params = __spreadValues(__spreadValues(__spreadValues({}, autoParams), cachedParams), this.customParams);
|
|
187
|
+
if (locationId && typeof window !== "undefined" && window.__cfTracking) {
|
|
154
188
|
try {
|
|
155
189
|
const data2 = await window.__cfTracking;
|
|
156
|
-
this.saveToCache(locationId, data2);
|
|
190
|
+
this.saveToCache(locationId, data2, params);
|
|
157
191
|
return this.formatApiResponse(data2);
|
|
158
192
|
} catch (e) {
|
|
159
193
|
}
|
|
160
194
|
}
|
|
161
|
-
const sessionId = this.cache.getSessionId(locationId);
|
|
162
|
-
const data = await this.fetchFromApi(locationId, sessionId);
|
|
163
|
-
|
|
195
|
+
const sessionId = locationId ? this.cache.getSessionId(locationId) : null;
|
|
196
|
+
const data = await this.fetchFromApi(locationId, sessionId, params);
|
|
197
|
+
if (locationId) {
|
|
198
|
+
this.saveToCache(locationId, data, params);
|
|
199
|
+
}
|
|
164
200
|
return this.formatApiResponse(data);
|
|
165
201
|
}
|
|
166
202
|
getLocationId() {
|
|
@@ -168,12 +204,20 @@ var CallForge = class _CallForge {
|
|
|
168
204
|
const params = new URLSearchParams(window.location.search);
|
|
169
205
|
return params.get("loc_physical_ms");
|
|
170
206
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
207
|
+
getAutoParams() {
|
|
208
|
+
if (typeof window === "undefined") return {};
|
|
209
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
210
|
+
const params = {};
|
|
211
|
+
for (const key of AUTO_PARAMS) {
|
|
212
|
+
const value = urlParams.get(key);
|
|
213
|
+
if (value) {
|
|
214
|
+
params[key] = value;
|
|
215
|
+
}
|
|
176
216
|
}
|
|
217
|
+
return params;
|
|
218
|
+
}
|
|
219
|
+
async fetchFromApi(locationId, sessionId, params) {
|
|
220
|
+
const url = this.buildUrl(locationId, sessionId, params);
|
|
177
221
|
const controller = new AbortController();
|
|
178
222
|
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
179
223
|
try {
|
|
@@ -189,13 +233,14 @@ var CallForge = class _CallForge {
|
|
|
189
233
|
clearTimeout(timeoutId);
|
|
190
234
|
}
|
|
191
235
|
}
|
|
192
|
-
saveToCache(locationId, data) {
|
|
236
|
+
saveToCache(locationId, data, params) {
|
|
193
237
|
const cached = {
|
|
194
238
|
locId: locationId,
|
|
195
239
|
sessionId: data.sessionId,
|
|
196
240
|
phoneNumber: data.phoneNumber,
|
|
197
241
|
location: data.location,
|
|
198
|
-
expiresAt: data.expiresAt
|
|
242
|
+
expiresAt: data.expiresAt,
|
|
243
|
+
params
|
|
199
244
|
};
|
|
200
245
|
this.cache.set(cached);
|
|
201
246
|
}
|
|
@@ -213,6 +258,26 @@ var CallForge = class _CallForge {
|
|
|
213
258
|
location: data.location
|
|
214
259
|
};
|
|
215
260
|
}
|
|
261
|
+
buildUrl(locationId, sessionId, params) {
|
|
262
|
+
const { categoryId, endpoint } = this.config;
|
|
263
|
+
const queryParams = {
|
|
264
|
+
categoryId
|
|
265
|
+
};
|
|
266
|
+
if (locationId) {
|
|
267
|
+
queryParams.loc_physical_ms = locationId;
|
|
268
|
+
}
|
|
269
|
+
if (sessionId) {
|
|
270
|
+
queryParams.sessionId = sessionId;
|
|
271
|
+
}
|
|
272
|
+
for (const [key, value] of Object.entries(params)) {
|
|
273
|
+
if (value !== void 0) {
|
|
274
|
+
queryParams[key] = value;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const sorted = Object.keys(queryParams).sort();
|
|
278
|
+
const qs = sorted.map((k) => `${k}=${encodeURIComponent(queryParams[k])}`).join("&");
|
|
279
|
+
return `${endpoint}/v1/tracking/session?${qs}`;
|
|
280
|
+
}
|
|
216
281
|
};
|
|
217
282
|
|
|
218
283
|
// src/preload.ts
|
|
@@ -234,17 +299,26 @@ function getPreloadSnippet(config) {
|
|
|
234
299
|
}
|
|
235
300
|
const cacheKey = `cf_tracking_${categoryId}`;
|
|
236
301
|
const script = `(function(){
|
|
237
|
-
var
|
|
238
|
-
|
|
302
|
+
var u=new URLSearchParams(location.search);
|
|
303
|
+
var loc=u.get('loc_physical_ms');
|
|
304
|
+
var ap=['gclid','gbraid','wbraid','msclkid','fbclid'];
|
|
305
|
+
var p={};
|
|
306
|
+
for(var i=0;i<ap.length;i++){var v=u.get(ap[i]);if(v)p[ap[i]]=v}
|
|
239
307
|
var key='${cacheKey}';
|
|
240
|
-
|
|
308
|
+
var sid=null;
|
|
309
|
+
if(loc){try{
|
|
241
310
|
var c=JSON.parse(localStorage.getItem(key));
|
|
242
|
-
if(c&&c.locId===loc&&c.expiresAt>Date.now()+30000){window.__cfTracking=Promise.resolve(c);return}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
311
|
+
if(c&&c.locId===loc&&c.expiresAt>Date.now()+30000){c.params=Object.assign({},c.params,p);window.__cfTracking=Promise.resolve(c);return}
|
|
312
|
+
sid=(c&&c.locId===loc)?c.sessionId:null;
|
|
313
|
+
var cp=c&&c.params||{};
|
|
314
|
+
p=Object.assign({},p,cp);
|
|
315
|
+
}catch(e){}}
|
|
316
|
+
var url='${endpoint}/v1/tracking/session?categoryId=${categoryId}';
|
|
317
|
+
if(loc)url+='&loc_physical_ms='+loc;
|
|
246
318
|
if(sid)url+='&sessionId='+sid;
|
|
247
|
-
|
|
319
|
+
var ks=Object.keys(p).sort();
|
|
320
|
+
for(var j=0;j<ks.length;j++)url+='&'+ks[j]+'='+encodeURIComponent(p[ks[j]]);
|
|
321
|
+
window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){return r.json()}).then(function(d){d.params=p;if(loc){try{localStorage.setItem(key,JSON.stringify({locId:loc,sessionId:d.sessionId,phoneNumber:d.phoneNumber,location:d.location,expiresAt:d.expiresAt,params:p}))}catch(e){}}return d});
|
|
248
322
|
})();`.replace(/\n/g, "");
|
|
249
323
|
return `<link rel="preconnect" href="${endpoint}">
|
|
250
324
|
<script>${script}</script>`;
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
|
|
1
18
|
// src/cache.ts
|
|
2
19
|
var EXPIRY_BUFFER_MS = 3e4;
|
|
3
20
|
var TrackingCache = class {
|
|
@@ -37,6 +54,15 @@ var TrackingCache = class {
|
|
|
37
54
|
if (cached.locId !== locationId) return null;
|
|
38
55
|
return cached.sessionId;
|
|
39
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Get cached params (for merging with new params).
|
|
59
|
+
* Returns empty object if no cache or no params.
|
|
60
|
+
*/
|
|
61
|
+
getParams() {
|
|
62
|
+
var _a;
|
|
63
|
+
const cached = this.read();
|
|
64
|
+
return (_a = cached == null ? void 0 : cached.params) != null ? _a : {};
|
|
65
|
+
}
|
|
40
66
|
/**
|
|
41
67
|
* Store session data in cache.
|
|
42
68
|
*/
|
|
@@ -80,9 +106,11 @@ var TrackingCache = class {
|
|
|
80
106
|
// src/client.ts
|
|
81
107
|
var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
|
|
82
108
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
109
|
+
var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid"];
|
|
83
110
|
var CallForge = class _CallForge {
|
|
84
111
|
constructor(config) {
|
|
85
112
|
this.sessionPromise = null;
|
|
113
|
+
this.customParams = {};
|
|
86
114
|
this.config = {
|
|
87
115
|
categoryId: config.categoryId,
|
|
88
116
|
endpoint: config.endpoint || DEFAULT_ENDPOINT
|
|
@@ -114,26 +142,37 @@ var CallForge = class _CallForge {
|
|
|
114
142
|
this.getSession().then(callback).catch(() => {
|
|
115
143
|
});
|
|
116
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Set custom tracking parameters.
|
|
147
|
+
* Merges with existing params (new values override).
|
|
148
|
+
*/
|
|
149
|
+
setParams(params) {
|
|
150
|
+
this.customParams = __spreadValues(__spreadValues({}, this.customParams), params);
|
|
151
|
+
}
|
|
117
152
|
async fetchSession() {
|
|
118
153
|
const locationId = this.getLocationId();
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return this.formatSession(cached);
|
|
154
|
+
if (locationId) {
|
|
155
|
+
const cached = this.cache.get(locationId);
|
|
156
|
+
if (cached) {
|
|
157
|
+
return this.formatSession(cached);
|
|
158
|
+
}
|
|
125
159
|
}
|
|
126
|
-
|
|
160
|
+
const autoParams = this.getAutoParams();
|
|
161
|
+
const cachedParams = this.cache.getParams();
|
|
162
|
+
const params = __spreadValues(__spreadValues(__spreadValues({}, autoParams), cachedParams), this.customParams);
|
|
163
|
+
if (locationId && typeof window !== "undefined" && window.__cfTracking) {
|
|
127
164
|
try {
|
|
128
165
|
const data2 = await window.__cfTracking;
|
|
129
|
-
this.saveToCache(locationId, data2);
|
|
166
|
+
this.saveToCache(locationId, data2, params);
|
|
130
167
|
return this.formatApiResponse(data2);
|
|
131
168
|
} catch (e) {
|
|
132
169
|
}
|
|
133
170
|
}
|
|
134
|
-
const sessionId = this.cache.getSessionId(locationId);
|
|
135
|
-
const data = await this.fetchFromApi(locationId, sessionId);
|
|
136
|
-
|
|
171
|
+
const sessionId = locationId ? this.cache.getSessionId(locationId) : null;
|
|
172
|
+
const data = await this.fetchFromApi(locationId, sessionId, params);
|
|
173
|
+
if (locationId) {
|
|
174
|
+
this.saveToCache(locationId, data, params);
|
|
175
|
+
}
|
|
137
176
|
return this.formatApiResponse(data);
|
|
138
177
|
}
|
|
139
178
|
getLocationId() {
|
|
@@ -141,12 +180,20 @@ var CallForge = class _CallForge {
|
|
|
141
180
|
const params = new URLSearchParams(window.location.search);
|
|
142
181
|
return params.get("loc_physical_ms");
|
|
143
182
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
183
|
+
getAutoParams() {
|
|
184
|
+
if (typeof window === "undefined") return {};
|
|
185
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
186
|
+
const params = {};
|
|
187
|
+
for (const key of AUTO_PARAMS) {
|
|
188
|
+
const value = urlParams.get(key);
|
|
189
|
+
if (value) {
|
|
190
|
+
params[key] = value;
|
|
191
|
+
}
|
|
149
192
|
}
|
|
193
|
+
return params;
|
|
194
|
+
}
|
|
195
|
+
async fetchFromApi(locationId, sessionId, params) {
|
|
196
|
+
const url = this.buildUrl(locationId, sessionId, params);
|
|
150
197
|
const controller = new AbortController();
|
|
151
198
|
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
152
199
|
try {
|
|
@@ -162,13 +209,14 @@ var CallForge = class _CallForge {
|
|
|
162
209
|
clearTimeout(timeoutId);
|
|
163
210
|
}
|
|
164
211
|
}
|
|
165
|
-
saveToCache(locationId, data) {
|
|
212
|
+
saveToCache(locationId, data, params) {
|
|
166
213
|
const cached = {
|
|
167
214
|
locId: locationId,
|
|
168
215
|
sessionId: data.sessionId,
|
|
169
216
|
phoneNumber: data.phoneNumber,
|
|
170
217
|
location: data.location,
|
|
171
|
-
expiresAt: data.expiresAt
|
|
218
|
+
expiresAt: data.expiresAt,
|
|
219
|
+
params
|
|
172
220
|
};
|
|
173
221
|
this.cache.set(cached);
|
|
174
222
|
}
|
|
@@ -186,6 +234,26 @@ var CallForge = class _CallForge {
|
|
|
186
234
|
location: data.location
|
|
187
235
|
};
|
|
188
236
|
}
|
|
237
|
+
buildUrl(locationId, sessionId, params) {
|
|
238
|
+
const { categoryId, endpoint } = this.config;
|
|
239
|
+
const queryParams = {
|
|
240
|
+
categoryId
|
|
241
|
+
};
|
|
242
|
+
if (locationId) {
|
|
243
|
+
queryParams.loc_physical_ms = locationId;
|
|
244
|
+
}
|
|
245
|
+
if (sessionId) {
|
|
246
|
+
queryParams.sessionId = sessionId;
|
|
247
|
+
}
|
|
248
|
+
for (const [key, value] of Object.entries(params)) {
|
|
249
|
+
if (value !== void 0) {
|
|
250
|
+
queryParams[key] = value;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const sorted = Object.keys(queryParams).sort();
|
|
254
|
+
const qs = sorted.map((k) => `${k}=${encodeURIComponent(queryParams[k])}`).join("&");
|
|
255
|
+
return `${endpoint}/v1/tracking/session?${qs}`;
|
|
256
|
+
}
|
|
189
257
|
};
|
|
190
258
|
|
|
191
259
|
// src/preload.ts
|
|
@@ -207,17 +275,26 @@ function getPreloadSnippet(config) {
|
|
|
207
275
|
}
|
|
208
276
|
const cacheKey = `cf_tracking_${categoryId}`;
|
|
209
277
|
const script = `(function(){
|
|
210
|
-
var
|
|
211
|
-
|
|
278
|
+
var u=new URLSearchParams(location.search);
|
|
279
|
+
var loc=u.get('loc_physical_ms');
|
|
280
|
+
var ap=['gclid','gbraid','wbraid','msclkid','fbclid'];
|
|
281
|
+
var p={};
|
|
282
|
+
for(var i=0;i<ap.length;i++){var v=u.get(ap[i]);if(v)p[ap[i]]=v}
|
|
212
283
|
var key='${cacheKey}';
|
|
213
|
-
|
|
284
|
+
var sid=null;
|
|
285
|
+
if(loc){try{
|
|
214
286
|
var c=JSON.parse(localStorage.getItem(key));
|
|
215
|
-
if(c&&c.locId===loc&&c.expiresAt>Date.now()+30000){window.__cfTracking=Promise.resolve(c);return}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
287
|
+
if(c&&c.locId===loc&&c.expiresAt>Date.now()+30000){c.params=Object.assign({},c.params,p);window.__cfTracking=Promise.resolve(c);return}
|
|
288
|
+
sid=(c&&c.locId===loc)?c.sessionId:null;
|
|
289
|
+
var cp=c&&c.params||{};
|
|
290
|
+
p=Object.assign({},p,cp);
|
|
291
|
+
}catch(e){}}
|
|
292
|
+
var url='${endpoint}/v1/tracking/session?categoryId=${categoryId}';
|
|
293
|
+
if(loc)url+='&loc_physical_ms='+loc;
|
|
219
294
|
if(sid)url+='&sessionId='+sid;
|
|
220
|
-
|
|
295
|
+
var ks=Object.keys(p).sort();
|
|
296
|
+
for(var j=0;j<ks.length;j++)url+='&'+ks[j]+'='+encodeURIComponent(p[ks[j]]);
|
|
297
|
+
window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){return r.json()}).then(function(d){d.params=p;if(loc){try{localStorage.setItem(key,JSON.stringify({locId:loc,sessionId:d.sessionId,phoneNumber:d.phoneNumber,location:d.location,expiresAt:d.expiresAt,params:p}))}catch(e){}}return d});
|
|
221
298
|
})();`.replace(/\n/g, "");
|
|
222
299
|
return `<link rel="preconnect" href="${endpoint}">
|
|
223
300
|
<script>${script}</script>`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@callforge/tracking-client",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"jsdom": "^27.4.0",
|
|
19
|
+
"tsup": "^8.0.0",
|
|
20
|
+
"typescript": "^5.3.0",
|
|
21
|
+
"vitest": "^1.6.0",
|
|
22
|
+
"@callforge/tsconfig": "0.0.0"
|
|
23
|
+
},
|
|
17
24
|
"scripts": {
|
|
18
25
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
19
26
|
"clean": "rm -rf dist",
|
|
20
27
|
"test": "vitest run",
|
|
21
28
|
"test:watch": "vitest"
|
|
22
|
-
},
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"@callforge/tsconfig": "workspace:*",
|
|
25
|
-
"jsdom": "^27.4.0",
|
|
26
|
-
"tsup": "^8.0.0",
|
|
27
|
-
"typescript": "^5.3.0",
|
|
28
|
-
"vitest": "^1.6.0"
|
|
29
29
|
}
|
|
30
|
-
}
|
|
30
|
+
}
|