@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 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 (!locationId) {
147
- return { sessionId: null, phoneNumber: null, location: null };
148
- }
149
- const cached = this.cache.get(locationId);
150
- if (cached) {
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
- if (typeof window !== "undefined" && window.__cfTracking) {
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
- this.saveToCache(locationId, data);
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
- async fetchFromApi(locationId, sessionId) {
172
- const { categoryId, endpoint } = this.config;
173
- let url = `${endpoint}/v1/tracking/session?categoryId=${categoryId}&loc_physical_ms=${locationId}`;
174
- if (sessionId) {
175
- url += `&sessionId=${sessionId}`;
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 loc=new URLSearchParams(location.search).get('loc_physical_ms');
238
- if(!loc)return;
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
- try{
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
- var sid=(c&&c.locId===loc)?c.sessionId:null;
244
- }catch(e){}
245
- var url='${endpoint}/v1/tracking/session?categoryId=${categoryId}&loc_physical_ms='+loc;
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
- window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){return r.json()});
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 (!locationId) {
120
- return { sessionId: null, phoneNumber: null, location: null };
121
- }
122
- const cached = this.cache.get(locationId);
123
- if (cached) {
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
- if (typeof window !== "undefined" && window.__cfTracking) {
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
- this.saveToCache(locationId, data);
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
- async fetchFromApi(locationId, sessionId) {
145
- const { categoryId, endpoint } = this.config;
146
- let url = `${endpoint}/v1/tracking/session?categoryId=${categoryId}&loc_physical_ms=${locationId}`;
147
- if (sessionId) {
148
- url += `&sessionId=${sessionId}`;
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 loc=new URLSearchParams(location.search).get('loc_physical_ms');
211
- if(!loc)return;
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
- try{
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
- var sid=(c&&c.locId===loc)?c.sessionId:null;
217
- }catch(e){}
218
- var url='${endpoint}/v1/tracking/session?categoryId=${categoryId}&loc_physical_ms='+loc;
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
- window.__cfTracking=fetch(url,{credentials:'omit'}).then(function(r){return r.json()});
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.1",
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
+ }