@callforge/tracking-client 0.6.3 → 0.7.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 +47 -11
- package/dist/index.d.mts +38 -6
- package/dist/index.d.ts +38 -6
- package/dist/index.js +169 -6
- package/dist/index.mjs +169 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Generated HTML:
|
|
|
30
30
|
<script>/* preload script */</script>
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### 2. Initialize and
|
|
33
|
+
### 2. Initialize and start session + location requests
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import { CallForge } from '@callforge/tracking-client';
|
|
@@ -40,9 +40,13 @@ const client = CallForge.init({
|
|
|
40
40
|
// endpoint: 'https://tracking-dev.callforge.io', // Optional: override for dev
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
const session =
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const { session, location } = client.getSessionAsync();
|
|
44
|
+
|
|
45
|
+
// Location is delivered independently (often faster than phone number assignment)
|
|
46
|
+
console.log(await location); // { city: "Woodstock", state: "Georgia", stateCode: "GA" } or null
|
|
47
|
+
|
|
48
|
+
// Phone session data (deterministic token + phone number)
|
|
49
|
+
console.log(await session); // { sessionToken, leaseId, phoneNumber }
|
|
46
50
|
```
|
|
47
51
|
|
|
48
52
|
### 3. Deterministic click/callback attribution (optional)
|
|
@@ -134,18 +138,13 @@ interface CallForgeConfig {
|
|
|
134
138
|
|
|
135
139
|
### `client.getSession()`
|
|
136
140
|
|
|
137
|
-
Get tracking session data. Returns cached data if valid, otherwise fetches from the API.
|
|
141
|
+
Get tracking session data (phone number + deterministic session token). Returns cached data if valid, otherwise fetches from the API.
|
|
138
142
|
|
|
139
143
|
```typescript
|
|
140
144
|
interface TrackingSession {
|
|
141
145
|
sessionToken: string; // Signed, opaque token used to refresh the session
|
|
142
146
|
leaseId: string | null; // Deterministic assignment lease ID (when available)
|
|
143
147
|
phoneNumber: string | null;
|
|
144
|
-
location: {
|
|
145
|
-
city: string;
|
|
146
|
-
state: string; // Full name: "Georgia"
|
|
147
|
-
stateCode: string; // Abbreviation: "GA"
|
|
148
|
-
} | null;
|
|
149
148
|
}
|
|
150
149
|
```
|
|
151
150
|
|
|
@@ -155,6 +154,31 @@ Behavior:
|
|
|
155
154
|
- If `loc_physical_ms` is present in the URL, cached sessions are only reused when it matches the cached `locId`.
|
|
156
155
|
- Throws on network errors or API errors.
|
|
157
156
|
|
|
157
|
+
### `client.getLocation()`
|
|
158
|
+
|
|
159
|
+
Get location data only. Returns cached data if valid, otherwise fetches from the API.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const location = await client.getLocation();
|
|
163
|
+
// { city, state, stateCode } or null
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `client.getSessionAsync()`
|
|
167
|
+
|
|
168
|
+
Kick off both requests and use each as soon as it resolves.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const { session, location } = client.getSessionAsync();
|
|
172
|
+
|
|
173
|
+
location.then((loc) => {
|
|
174
|
+
// show city/state ASAP
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
session.then((sess) => {
|
|
178
|
+
// show phone number when ready
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
158
182
|
### `client.createCallIntent()`
|
|
159
183
|
|
|
160
184
|
Create a short-lived call intent token for click/callback deterministic attribution.
|
|
@@ -174,6 +198,16 @@ client.onReady((session) => {
|
|
|
174
198
|
});
|
|
175
199
|
```
|
|
176
200
|
|
|
201
|
+
### `client.onLocationReady(callback)`
|
|
202
|
+
|
|
203
|
+
Subscribe to location ready event. Callback is called once location data is available.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
client.onLocationReady((location) => {
|
|
207
|
+
// location is { city, state, stateCode } or null
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
177
211
|
### `client.setParams(params)`
|
|
178
212
|
|
|
179
213
|
Set custom tracking parameters for conversion attribution.
|
|
@@ -230,7 +264,8 @@ Parameters are sent as a sorted query string for cache consistency:
|
|
|
230
264
|
|
|
231
265
|
## Caching Behavior
|
|
232
266
|
|
|
233
|
-
-
|
|
267
|
+
- Session cache key: `cf_tracking_v1_<siteKey>_<categoryId>`
|
|
268
|
+
- Location cache key: `cf_location_v1_<siteKey>`
|
|
234
269
|
- TTL: controlled by the server `expiresAt` response (currently 30 minutes)
|
|
235
270
|
- Storage: localStorage (falls back to memory if unavailable)
|
|
236
271
|
|
|
@@ -261,6 +296,7 @@ import type {
|
|
|
261
296
|
TrackingLocation,
|
|
262
297
|
TrackingParams,
|
|
263
298
|
ReadyCallback,
|
|
299
|
+
LocationReadyCallback,
|
|
264
300
|
CallIntentResponse,
|
|
265
301
|
} from '@callforge/tracking-client';
|
|
266
302
|
```
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,7 @@ interface TrackingLocation {
|
|
|
22
22
|
/** State abbreviation (e.g., "GA") */
|
|
23
23
|
stateCode: string;
|
|
24
24
|
}
|
|
25
|
+
type TrackingLocationSource = 'google_criteria' | 'cloudflare_geo';
|
|
25
26
|
/**
|
|
26
27
|
* Tracking parameters for attribution (auto-captured + custom).
|
|
27
28
|
*/
|
|
@@ -49,20 +50,25 @@ interface TrackingSession {
|
|
|
49
50
|
leaseId: string | null;
|
|
50
51
|
/** Assigned phone number, or null if no number available */
|
|
51
52
|
phoneNumber: string | null;
|
|
52
|
-
/** Location data, or null if location could not be resolved */
|
|
53
|
-
location: TrackingLocation | null;
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
|
-
*
|
|
55
|
+
* Session API response format (includes expiresAt).
|
|
57
56
|
*/
|
|
58
|
-
interface
|
|
57
|
+
interface ApiSessionResponse {
|
|
59
58
|
sessionToken: string;
|
|
60
59
|
leaseId: string | null;
|
|
61
60
|
phoneNumber: string | null;
|
|
61
|
+
expiresAt: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Location API response format (includes expiresAt).
|
|
65
|
+
*/
|
|
66
|
+
interface ApiLocationResponse {
|
|
62
67
|
location: {
|
|
63
68
|
city: string;
|
|
64
69
|
state: string;
|
|
65
70
|
stateCode: string;
|
|
71
|
+
source: TrackingLocationSource;
|
|
66
72
|
} | null;
|
|
67
73
|
expiresAt: number;
|
|
68
74
|
}
|
|
@@ -78,12 +84,14 @@ interface CallIntentResponse {
|
|
|
78
84
|
* Callback function for onReady subscription.
|
|
79
85
|
*/
|
|
80
86
|
type ReadyCallback = (session: TrackingSession) => void;
|
|
87
|
+
type LocationReadyCallback = (location: TrackingLocation | null) => void;
|
|
81
88
|
/**
|
|
82
89
|
* Global window extension for preload promise and gtag.
|
|
83
90
|
*/
|
|
84
91
|
declare global {
|
|
85
92
|
interface Window {
|
|
86
|
-
__cfTracking?: Promise<
|
|
93
|
+
__cfTracking?: Promise<ApiSessionResponse>;
|
|
94
|
+
__cfTrackingLocation?: Promise<ApiLocationResponse>;
|
|
87
95
|
gtag?: (command: 'get', targetId: string, fieldName: string, callback: (value: string) => void) => void;
|
|
88
96
|
}
|
|
89
97
|
}
|
|
@@ -91,7 +99,9 @@ declare global {
|
|
|
91
99
|
declare class CallForge {
|
|
92
100
|
private readonly config;
|
|
93
101
|
private readonly cache;
|
|
102
|
+
private readonly locationCache;
|
|
94
103
|
private sessionPromise;
|
|
104
|
+
private locationPromise;
|
|
95
105
|
private customParams;
|
|
96
106
|
private constructor();
|
|
97
107
|
/**
|
|
@@ -103,6 +113,19 @@ declare class CallForge {
|
|
|
103
113
|
* Returns cached data if valid, otherwise fetches from API.
|
|
104
114
|
*/
|
|
105
115
|
getSession(): Promise<TrackingSession>;
|
|
116
|
+
/**
|
|
117
|
+
* Get location data only.
|
|
118
|
+
* Returns cached data if valid, otherwise fetches from API.
|
|
119
|
+
*/
|
|
120
|
+
getLocation(): Promise<TrackingLocation | null>;
|
|
121
|
+
/**
|
|
122
|
+
* Kick off both session and location requests.
|
|
123
|
+
* Returns separate Promises so consumers can use each as soon as it resolves.
|
|
124
|
+
*/
|
|
125
|
+
getSessionAsync(): {
|
|
126
|
+
session: Promise<TrackingSession>;
|
|
127
|
+
location: Promise<TrackingLocation | null>;
|
|
128
|
+
};
|
|
106
129
|
/**
|
|
107
130
|
* Create a short-lived call intent token for click/callback deterministic attribution.
|
|
108
131
|
*/
|
|
@@ -112,6 +135,11 @@ declare class CallForge {
|
|
|
112
135
|
* Callback is called once session data is available.
|
|
113
136
|
*/
|
|
114
137
|
onReady(callback: ReadyCallback): void;
|
|
138
|
+
/**
|
|
139
|
+
* Subscribe to location ready event.
|
|
140
|
+
* Callback is called once location data is available.
|
|
141
|
+
*/
|
|
142
|
+
onLocationReady(callback: LocationReadyCallback): void;
|
|
115
143
|
/**
|
|
116
144
|
* Set custom tracking parameters for attribution.
|
|
117
145
|
*
|
|
@@ -139,14 +167,18 @@ declare class CallForge {
|
|
|
139
167
|
*/
|
|
140
168
|
private getGA4ClientIdFromCookie;
|
|
141
169
|
private fetchSession;
|
|
170
|
+
private fetchLocation;
|
|
142
171
|
private getLocationId;
|
|
143
172
|
private getAutoParams;
|
|
144
173
|
private fetchFromApi;
|
|
174
|
+
private fetchLocationFromApi;
|
|
145
175
|
private saveToCache;
|
|
176
|
+
private saveLocationToCache;
|
|
146
177
|
private formatSession;
|
|
147
178
|
private formatApiResponse;
|
|
148
179
|
private syncParamsToCallForgeIfPossible;
|
|
149
180
|
private buildUrl;
|
|
181
|
+
private buildLocationUrl;
|
|
150
182
|
}
|
|
151
183
|
|
|
152
184
|
/**
|
|
@@ -155,4 +187,4 @@ declare class CallForge {
|
|
|
155
187
|
*/
|
|
156
188
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
157
189
|
|
|
158
|
-
export { CallForge, type CallForgeConfig, type CallIntentResponse, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
|
190
|
+
export { CallForge, type CallForgeConfig, type CallIntentResponse, type LocationReadyCallback, type ReadyCallback, type TrackingLocation, type TrackingLocationSource, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ interface TrackingLocation {
|
|
|
22
22
|
/** State abbreviation (e.g., "GA") */
|
|
23
23
|
stateCode: string;
|
|
24
24
|
}
|
|
25
|
+
type TrackingLocationSource = 'google_criteria' | 'cloudflare_geo';
|
|
25
26
|
/**
|
|
26
27
|
* Tracking parameters for attribution (auto-captured + custom).
|
|
27
28
|
*/
|
|
@@ -49,20 +50,25 @@ interface TrackingSession {
|
|
|
49
50
|
leaseId: string | null;
|
|
50
51
|
/** Assigned phone number, or null if no number available */
|
|
51
52
|
phoneNumber: string | null;
|
|
52
|
-
/** Location data, or null if location could not be resolved */
|
|
53
|
-
location: TrackingLocation | null;
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
|
-
*
|
|
55
|
+
* Session API response format (includes expiresAt).
|
|
57
56
|
*/
|
|
58
|
-
interface
|
|
57
|
+
interface ApiSessionResponse {
|
|
59
58
|
sessionToken: string;
|
|
60
59
|
leaseId: string | null;
|
|
61
60
|
phoneNumber: string | null;
|
|
61
|
+
expiresAt: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Location API response format (includes expiresAt).
|
|
65
|
+
*/
|
|
66
|
+
interface ApiLocationResponse {
|
|
62
67
|
location: {
|
|
63
68
|
city: string;
|
|
64
69
|
state: string;
|
|
65
70
|
stateCode: string;
|
|
71
|
+
source: TrackingLocationSource;
|
|
66
72
|
} | null;
|
|
67
73
|
expiresAt: number;
|
|
68
74
|
}
|
|
@@ -78,12 +84,14 @@ interface CallIntentResponse {
|
|
|
78
84
|
* Callback function for onReady subscription.
|
|
79
85
|
*/
|
|
80
86
|
type ReadyCallback = (session: TrackingSession) => void;
|
|
87
|
+
type LocationReadyCallback = (location: TrackingLocation | null) => void;
|
|
81
88
|
/**
|
|
82
89
|
* Global window extension for preload promise and gtag.
|
|
83
90
|
*/
|
|
84
91
|
declare global {
|
|
85
92
|
interface Window {
|
|
86
|
-
__cfTracking?: Promise<
|
|
93
|
+
__cfTracking?: Promise<ApiSessionResponse>;
|
|
94
|
+
__cfTrackingLocation?: Promise<ApiLocationResponse>;
|
|
87
95
|
gtag?: (command: 'get', targetId: string, fieldName: string, callback: (value: string) => void) => void;
|
|
88
96
|
}
|
|
89
97
|
}
|
|
@@ -91,7 +99,9 @@ declare global {
|
|
|
91
99
|
declare class CallForge {
|
|
92
100
|
private readonly config;
|
|
93
101
|
private readonly cache;
|
|
102
|
+
private readonly locationCache;
|
|
94
103
|
private sessionPromise;
|
|
104
|
+
private locationPromise;
|
|
95
105
|
private customParams;
|
|
96
106
|
private constructor();
|
|
97
107
|
/**
|
|
@@ -103,6 +113,19 @@ declare class CallForge {
|
|
|
103
113
|
* Returns cached data if valid, otherwise fetches from API.
|
|
104
114
|
*/
|
|
105
115
|
getSession(): Promise<TrackingSession>;
|
|
116
|
+
/**
|
|
117
|
+
* Get location data only.
|
|
118
|
+
* Returns cached data if valid, otherwise fetches from API.
|
|
119
|
+
*/
|
|
120
|
+
getLocation(): Promise<TrackingLocation | null>;
|
|
121
|
+
/**
|
|
122
|
+
* Kick off both session and location requests.
|
|
123
|
+
* Returns separate Promises so consumers can use each as soon as it resolves.
|
|
124
|
+
*/
|
|
125
|
+
getSessionAsync(): {
|
|
126
|
+
session: Promise<TrackingSession>;
|
|
127
|
+
location: Promise<TrackingLocation | null>;
|
|
128
|
+
};
|
|
106
129
|
/**
|
|
107
130
|
* Create a short-lived call intent token for click/callback deterministic attribution.
|
|
108
131
|
*/
|
|
@@ -112,6 +135,11 @@ declare class CallForge {
|
|
|
112
135
|
* Callback is called once session data is available.
|
|
113
136
|
*/
|
|
114
137
|
onReady(callback: ReadyCallback): void;
|
|
138
|
+
/**
|
|
139
|
+
* Subscribe to location ready event.
|
|
140
|
+
* Callback is called once location data is available.
|
|
141
|
+
*/
|
|
142
|
+
onLocationReady(callback: LocationReadyCallback): void;
|
|
115
143
|
/**
|
|
116
144
|
* Set custom tracking parameters for attribution.
|
|
117
145
|
*
|
|
@@ -139,14 +167,18 @@ declare class CallForge {
|
|
|
139
167
|
*/
|
|
140
168
|
private getGA4ClientIdFromCookie;
|
|
141
169
|
private fetchSession;
|
|
170
|
+
private fetchLocation;
|
|
142
171
|
private getLocationId;
|
|
143
172
|
private getAutoParams;
|
|
144
173
|
private fetchFromApi;
|
|
174
|
+
private fetchLocationFromApi;
|
|
145
175
|
private saveToCache;
|
|
176
|
+
private saveLocationToCache;
|
|
146
177
|
private formatSession;
|
|
147
178
|
private formatApiResponse;
|
|
148
179
|
private syncParamsToCallForgeIfPossible;
|
|
149
180
|
private buildUrl;
|
|
181
|
+
private buildLocationUrl;
|
|
150
182
|
}
|
|
151
183
|
|
|
152
184
|
/**
|
|
@@ -155,4 +187,4 @@ declare class CallForge {
|
|
|
155
187
|
*/
|
|
156
188
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
157
189
|
|
|
158
|
-
export { CallForge, type CallForgeConfig, type CallIntentResponse, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
|
190
|
+
export { CallForge, type CallForgeConfig, type CallIntentResponse, type LocationReadyCallback, type ReadyCallback, type TrackingLocation, type TrackingLocationSource, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
package/dist/index.js
CHANGED
|
@@ -138,6 +138,67 @@ var TrackingCache = class {
|
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
+
// src/location-cache.ts
|
|
142
|
+
var EXPIRY_BUFFER_MS2 = 3e4;
|
|
143
|
+
var LocationCache = class {
|
|
144
|
+
constructor(siteKey) {
|
|
145
|
+
this.memoryCache = null;
|
|
146
|
+
this.useMemory = false;
|
|
147
|
+
const resolvedSiteKey = siteKey || (typeof window !== "undefined" ? window.location.hostname : "unknown-site");
|
|
148
|
+
this.key = `cf_location_v1_${resolvedSiteKey}`;
|
|
149
|
+
this.useMemory = !this.isLocalStorageAvailable();
|
|
150
|
+
}
|
|
151
|
+
isLocalStorageAvailable() {
|
|
152
|
+
try {
|
|
153
|
+
localStorage.setItem("__cf_test__", "1");
|
|
154
|
+
localStorage.removeItem("__cf_test__");
|
|
155
|
+
return true;
|
|
156
|
+
} catch (e) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
get(locationId) {
|
|
161
|
+
const cached = this.read();
|
|
162
|
+
if (!cached) return null;
|
|
163
|
+
if (cached.expiresAt - EXPIRY_BUFFER_MS2 <= Date.now()) return null;
|
|
164
|
+
if (!locationId) return cached;
|
|
165
|
+
if (cached.locId !== locationId) return null;
|
|
166
|
+
return cached;
|
|
167
|
+
}
|
|
168
|
+
set(value) {
|
|
169
|
+
if (this.useMemory) {
|
|
170
|
+
this.memoryCache = value;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
localStorage.setItem(this.key, JSON.stringify(value));
|
|
175
|
+
} catch (e) {
|
|
176
|
+
this.memoryCache = value;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
clear() {
|
|
180
|
+
this.memoryCache = null;
|
|
181
|
+
if (!this.useMemory) {
|
|
182
|
+
try {
|
|
183
|
+
localStorage.removeItem(this.key);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
read() {
|
|
189
|
+
if (this.useMemory) {
|
|
190
|
+
return this.memoryCache;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const raw = localStorage.getItem(this.key);
|
|
194
|
+
if (!raw) return null;
|
|
195
|
+
return JSON.parse(raw);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return this.memoryCache;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
141
202
|
// src/client.ts
|
|
142
203
|
var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
|
|
143
204
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -146,6 +207,7 @@ var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid", "gad_campai
|
|
|
146
207
|
var CallForge = class _CallForge {
|
|
147
208
|
constructor(config) {
|
|
148
209
|
this.sessionPromise = null;
|
|
210
|
+
this.locationPromise = null;
|
|
149
211
|
this.customParams = {};
|
|
150
212
|
this.config = {
|
|
151
213
|
categoryId: config.categoryId,
|
|
@@ -154,6 +216,7 @@ var CallForge = class _CallForge {
|
|
|
154
216
|
siteKey: config.siteKey
|
|
155
217
|
};
|
|
156
218
|
this.cache = new TrackingCache(config.categoryId, config.siteKey);
|
|
219
|
+
this.locationCache = new LocationCache(config.siteKey);
|
|
157
220
|
this.captureGA4ClientId();
|
|
158
221
|
this.startGA4ClientIdPolling();
|
|
159
222
|
}
|
|
@@ -174,6 +237,27 @@ var CallForge = class _CallForge {
|
|
|
174
237
|
this.sessionPromise = this.fetchSession();
|
|
175
238
|
return this.sessionPromise;
|
|
176
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Get location data only.
|
|
242
|
+
* Returns cached data if valid, otherwise fetches from API.
|
|
243
|
+
*/
|
|
244
|
+
async getLocation() {
|
|
245
|
+
if (this.locationPromise) {
|
|
246
|
+
return this.locationPromise;
|
|
247
|
+
}
|
|
248
|
+
this.locationPromise = this.fetchLocation();
|
|
249
|
+
return this.locationPromise;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Kick off both session and location requests.
|
|
253
|
+
* Returns separate Promises so consumers can use each as soon as it resolves.
|
|
254
|
+
*/
|
|
255
|
+
getSessionAsync() {
|
|
256
|
+
return {
|
|
257
|
+
session: this.getSession(),
|
|
258
|
+
location: this.getLocation()
|
|
259
|
+
};
|
|
260
|
+
}
|
|
177
261
|
/**
|
|
178
262
|
* Create a short-lived call intent token for click/callback deterministic attribution.
|
|
179
263
|
*/
|
|
@@ -209,6 +293,14 @@ var CallForge = class _CallForge {
|
|
|
209
293
|
this.getSession().then(callback).catch(() => {
|
|
210
294
|
});
|
|
211
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Subscribe to location ready event.
|
|
298
|
+
* Callback is called once location data is available.
|
|
299
|
+
*/
|
|
300
|
+
onLocationReady(callback) {
|
|
301
|
+
this.getLocation().then(callback).catch(() => {
|
|
302
|
+
});
|
|
303
|
+
}
|
|
212
304
|
/**
|
|
213
305
|
* Set custom tracking parameters for attribution.
|
|
214
306
|
*
|
|
@@ -331,6 +423,37 @@ var CallForge = class _CallForge {
|
|
|
331
423
|
this.saveToCache(locationId, data, params);
|
|
332
424
|
return this.formatApiResponse(data);
|
|
333
425
|
}
|
|
426
|
+
async fetchLocation() {
|
|
427
|
+
var _a;
|
|
428
|
+
const locationId = this.getLocationId();
|
|
429
|
+
if (typeof window !== "undefined" && window.__cfTrackingLocation) {
|
|
430
|
+
try {
|
|
431
|
+
const data2 = await window.__cfTrackingLocation;
|
|
432
|
+
const dataWithExtras = data2;
|
|
433
|
+
const effectiveLocId = (_a = dataWithExtras.locId) != null ? _a : locationId;
|
|
434
|
+
const location2 = data2.location ? {
|
|
435
|
+
city: data2.location.city,
|
|
436
|
+
state: data2.location.state,
|
|
437
|
+
stateCode: data2.location.stateCode
|
|
438
|
+
} : null;
|
|
439
|
+
this.saveLocationToCache(effectiveLocId, location2, data2.expiresAt);
|
|
440
|
+
return location2;
|
|
441
|
+
} catch (e) {
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const cached = this.locationCache.get(locationId);
|
|
445
|
+
if (cached) {
|
|
446
|
+
return cached.location;
|
|
447
|
+
}
|
|
448
|
+
const data = await this.fetchLocationFromApi(locationId);
|
|
449
|
+
const location = data.location ? {
|
|
450
|
+
city: data.location.city,
|
|
451
|
+
state: data.location.state,
|
|
452
|
+
stateCode: data.location.stateCode
|
|
453
|
+
} : null;
|
|
454
|
+
this.saveLocationToCache(locationId, location, data.expiresAt);
|
|
455
|
+
return location;
|
|
456
|
+
}
|
|
334
457
|
getLocationId() {
|
|
335
458
|
if (typeof window === "undefined") return null;
|
|
336
459
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -365,33 +488,55 @@ var CallForge = class _CallForge {
|
|
|
365
488
|
clearTimeout(timeoutId);
|
|
366
489
|
}
|
|
367
490
|
}
|
|
491
|
+
async fetchLocationFromApi(locationId) {
|
|
492
|
+
const url = this.buildLocationUrl(locationId);
|
|
493
|
+
const controller = new AbortController();
|
|
494
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
495
|
+
try {
|
|
496
|
+
const response = await fetch(url, {
|
|
497
|
+
credentials: "omit",
|
|
498
|
+
signal: controller.signal
|
|
499
|
+
});
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
502
|
+
}
|
|
503
|
+
return await response.json();
|
|
504
|
+
} finally {
|
|
505
|
+
clearTimeout(timeoutId);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
368
508
|
saveToCache(locationId, data, params) {
|
|
369
509
|
const cached = {
|
|
370
510
|
locId: locationId != null ? locationId : null,
|
|
371
511
|
sessionToken: data.sessionToken,
|
|
372
512
|
leaseId: data.leaseId,
|
|
373
513
|
phoneNumber: data.phoneNumber,
|
|
374
|
-
location: data.location,
|
|
375
514
|
expiresAt: data.expiresAt,
|
|
376
515
|
tokenVersion: "v1",
|
|
377
516
|
params
|
|
378
517
|
};
|
|
379
518
|
this.cache.set(cached);
|
|
380
519
|
}
|
|
520
|
+
saveLocationToCache(locationId, location, expiresAt) {
|
|
521
|
+
this.locationCache.set({
|
|
522
|
+
locId: locationId != null ? locationId : null,
|
|
523
|
+
location,
|
|
524
|
+
expiresAt,
|
|
525
|
+
tokenVersion: "v1"
|
|
526
|
+
});
|
|
527
|
+
}
|
|
381
528
|
formatSession(cached) {
|
|
382
529
|
return {
|
|
383
530
|
sessionToken: cached.sessionToken,
|
|
384
531
|
leaseId: cached.leaseId,
|
|
385
|
-
phoneNumber: cached.phoneNumber
|
|
386
|
-
location: cached.location
|
|
532
|
+
phoneNumber: cached.phoneNumber
|
|
387
533
|
};
|
|
388
534
|
}
|
|
389
535
|
formatApiResponse(data) {
|
|
390
536
|
return {
|
|
391
537
|
sessionToken: data.sessionToken,
|
|
392
538
|
leaseId: data.leaseId,
|
|
393
|
-
phoneNumber: data.phoneNumber
|
|
394
|
-
location: data.location
|
|
539
|
+
phoneNumber: data.phoneNumber
|
|
395
540
|
};
|
|
396
541
|
}
|
|
397
542
|
async syncParamsToCallForgeIfPossible() {
|
|
@@ -424,6 +569,13 @@ var CallForge = class _CallForge {
|
|
|
424
569
|
const qs = sorted.map((k) => `${k}=${encodeURIComponent(queryParams[k])}`).join("&");
|
|
425
570
|
return `${endpoint}/v1/tracking/session?${qs}`;
|
|
426
571
|
}
|
|
572
|
+
buildLocationUrl(locationId) {
|
|
573
|
+
const { endpoint } = this.config;
|
|
574
|
+
if (!locationId) {
|
|
575
|
+
return `${endpoint}/v1/tracking/location`;
|
|
576
|
+
}
|
|
577
|
+
return `${endpoint}/v1/tracking/location?loc_physical_ms=${encodeURIComponent(locationId)}`;
|
|
578
|
+
}
|
|
427
579
|
};
|
|
428
580
|
|
|
429
581
|
// src/preload.ts
|
|
@@ -453,6 +605,17 @@ if(m){try{var g=decodeURIComponent(m[1]||'');var s=g.split('.');if(s.length>=2){
|
|
|
453
605
|
for(var i=0;i<ap.length;i++){var v=u.get(ap[i]);if(v)p[ap[i]]=v}
|
|
454
606
|
var site='${config.siteKey || ""}'||location.hostname||'unknown-site';
|
|
455
607
|
var key='cf_tracking_v1_'+site+'_${categoryId}';
|
|
608
|
+
var lkey='cf_location_v1_'+site;
|
|
609
|
+
try{
|
|
610
|
+
var cl=JSON.parse(localStorage.getItem(lkey));
|
|
611
|
+
if(cl&&cl.expiresAt>Date.now()+30000){
|
|
612
|
+
if(!loc||(loc&&cl.locId===loc)){window.__cfTrackingLocation=Promise.resolve(cl)}
|
|
613
|
+
}}catch(e){}
|
|
614
|
+
if(!window.__cfTrackingLocation){
|
|
615
|
+
var lurl='${endpoint}/v1/tracking/location';
|
|
616
|
+
if(loc)lurl+='?loc_physical_ms='+encodeURIComponent(loc);
|
|
617
|
+
window.__cfTrackingLocation=fetch(lurl,{credentials:'omit'}).then(function(r){if(!r.ok)throw new Error('tracking preload location failed');return r.json()}).then(function(d){d.locId=loc;try{localStorage.setItem(lkey,JSON.stringify({locId:loc,location:d.location?{city:d.location.city,state:d.location.state,stateCode:d.location.stateCode}:null,expiresAt:d.expiresAt,tokenVersion:'v1'}))}catch(e){}return d});
|
|
618
|
+
}
|
|
456
619
|
var token=null;
|
|
457
620
|
try{
|
|
458
621
|
var c=JSON.parse(localStorage.getItem(key));
|
|
@@ -467,7 +630,7 @@ if(loc)url+='&loc_physical_ms='+loc;
|
|
|
467
630
|
if(token)url+='&sessionToken='+encodeURIComponent(token);
|
|
468
631
|
var ks=Object.keys(p).sort();
|
|
469
632
|
for(var j=0;j<ks.length;j++)url+='&'+ks[j]+'='+encodeURIComponent(p[ks[j]]);
|
|
470
|
-
window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){if(!r.ok)throw new Error('tracking preload failed');return r.json()}).then(function(d){d.params=p;d.locId=loc;try{localStorage.setItem(key,JSON.stringify({locId:loc,sessionToken:d.sessionToken,leaseId:d.leaseId,phoneNumber:d.phoneNumber,
|
|
633
|
+
window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){if(!r.ok)throw new Error('tracking preload failed');return r.json()}).then(function(d){d.params=p;d.locId=loc;try{localStorage.setItem(key,JSON.stringify({locId:loc,sessionToken:d.sessionToken,leaseId:d.leaseId,phoneNumber:d.phoneNumber,expiresAt:d.expiresAt,tokenVersion:'v1',params:p}))}catch(e){}return d});
|
|
471
634
|
})();`.replace(/\n/g, "");
|
|
472
635
|
return `<link rel="preconnect" href="${endpoint}">
|
|
473
636
|
<script>${script}</script>`;
|
package/dist/index.mjs
CHANGED
|
@@ -114,6 +114,67 @@ var TrackingCache = class {
|
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
|
|
117
|
+
// src/location-cache.ts
|
|
118
|
+
var EXPIRY_BUFFER_MS2 = 3e4;
|
|
119
|
+
var LocationCache = class {
|
|
120
|
+
constructor(siteKey) {
|
|
121
|
+
this.memoryCache = null;
|
|
122
|
+
this.useMemory = false;
|
|
123
|
+
const resolvedSiteKey = siteKey || (typeof window !== "undefined" ? window.location.hostname : "unknown-site");
|
|
124
|
+
this.key = `cf_location_v1_${resolvedSiteKey}`;
|
|
125
|
+
this.useMemory = !this.isLocalStorageAvailable();
|
|
126
|
+
}
|
|
127
|
+
isLocalStorageAvailable() {
|
|
128
|
+
try {
|
|
129
|
+
localStorage.setItem("__cf_test__", "1");
|
|
130
|
+
localStorage.removeItem("__cf_test__");
|
|
131
|
+
return true;
|
|
132
|
+
} catch (e) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
get(locationId) {
|
|
137
|
+
const cached = this.read();
|
|
138
|
+
if (!cached) return null;
|
|
139
|
+
if (cached.expiresAt - EXPIRY_BUFFER_MS2 <= Date.now()) return null;
|
|
140
|
+
if (!locationId) return cached;
|
|
141
|
+
if (cached.locId !== locationId) return null;
|
|
142
|
+
return cached;
|
|
143
|
+
}
|
|
144
|
+
set(value) {
|
|
145
|
+
if (this.useMemory) {
|
|
146
|
+
this.memoryCache = value;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
localStorage.setItem(this.key, JSON.stringify(value));
|
|
151
|
+
} catch (e) {
|
|
152
|
+
this.memoryCache = value;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
clear() {
|
|
156
|
+
this.memoryCache = null;
|
|
157
|
+
if (!this.useMemory) {
|
|
158
|
+
try {
|
|
159
|
+
localStorage.removeItem(this.key);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
read() {
|
|
165
|
+
if (this.useMemory) {
|
|
166
|
+
return this.memoryCache;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const raw = localStorage.getItem(this.key);
|
|
170
|
+
if (!raw) return null;
|
|
171
|
+
return JSON.parse(raw);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
return this.memoryCache;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
117
178
|
// src/client.ts
|
|
118
179
|
var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
|
|
119
180
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -122,6 +183,7 @@ var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid", "gad_campai
|
|
|
122
183
|
var CallForge = class _CallForge {
|
|
123
184
|
constructor(config) {
|
|
124
185
|
this.sessionPromise = null;
|
|
186
|
+
this.locationPromise = null;
|
|
125
187
|
this.customParams = {};
|
|
126
188
|
this.config = {
|
|
127
189
|
categoryId: config.categoryId,
|
|
@@ -130,6 +192,7 @@ var CallForge = class _CallForge {
|
|
|
130
192
|
siteKey: config.siteKey
|
|
131
193
|
};
|
|
132
194
|
this.cache = new TrackingCache(config.categoryId, config.siteKey);
|
|
195
|
+
this.locationCache = new LocationCache(config.siteKey);
|
|
133
196
|
this.captureGA4ClientId();
|
|
134
197
|
this.startGA4ClientIdPolling();
|
|
135
198
|
}
|
|
@@ -150,6 +213,27 @@ var CallForge = class _CallForge {
|
|
|
150
213
|
this.sessionPromise = this.fetchSession();
|
|
151
214
|
return this.sessionPromise;
|
|
152
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Get location data only.
|
|
218
|
+
* Returns cached data if valid, otherwise fetches from API.
|
|
219
|
+
*/
|
|
220
|
+
async getLocation() {
|
|
221
|
+
if (this.locationPromise) {
|
|
222
|
+
return this.locationPromise;
|
|
223
|
+
}
|
|
224
|
+
this.locationPromise = this.fetchLocation();
|
|
225
|
+
return this.locationPromise;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Kick off both session and location requests.
|
|
229
|
+
* Returns separate Promises so consumers can use each as soon as it resolves.
|
|
230
|
+
*/
|
|
231
|
+
getSessionAsync() {
|
|
232
|
+
return {
|
|
233
|
+
session: this.getSession(),
|
|
234
|
+
location: this.getLocation()
|
|
235
|
+
};
|
|
236
|
+
}
|
|
153
237
|
/**
|
|
154
238
|
* Create a short-lived call intent token for click/callback deterministic attribution.
|
|
155
239
|
*/
|
|
@@ -185,6 +269,14 @@ var CallForge = class _CallForge {
|
|
|
185
269
|
this.getSession().then(callback).catch(() => {
|
|
186
270
|
});
|
|
187
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Subscribe to location ready event.
|
|
274
|
+
* Callback is called once location data is available.
|
|
275
|
+
*/
|
|
276
|
+
onLocationReady(callback) {
|
|
277
|
+
this.getLocation().then(callback).catch(() => {
|
|
278
|
+
});
|
|
279
|
+
}
|
|
188
280
|
/**
|
|
189
281
|
* Set custom tracking parameters for attribution.
|
|
190
282
|
*
|
|
@@ -307,6 +399,37 @@ var CallForge = class _CallForge {
|
|
|
307
399
|
this.saveToCache(locationId, data, params);
|
|
308
400
|
return this.formatApiResponse(data);
|
|
309
401
|
}
|
|
402
|
+
async fetchLocation() {
|
|
403
|
+
var _a;
|
|
404
|
+
const locationId = this.getLocationId();
|
|
405
|
+
if (typeof window !== "undefined" && window.__cfTrackingLocation) {
|
|
406
|
+
try {
|
|
407
|
+
const data2 = await window.__cfTrackingLocation;
|
|
408
|
+
const dataWithExtras = data2;
|
|
409
|
+
const effectiveLocId = (_a = dataWithExtras.locId) != null ? _a : locationId;
|
|
410
|
+
const location2 = data2.location ? {
|
|
411
|
+
city: data2.location.city,
|
|
412
|
+
state: data2.location.state,
|
|
413
|
+
stateCode: data2.location.stateCode
|
|
414
|
+
} : null;
|
|
415
|
+
this.saveLocationToCache(effectiveLocId, location2, data2.expiresAt);
|
|
416
|
+
return location2;
|
|
417
|
+
} catch (e) {
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const cached = this.locationCache.get(locationId);
|
|
421
|
+
if (cached) {
|
|
422
|
+
return cached.location;
|
|
423
|
+
}
|
|
424
|
+
const data = await this.fetchLocationFromApi(locationId);
|
|
425
|
+
const location = data.location ? {
|
|
426
|
+
city: data.location.city,
|
|
427
|
+
state: data.location.state,
|
|
428
|
+
stateCode: data.location.stateCode
|
|
429
|
+
} : null;
|
|
430
|
+
this.saveLocationToCache(locationId, location, data.expiresAt);
|
|
431
|
+
return location;
|
|
432
|
+
}
|
|
310
433
|
getLocationId() {
|
|
311
434
|
if (typeof window === "undefined") return null;
|
|
312
435
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -341,33 +464,55 @@ var CallForge = class _CallForge {
|
|
|
341
464
|
clearTimeout(timeoutId);
|
|
342
465
|
}
|
|
343
466
|
}
|
|
467
|
+
async fetchLocationFromApi(locationId) {
|
|
468
|
+
const url = this.buildLocationUrl(locationId);
|
|
469
|
+
const controller = new AbortController();
|
|
470
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
471
|
+
try {
|
|
472
|
+
const response = await fetch(url, {
|
|
473
|
+
credentials: "omit",
|
|
474
|
+
signal: controller.signal
|
|
475
|
+
});
|
|
476
|
+
if (!response.ok) {
|
|
477
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
478
|
+
}
|
|
479
|
+
return await response.json();
|
|
480
|
+
} finally {
|
|
481
|
+
clearTimeout(timeoutId);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
344
484
|
saveToCache(locationId, data, params) {
|
|
345
485
|
const cached = {
|
|
346
486
|
locId: locationId != null ? locationId : null,
|
|
347
487
|
sessionToken: data.sessionToken,
|
|
348
488
|
leaseId: data.leaseId,
|
|
349
489
|
phoneNumber: data.phoneNumber,
|
|
350
|
-
location: data.location,
|
|
351
490
|
expiresAt: data.expiresAt,
|
|
352
491
|
tokenVersion: "v1",
|
|
353
492
|
params
|
|
354
493
|
};
|
|
355
494
|
this.cache.set(cached);
|
|
356
495
|
}
|
|
496
|
+
saveLocationToCache(locationId, location, expiresAt) {
|
|
497
|
+
this.locationCache.set({
|
|
498
|
+
locId: locationId != null ? locationId : null,
|
|
499
|
+
location,
|
|
500
|
+
expiresAt,
|
|
501
|
+
tokenVersion: "v1"
|
|
502
|
+
});
|
|
503
|
+
}
|
|
357
504
|
formatSession(cached) {
|
|
358
505
|
return {
|
|
359
506
|
sessionToken: cached.sessionToken,
|
|
360
507
|
leaseId: cached.leaseId,
|
|
361
|
-
phoneNumber: cached.phoneNumber
|
|
362
|
-
location: cached.location
|
|
508
|
+
phoneNumber: cached.phoneNumber
|
|
363
509
|
};
|
|
364
510
|
}
|
|
365
511
|
formatApiResponse(data) {
|
|
366
512
|
return {
|
|
367
513
|
sessionToken: data.sessionToken,
|
|
368
514
|
leaseId: data.leaseId,
|
|
369
|
-
phoneNumber: data.phoneNumber
|
|
370
|
-
location: data.location
|
|
515
|
+
phoneNumber: data.phoneNumber
|
|
371
516
|
};
|
|
372
517
|
}
|
|
373
518
|
async syncParamsToCallForgeIfPossible() {
|
|
@@ -400,6 +545,13 @@ var CallForge = class _CallForge {
|
|
|
400
545
|
const qs = sorted.map((k) => `${k}=${encodeURIComponent(queryParams[k])}`).join("&");
|
|
401
546
|
return `${endpoint}/v1/tracking/session?${qs}`;
|
|
402
547
|
}
|
|
548
|
+
buildLocationUrl(locationId) {
|
|
549
|
+
const { endpoint } = this.config;
|
|
550
|
+
if (!locationId) {
|
|
551
|
+
return `${endpoint}/v1/tracking/location`;
|
|
552
|
+
}
|
|
553
|
+
return `${endpoint}/v1/tracking/location?loc_physical_ms=${encodeURIComponent(locationId)}`;
|
|
554
|
+
}
|
|
403
555
|
};
|
|
404
556
|
|
|
405
557
|
// src/preload.ts
|
|
@@ -429,6 +581,17 @@ if(m){try{var g=decodeURIComponent(m[1]||'');var s=g.split('.');if(s.length>=2){
|
|
|
429
581
|
for(var i=0;i<ap.length;i++){var v=u.get(ap[i]);if(v)p[ap[i]]=v}
|
|
430
582
|
var site='${config.siteKey || ""}'||location.hostname||'unknown-site';
|
|
431
583
|
var key='cf_tracking_v1_'+site+'_${categoryId}';
|
|
584
|
+
var lkey='cf_location_v1_'+site;
|
|
585
|
+
try{
|
|
586
|
+
var cl=JSON.parse(localStorage.getItem(lkey));
|
|
587
|
+
if(cl&&cl.expiresAt>Date.now()+30000){
|
|
588
|
+
if(!loc||(loc&&cl.locId===loc)){window.__cfTrackingLocation=Promise.resolve(cl)}
|
|
589
|
+
}}catch(e){}
|
|
590
|
+
if(!window.__cfTrackingLocation){
|
|
591
|
+
var lurl='${endpoint}/v1/tracking/location';
|
|
592
|
+
if(loc)lurl+='?loc_physical_ms='+encodeURIComponent(loc);
|
|
593
|
+
window.__cfTrackingLocation=fetch(lurl,{credentials:'omit'}).then(function(r){if(!r.ok)throw new Error('tracking preload location failed');return r.json()}).then(function(d){d.locId=loc;try{localStorage.setItem(lkey,JSON.stringify({locId:loc,location:d.location?{city:d.location.city,state:d.location.state,stateCode:d.location.stateCode}:null,expiresAt:d.expiresAt,tokenVersion:'v1'}))}catch(e){}return d});
|
|
594
|
+
}
|
|
432
595
|
var token=null;
|
|
433
596
|
try{
|
|
434
597
|
var c=JSON.parse(localStorage.getItem(key));
|
|
@@ -443,7 +606,7 @@ if(loc)url+='&loc_physical_ms='+loc;
|
|
|
443
606
|
if(token)url+='&sessionToken='+encodeURIComponent(token);
|
|
444
607
|
var ks=Object.keys(p).sort();
|
|
445
608
|
for(var j=0;j<ks.length;j++)url+='&'+ks[j]+'='+encodeURIComponent(p[ks[j]]);
|
|
446
|
-
window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){if(!r.ok)throw new Error('tracking preload failed');return r.json()}).then(function(d){d.params=p;d.locId=loc;try{localStorage.setItem(key,JSON.stringify({locId:loc,sessionToken:d.sessionToken,leaseId:d.leaseId,phoneNumber:d.phoneNumber,
|
|
609
|
+
window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){if(!r.ok)throw new Error('tracking preload failed');return r.json()}).then(function(d){d.params=p;d.locId=loc;try{localStorage.setItem(key,JSON.stringify({locId:loc,sessionToken:d.sessionToken,leaseId:d.leaseId,phoneNumber:d.phoneNumber,expiresAt:d.expiresAt,tokenVersion:'v1',params:p}))}catch(e){}return d});
|
|
447
610
|
})();`.replace(/\n/g, "");
|
|
448
611
|
return `<link rel="preconnect" href="${endpoint}">
|
|
449
612
|
<script>${script}</script>`;
|