@callforge/tracking-client 0.3.0 → 0.4.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 +112 -16
- package/dist/index.d.mts +18 -3
- package/dist/index.d.ts +18 -3
- package/dist/index.js +24 -5
- package/dist/index.mjs +23 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Lightweight client library for the CallForge tracking API. Handles location-aware phone number assignment with aggressive caching and preload optimization.
|
|
4
4
|
|
|
5
|
+
**v0.4.0** - Added `ga4MeasurementId` config option for cleaner gtag callback instead of cookie polling.
|
|
6
|
+
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -51,7 +53,51 @@ client.onReady((session) => {
|
|
|
51
53
|
});
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
### 3.
|
|
56
|
+
### 3. GA4 Integration (automatic)
|
|
57
|
+
|
|
58
|
+
The client automatically captures the GA4 client ID and associates it with the tracking session. This enables CallForge to send call events (phone call, conversion, billable conversion) to your Google Analytics 4 property.
|
|
59
|
+
|
|
60
|
+
**Requirements:**
|
|
61
|
+
1. Google Analytics 4 must be installed on your site
|
|
62
|
+
2. Configure GA4 credentials in CallForge dashboard (Categories → Edit → GA4 tab)
|
|
63
|
+
|
|
64
|
+
**Recommended: Provide Measurement ID for callback-based capture:**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const client = CallForge.init({
|
|
68
|
+
categoryId: 'your-category-id',
|
|
69
|
+
ga4MeasurementId: 'G-XXXXXXXXXX', // Enables gtag('get') callback
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
When `ga4MeasurementId` is provided, the client uses the official `gtag('get')` API callback to retrieve the client ID. This is cleaner and more reliable than cookie polling.
|
|
74
|
+
|
|
75
|
+
**How it works:**
|
|
76
|
+
1. **If `ga4MeasurementId` is provided AND `window.gtag` exists:**
|
|
77
|
+
- Uses `gtag('get', measurementId, 'client_id', callback)` to get the client ID
|
|
78
|
+
- No polling needed - callback fires when GA4 is ready
|
|
79
|
+
2. **Otherwise (fallback):**
|
|
80
|
+
- Checks for the `_ga` cookie immediately
|
|
81
|
+
- If not found, polls every 500ms for up to 10 seconds
|
|
82
|
+
- Handles async GA4 script loading
|
|
83
|
+
|
|
84
|
+
**Manual override:** If you need to pass a custom GA4 client ID:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
client.setParams({
|
|
88
|
+
ga4ClientId: '1234567890.1234567890',
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Extract GA4 client ID yourself:**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { getGA4ClientId } from '@callforge/tracking-client';
|
|
96
|
+
|
|
97
|
+
const clientId = getGA4ClientId(); // "1234567890.1234567890" or null
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 4. Track conversion parameters (optional)
|
|
55
101
|
|
|
56
102
|
The client automatically captures ad platform click IDs from the URL:
|
|
57
103
|
|
|
@@ -82,8 +128,10 @@ Initialize the tracking client.
|
|
|
82
128
|
|
|
83
129
|
```typescript
|
|
84
130
|
interface CallForgeConfig {
|
|
85
|
-
categoryId: string;
|
|
86
|
-
endpoint?: string;
|
|
131
|
+
categoryId: string; // Required - which number pool to use
|
|
132
|
+
endpoint?: string; // Optional - defaults to 'https://tracking.callforge.io'
|
|
133
|
+
ga4MeasurementId?: string; // Optional - GA4 Measurement ID (e.g., "G-XXXXXXXXXX")
|
|
134
|
+
// Enables gtag callback instead of cookie polling
|
|
87
135
|
}
|
|
88
136
|
```
|
|
89
137
|
|
|
@@ -150,19 +198,35 @@ const html = getPreloadSnippet({
|
|
|
150
198
|
});
|
|
151
199
|
```
|
|
152
200
|
|
|
201
|
+
### `getGA4ClientId()`
|
|
202
|
+
|
|
203
|
+
Extract the GA4 client ID from the `_ga` cookie.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { getGA4ClientId } from '@callforge/tracking-client';
|
|
207
|
+
|
|
208
|
+
const clientId = getGA4ClientId();
|
|
209
|
+
// Returns: "1234567890.1234567890" or null if cookie not found
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Cookie format:** `GA1.1.1234567890.1234567890`
|
|
213
|
+
|
|
214
|
+
The client ID is the last two dot-separated segments (timestamp.random).
|
|
215
|
+
|
|
153
216
|
## Tracking Parameters
|
|
154
217
|
|
|
155
218
|
### Auto-Capture
|
|
156
219
|
|
|
157
|
-
The client automatically extracts these parameters
|
|
220
|
+
The client automatically extracts these parameters:
|
|
158
221
|
|
|
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 |
|
|
222
|
+
| Parameter | Source | Capture Method |
|
|
223
|
+
|-----------|--------|----------------|
|
|
224
|
+
| `gclid` | Google Ads Click ID | URL query param |
|
|
225
|
+
| `gbraid` | Google app-to-web (iOS 14+) | URL query param |
|
|
226
|
+
| `wbraid` | Google web-to-app | URL query param |
|
|
227
|
+
| `msclkid` | Microsoft/Bing Ads | URL query param |
|
|
228
|
+
| `fbclid` | Facebook/Meta Ads | URL query param |
|
|
229
|
+
| `ga4ClientId` | Google Analytics 4 | `_ga` cookie (polled) |
|
|
166
230
|
|
|
167
231
|
### Persistence
|
|
168
232
|
|
|
@@ -214,21 +278,53 @@ import type {
|
|
|
214
278
|
TrackingParams,
|
|
215
279
|
ReadyCallback,
|
|
216
280
|
} from '@callforge/tracking-client';
|
|
281
|
+
|
|
282
|
+
import { getGA4ClientId } from '@callforge/tracking-client';
|
|
217
283
|
```
|
|
218
284
|
|
|
219
285
|
### TrackingParams
|
|
220
286
|
|
|
221
287
|
```typescript
|
|
222
288
|
interface TrackingParams {
|
|
223
|
-
gclid?: string;
|
|
224
|
-
gbraid?: string;
|
|
225
|
-
wbraid?: string;
|
|
226
|
-
msclkid?: string;
|
|
227
|
-
fbclid?: string;
|
|
289
|
+
gclid?: string; // Google Ads Click ID
|
|
290
|
+
gbraid?: string; // Google app-to-web (iOS 14+)
|
|
291
|
+
wbraid?: string; // Google web-to-app
|
|
292
|
+
msclkid?: string; // Microsoft/Bing Ads
|
|
293
|
+
fbclid?: string; // Facebook/Meta Ads
|
|
294
|
+
ga4ClientId?: string; // Google Analytics 4 Client ID (auto-captured from _ga cookie)
|
|
228
295
|
[key: string]: string | undefined; // Custom params
|
|
229
296
|
}
|
|
230
297
|
```
|
|
231
298
|
|
|
299
|
+
## GA4 Events
|
|
300
|
+
|
|
301
|
+
When GA4 is configured for a category, CallForge sends these events to Google Analytics 4 via the Measurement Protocol:
|
|
302
|
+
|
|
303
|
+
| Event Name | When Sent | Description |
|
|
304
|
+
|------------|-----------|-------------|
|
|
305
|
+
| `phone_call` | Call initiated | A phone call was placed to the tracking number |
|
|
306
|
+
| `call_conversion` | Call qualified | The call met conversion criteria (e.g., duration threshold) |
|
|
307
|
+
| `call_conversion_billable` | Call billable | The call was both qualified AND billable |
|
|
308
|
+
|
|
309
|
+
**Event Parameters:**
|
|
310
|
+
|
|
311
|
+
All events include:
|
|
312
|
+
- `session_id` - CallForge session ID
|
|
313
|
+
- `phone_number` - Tracking number dialed
|
|
314
|
+
- `category` - Category slug
|
|
315
|
+
|
|
316
|
+
`call_conversion` and `call_conversion_billable` also include:
|
|
317
|
+
- `call_duration` - Call duration in seconds
|
|
318
|
+
- `buyer` - Buyer the call was routed to (if applicable)
|
|
319
|
+
|
|
320
|
+
**Setup:**
|
|
321
|
+
1. In Google Analytics 4, go to Admin → Data Streams → your web stream
|
|
322
|
+
2. Copy the **Measurement ID** (e.g., `G-XXXXXXXXXX`)
|
|
323
|
+
3. Go to Admin → Data Streams → Measurement Protocol API secrets → Create
|
|
324
|
+
4. Copy the **API Secret**
|
|
325
|
+
5. In CallForge dashboard: Categories → Edit category → GA4 tab
|
|
326
|
+
6. Enable GA4, paste Measurement ID and API Secret, save
|
|
327
|
+
|
|
232
328
|
## Environment URLs
|
|
233
329
|
|
|
234
330
|
| Environment | Endpoint |
|
package/dist/index.d.mts
CHANGED
|
@@ -6,6 +6,8 @@ interface CallForgeConfig {
|
|
|
6
6
|
categoryId: string;
|
|
7
7
|
/** Optional - API endpoint. Defaults to 'https://tracking.callforge.io' */
|
|
8
8
|
endpoint?: string;
|
|
9
|
+
/** Optional - GA4 Measurement ID (e.g., "G-XXXXXXXXXX"). Enables gtag callback instead of cookie polling. */
|
|
10
|
+
ga4MeasurementId?: string;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Location data returned by the tracking API.
|
|
@@ -64,14 +66,21 @@ interface ApiResponse {
|
|
|
64
66
|
*/
|
|
65
67
|
type ReadyCallback = (session: TrackingSession) => void;
|
|
66
68
|
/**
|
|
67
|
-
* Global window extension for preload promise.
|
|
69
|
+
* Global window extension for preload promise and gtag.
|
|
68
70
|
*/
|
|
69
71
|
declare global {
|
|
70
72
|
interface Window {
|
|
71
73
|
__cfTracking?: Promise<ApiResponse>;
|
|
74
|
+
gtag?: (command: 'get', targetId: string, fieldName: string, callback: (value: string) => void) => void;
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Extract GA4 client_id from the _ga cookie.
|
|
80
|
+
* Cookie format: GA1.1.1234567890.1234567890
|
|
81
|
+
* Client ID is the last two segments: 1234567890.1234567890
|
|
82
|
+
*/
|
|
83
|
+
declare function getGA4ClientId(): string | null;
|
|
75
84
|
declare class CallForge {
|
|
76
85
|
private readonly config;
|
|
77
86
|
private readonly cache;
|
|
@@ -107,12 +116,18 @@ declare class CallForge {
|
|
|
107
116
|
*/
|
|
108
117
|
getQueuedParams(): TrackingParams;
|
|
109
118
|
private patchSession;
|
|
119
|
+
/**
|
|
120
|
+
* Start capturing the GA4 client ID.
|
|
121
|
+
* If ga4MeasurementId is configured, uses gtag('get') callback.
|
|
122
|
+
* Otherwise falls back to polling the _ga cookie.
|
|
123
|
+
*/
|
|
124
|
+
startGA4ClientIdCapture(): void;
|
|
110
125
|
/**
|
|
111
126
|
* Start polling for the GA4 _ga cookie.
|
|
112
127
|
* If found immediately, calls setParams right away.
|
|
113
128
|
* Otherwise polls every 500ms for up to 10 seconds.
|
|
114
129
|
*/
|
|
115
|
-
startGA4CookiePolling
|
|
130
|
+
private startGA4CookiePolling;
|
|
116
131
|
private stopGA4CookiePolling;
|
|
117
132
|
private fetchSession;
|
|
118
133
|
private getLocationId;
|
|
@@ -141,4 +156,4 @@ declare class CallForge {
|
|
|
141
156
|
*/
|
|
142
157
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
143
158
|
|
|
144
|
-
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
|
159
|
+
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getGA4ClientId, getPreloadSnippet };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ interface CallForgeConfig {
|
|
|
6
6
|
categoryId: string;
|
|
7
7
|
/** Optional - API endpoint. Defaults to 'https://tracking.callforge.io' */
|
|
8
8
|
endpoint?: string;
|
|
9
|
+
/** Optional - GA4 Measurement ID (e.g., "G-XXXXXXXXXX"). Enables gtag callback instead of cookie polling. */
|
|
10
|
+
ga4MeasurementId?: string;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Location data returned by the tracking API.
|
|
@@ -64,14 +66,21 @@ interface ApiResponse {
|
|
|
64
66
|
*/
|
|
65
67
|
type ReadyCallback = (session: TrackingSession) => void;
|
|
66
68
|
/**
|
|
67
|
-
* Global window extension for preload promise.
|
|
69
|
+
* Global window extension for preload promise and gtag.
|
|
68
70
|
*/
|
|
69
71
|
declare global {
|
|
70
72
|
interface Window {
|
|
71
73
|
__cfTracking?: Promise<ApiResponse>;
|
|
74
|
+
gtag?: (command: 'get', targetId: string, fieldName: string, callback: (value: string) => void) => void;
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Extract GA4 client_id from the _ga cookie.
|
|
80
|
+
* Cookie format: GA1.1.1234567890.1234567890
|
|
81
|
+
* Client ID is the last two segments: 1234567890.1234567890
|
|
82
|
+
*/
|
|
83
|
+
declare function getGA4ClientId(): string | null;
|
|
75
84
|
declare class CallForge {
|
|
76
85
|
private readonly config;
|
|
77
86
|
private readonly cache;
|
|
@@ -107,12 +116,18 @@ declare class CallForge {
|
|
|
107
116
|
*/
|
|
108
117
|
getQueuedParams(): TrackingParams;
|
|
109
118
|
private patchSession;
|
|
119
|
+
/**
|
|
120
|
+
* Start capturing the GA4 client ID.
|
|
121
|
+
* If ga4MeasurementId is configured, uses gtag('get') callback.
|
|
122
|
+
* Otherwise falls back to polling the _ga cookie.
|
|
123
|
+
*/
|
|
124
|
+
startGA4ClientIdCapture(): void;
|
|
110
125
|
/**
|
|
111
126
|
* Start polling for the GA4 _ga cookie.
|
|
112
127
|
* If found immediately, calls setParams right away.
|
|
113
128
|
* Otherwise polls every 500ms for up to 10 seconds.
|
|
114
129
|
*/
|
|
115
|
-
startGA4CookiePolling
|
|
130
|
+
private startGA4CookiePolling;
|
|
116
131
|
private stopGA4CookiePolling;
|
|
117
132
|
private fetchSession;
|
|
118
133
|
private getLocationId;
|
|
@@ -141,4 +156,4 @@ declare class CallForge {
|
|
|
141
156
|
*/
|
|
142
157
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
143
158
|
|
|
144
|
-
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
|
159
|
+
export { CallForge, type CallForgeConfig, type ReadyCallback, type TrackingLocation, type TrackingParams, type TrackingSession, getGA4ClientId, getPreloadSnippet };
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
35
35
|
var index_exports = {};
|
|
36
36
|
__export(index_exports, {
|
|
37
37
|
CallForge: () => CallForge,
|
|
38
|
+
getGA4ClientId: () => getGA4ClientId,
|
|
38
39
|
getPreloadSnippet: () => getPreloadSnippet
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -146,10 +147,11 @@ var CallForge = class _CallForge {
|
|
|
146
147
|
this.sessionCreated = false;
|
|
147
148
|
this.config = {
|
|
148
149
|
categoryId: config.categoryId,
|
|
149
|
-
endpoint: config.endpoint || DEFAULT_ENDPOINT
|
|
150
|
+
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
151
|
+
ga4MeasurementId: config.ga4MeasurementId
|
|
150
152
|
};
|
|
151
153
|
this.cache = new TrackingCache(config.categoryId);
|
|
152
|
-
this.
|
|
154
|
+
this.startGA4ClientIdCapture();
|
|
153
155
|
}
|
|
154
156
|
/**
|
|
155
157
|
* Initialize the CallForge tracking client.
|
|
@@ -210,15 +212,31 @@ var CallForge = class _CallForge {
|
|
|
210
212
|
console.warn("[CallForge] Failed to patch session:", error);
|
|
211
213
|
}
|
|
212
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Start capturing the GA4 client ID.
|
|
217
|
+
* If ga4MeasurementId is configured, uses gtag('get') callback.
|
|
218
|
+
* Otherwise falls back to polling the _ga cookie.
|
|
219
|
+
*/
|
|
220
|
+
startGA4ClientIdCapture() {
|
|
221
|
+
if (this.ga4PollInterval !== null || this.ga4PollTimeout !== null) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (this.config.ga4MeasurementId && typeof window !== "undefined" && window.gtag) {
|
|
225
|
+
window.gtag("get", this.config.ga4MeasurementId, "client_id", (clientId) => {
|
|
226
|
+
if (clientId) {
|
|
227
|
+
this.setParams({ ga4ClientId: clientId });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this.startGA4CookiePolling();
|
|
233
|
+
}
|
|
213
234
|
/**
|
|
214
235
|
* Start polling for the GA4 _ga cookie.
|
|
215
236
|
* If found immediately, calls setParams right away.
|
|
216
237
|
* Otherwise polls every 500ms for up to 10 seconds.
|
|
217
238
|
*/
|
|
218
239
|
startGA4CookiePolling() {
|
|
219
|
-
if (this.ga4PollInterval !== null || this.ga4PollTimeout !== null) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
240
|
const clientId = getGA4ClientId();
|
|
223
241
|
if (clientId) {
|
|
224
242
|
this.setParams({ ga4ClientId: clientId });
|
|
@@ -404,5 +422,6 @@ window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){return r.js
|
|
|
404
422
|
// Annotate the CommonJS export names for ESM import in node:
|
|
405
423
|
0 && (module.exports = {
|
|
406
424
|
CallForge,
|
|
425
|
+
getGA4ClientId,
|
|
407
426
|
getPreloadSnippet
|
|
408
427
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -122,10 +122,11 @@ var CallForge = class _CallForge {
|
|
|
122
122
|
this.sessionCreated = false;
|
|
123
123
|
this.config = {
|
|
124
124
|
categoryId: config.categoryId,
|
|
125
|
-
endpoint: config.endpoint || DEFAULT_ENDPOINT
|
|
125
|
+
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
126
|
+
ga4MeasurementId: config.ga4MeasurementId
|
|
126
127
|
};
|
|
127
128
|
this.cache = new TrackingCache(config.categoryId);
|
|
128
|
-
this.
|
|
129
|
+
this.startGA4ClientIdCapture();
|
|
129
130
|
}
|
|
130
131
|
/**
|
|
131
132
|
* Initialize the CallForge tracking client.
|
|
@@ -186,15 +187,31 @@ var CallForge = class _CallForge {
|
|
|
186
187
|
console.warn("[CallForge] Failed to patch session:", error);
|
|
187
188
|
}
|
|
188
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Start capturing the GA4 client ID.
|
|
192
|
+
* If ga4MeasurementId is configured, uses gtag('get') callback.
|
|
193
|
+
* Otherwise falls back to polling the _ga cookie.
|
|
194
|
+
*/
|
|
195
|
+
startGA4ClientIdCapture() {
|
|
196
|
+
if (this.ga4PollInterval !== null || this.ga4PollTimeout !== null) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (this.config.ga4MeasurementId && typeof window !== "undefined" && window.gtag) {
|
|
200
|
+
window.gtag("get", this.config.ga4MeasurementId, "client_id", (clientId) => {
|
|
201
|
+
if (clientId) {
|
|
202
|
+
this.setParams({ ga4ClientId: clientId });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.startGA4CookiePolling();
|
|
208
|
+
}
|
|
189
209
|
/**
|
|
190
210
|
* Start polling for the GA4 _ga cookie.
|
|
191
211
|
* If found immediately, calls setParams right away.
|
|
192
212
|
* Otherwise polls every 500ms for up to 10 seconds.
|
|
193
213
|
*/
|
|
194
214
|
startGA4CookiePolling() {
|
|
195
|
-
if (this.ga4PollInterval !== null || this.ga4PollTimeout !== null) {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
215
|
const clientId = getGA4ClientId();
|
|
199
216
|
if (clientId) {
|
|
200
217
|
this.setParams({ ga4ClientId: clientId });
|
|
@@ -379,5 +396,6 @@ window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){return r.js
|
|
|
379
396
|
}
|
|
380
397
|
export {
|
|
381
398
|
CallForge,
|
|
399
|
+
getGA4ClientId,
|
|
382
400
|
getPreloadSnippet
|
|
383
401
|
};
|