@callforge/tracking-client 0.7.1 → 0.8.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
@@ -1,6 +1,6 @@
1
1
  # @callforge/tracking-client
2
2
 
3
- Lightweight client library for the CallForge tracking API. Handles location-aware phone number assignment with aggressive caching and optional preload optimization.
3
+ Lightweight client library for the CallForge tracking API. Handles location-aware phone number assignment with aggressive caching and preload optimization.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,11 +10,20 @@ npm install @callforge/tracking-client
10
10
  pnpm add @callforge/tracking-client
11
11
  ```
12
12
 
13
+ ## Lease Hardening Migration (v0.8+)
14
+
15
+ This release adds bootstrap-token support for lease hardening and bot suppression.
16
+
17
+ Client integration requirements:
18
+ - Add the generated preload snippet in `<head>`. This prefetches bootstrap tokens and keeps session lookup fast.
19
+ - Keep handling `phoneNumber` and `leaseId` separately. A request can return a phone number with `leaseId: null` when lease assignment is intentionally suppressed.
20
+ - For attribution/scale metrics, treat `leaseId` as the source of truth for lease-backed traffic.
21
+
13
22
  ## Quick Start
14
23
 
15
- ### 1. Add preload snippet to `<head>` (optional but recommended)
24
+ ### 1. Add preload snippet to `<head>` (required for deterministic leases)
16
25
 
17
- For optimal performance on static sites, add this snippet to your HTML `<head>`:
26
+ For optimal performance and lease hardening, add this snippet to your HTML `<head>`:
18
27
 
19
28
  ```typescript
20
29
  import { getPreloadSnippet } from '@callforge/tracking-client';
@@ -158,6 +167,7 @@ Behavior:
158
167
  - Returns cached data if valid.
159
168
  - Fetches fresh data when cache is missing/expired.
160
169
  - If `loc_physical_ms` is present in the URL, cached sessions are only reused when it matches the cached `locId`.
170
+ - If lease assignment is suppressed (for example bot traffic or missing/invalid bootstrap), `phoneNumber` may be present while `leaseId` is `null`.
161
171
  - Throws on network errors or API errors.
162
172
 
163
173
  ### `client.getLocation()`
@@ -282,6 +292,7 @@ Parameters are sent as a sorted query string for cache consistency:
282
292
 
283
293
  - Session cache key: `cf_tracking_v1_<siteKey>_<categoryId>`
284
294
  - Location cache key: `cf_location_v1_<siteKey>`
295
+ - Bootstrap cache key: `cf_bootstrap_v1_<siteKey>_<categoryId>`
285
296
  - TTL: controlled by the server `expiresAt` response (currently 30 minutes)
286
297
  - Storage: localStorage (falls back to memory if unavailable)
287
298
 
package/dist/index.d.mts CHANGED
@@ -103,6 +103,8 @@ declare class CallForge {
103
103
  private readonly config;
104
104
  private readonly cache;
105
105
  private readonly locationCache;
106
+ private readonly bootstrapCacheKey;
107
+ private bootstrapMemoryCache;
106
108
  private sessionPromise;
107
109
  private locationPromise;
108
110
  private customParams;
@@ -174,7 +176,10 @@ declare class CallForge {
174
176
  private getLocationId;
175
177
  private getAutoParams;
176
178
  private fetchFromApi;
179
+ private getBootstrapToken;
177
180
  private fetchLocationFromApi;
181
+ private getCachedBootstrapToken;
182
+ private saveBootstrapToken;
178
183
  private saveToCache;
179
184
  private saveLocationToCache;
180
185
  private toTrackingLocation;
@@ -183,6 +188,7 @@ declare class CallForge {
183
188
  private formatApiResponse;
184
189
  private syncParamsToCallForgeIfPossible;
185
190
  private buildUrl;
191
+ private buildBootstrapUrl;
186
192
  private buildLocationUrl;
187
193
  }
188
194
 
package/dist/index.d.ts CHANGED
@@ -103,6 +103,8 @@ declare class CallForge {
103
103
  private readonly config;
104
104
  private readonly cache;
105
105
  private readonly locationCache;
106
+ private readonly bootstrapCacheKey;
107
+ private bootstrapMemoryCache;
106
108
  private sessionPromise;
107
109
  private locationPromise;
108
110
  private customParams;
@@ -174,7 +176,10 @@ declare class CallForge {
174
176
  private getLocationId;
175
177
  private getAutoParams;
176
178
  private fetchFromApi;
179
+ private getBootstrapToken;
177
180
  private fetchLocationFromApi;
181
+ private getCachedBootstrapToken;
182
+ private saveBootstrapToken;
178
183
  private saveToCache;
179
184
  private saveLocationToCache;
180
185
  private toTrackingLocation;
@@ -183,6 +188,7 @@ declare class CallForge {
183
188
  private formatApiResponse;
184
189
  private syncParamsToCallForgeIfPossible;
185
190
  private buildUrl;
191
+ private buildBootstrapUrl;
186
192
  private buildLocationUrl;
187
193
  }
188
194
 
package/dist/index.js CHANGED
@@ -203,9 +203,11 @@ var LocationCache = class {
203
203
  var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
204
204
  var FETCH_TIMEOUT_MS = 1e4;
205
205
  var CALL_INTENT_TIMEOUT_MS = 8e3;
206
+ var BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS = 1e4;
206
207
  var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid", "gad_campaignid", "gad_source"];
207
208
  var CallForge = class _CallForge {
208
209
  constructor(config) {
210
+ this.bootstrapMemoryCache = null;
209
211
  this.sessionPromise = null;
210
212
  this.locationPromise = null;
211
213
  this.customParams = {};
@@ -215,8 +217,10 @@ var CallForge = class _CallForge {
215
217
  ga4MeasurementId: config.ga4MeasurementId,
216
218
  siteKey: config.siteKey
217
219
  };
220
+ const resolvedSiteKey = config.siteKey || (typeof window !== "undefined" ? window.location.hostname : "unknown-site");
218
221
  this.cache = new TrackingCache(config.categoryId, config.siteKey);
219
222
  this.locationCache = new LocationCache(config.siteKey);
223
+ this.bootstrapCacheKey = `cf_bootstrap_v1_${resolvedSiteKey}_${config.categoryId}`;
220
224
  this.captureGA4ClientId();
221
225
  this.startGA4ClientIdPolling();
222
226
  }
@@ -464,7 +468,43 @@ var CallForge = class _CallForge {
464
468
  return params;
465
469
  }
466
470
  async fetchFromApi(locationId, sessionToken, params) {
467
- const url = this.buildUrl(locationId, sessionToken, params);
471
+ const controller = new AbortController();
472
+ const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
473
+ try {
474
+ let bootstrapToken = this.getCachedBootstrapToken();
475
+ let response = await fetch(
476
+ this.buildUrl(locationId, sessionToken, params, bootstrapToken),
477
+ {
478
+ credentials: "omit",
479
+ signal: controller.signal
480
+ }
481
+ );
482
+ if (response.status === 401) {
483
+ bootstrapToken = await this.getBootstrapToken(true);
484
+ if (bootstrapToken) {
485
+ response = await fetch(
486
+ this.buildUrl(locationId, sessionToken, params, bootstrapToken),
487
+ {
488
+ credentials: "omit",
489
+ signal: controller.signal
490
+ }
491
+ );
492
+ }
493
+ }
494
+ if (!response.ok) {
495
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
496
+ }
497
+ return await response.json();
498
+ } finally {
499
+ clearTimeout(timeoutId);
500
+ }
501
+ }
502
+ async getBootstrapToken(forceRefresh = false) {
503
+ const cached = this.getCachedBootstrapToken();
504
+ if (!forceRefresh && cached) {
505
+ return cached;
506
+ }
507
+ const url = this.buildBootstrapUrl();
468
508
  const controller = new AbortController();
469
509
  const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
470
510
  try {
@@ -473,9 +513,14 @@ var CallForge = class _CallForge {
473
513
  signal: controller.signal
474
514
  });
475
515
  if (!response.ok) {
476
- throw new Error(`API error: ${response.status} ${response.statusText}`);
516
+ return null;
477
517
  }
478
- return await response.json();
518
+ const data = await response.json();
519
+ if (typeof data.bootstrapToken !== "string" || typeof data.expiresAt !== "number") {
520
+ return null;
521
+ }
522
+ this.saveBootstrapToken(data.bootstrapToken, data.expiresAt);
523
+ return data.bootstrapToken;
479
524
  } finally {
480
525
  clearTimeout(timeoutId);
481
526
  }
@@ -497,6 +542,40 @@ var CallForge = class _CallForge {
497
542
  clearTimeout(timeoutId);
498
543
  }
499
544
  }
545
+ getCachedBootstrapToken() {
546
+ var _a;
547
+ const now = Date.now();
548
+ const fromMemory = this.bootstrapMemoryCache;
549
+ if (fromMemory && fromMemory.expiresAt - BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS > now) {
550
+ return fromMemory.token;
551
+ }
552
+ try {
553
+ const raw = localStorage.getItem(this.bootstrapCacheKey);
554
+ if (!raw) return null;
555
+ const cached = JSON.parse(raw);
556
+ if (typeof cached.token !== "string" || typeof cached.expiresAt !== "number") {
557
+ return null;
558
+ }
559
+ if (cached.expiresAt - BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS <= now) {
560
+ return null;
561
+ }
562
+ return cached.token;
563
+ } catch (e) {
564
+ return (_a = fromMemory == null ? void 0 : fromMemory.token) != null ? _a : null;
565
+ }
566
+ }
567
+ saveBootstrapToken(token, expiresAt) {
568
+ const cached = {
569
+ token,
570
+ expiresAt,
571
+ tokenVersion: "b1"
572
+ };
573
+ this.bootstrapMemoryCache = cached;
574
+ try {
575
+ localStorage.setItem(this.bootstrapCacheKey, JSON.stringify(cached));
576
+ } catch (e) {
577
+ }
578
+ }
500
579
  saveToCache(locationId, data, params) {
501
580
  const cached = {
502
581
  locId: locationId != null ? locationId : null,
@@ -562,7 +641,7 @@ var CallForge = class _CallForge {
562
641
  const data = await this.fetchFromApi(locationId, sessionToken, params);
563
642
  this.saveToCache(locationId, data, params);
564
643
  }
565
- buildUrl(locationId, sessionToken, params) {
644
+ buildUrl(locationId, sessionToken, params, bootstrapToken) {
566
645
  const { categoryId, endpoint } = this.config;
567
646
  const queryParams = {
568
647
  categoryId
@@ -573,6 +652,9 @@ var CallForge = class _CallForge {
573
652
  if (sessionToken) {
574
653
  queryParams.sessionToken = sessionToken;
575
654
  }
655
+ if (bootstrapToken) {
656
+ queryParams.bootstrapToken = bootstrapToken;
657
+ }
576
658
  for (const [key, value] of Object.entries(params)) {
577
659
  if (value !== void 0) {
578
660
  queryParams[key] = value;
@@ -582,6 +664,10 @@ var CallForge = class _CallForge {
582
664
  const qs = sorted.map((k) => `${k}=${encodeURIComponent(queryParams[k])}`).join("&");
583
665
  return `${endpoint}/v1/tracking/session?${qs}`;
584
666
  }
667
+ buildBootstrapUrl() {
668
+ const { endpoint, categoryId } = this.config;
669
+ return `${endpoint}/v1/tracking/bootstrap?categoryId=${encodeURIComponent(categoryId)}`;
670
+ }
585
671
  buildLocationUrl(locationId) {
586
672
  const { endpoint } = this.config;
587
673
  if (!locationId) {
@@ -619,6 +705,7 @@ for(var i=0;i<ap.length;i++){var v=u.get(ap[i]);if(v)p[ap[i]]=v}
619
705
  var site='${config.siteKey || ""}'||location.hostname||'unknown-site';
620
706
  var key='cf_tracking_v1_'+site+'_${categoryId}';
621
707
  var lkey='cf_location_v1_'+site;
708
+ var bkey='cf_bootstrap_v1_'+site+'_${categoryId}';
622
709
  try{
623
710
  var cl=JSON.parse(localStorage.getItem(lkey));
624
711
  if(cl&&cl.expiresAt>Date.now()+30000){
@@ -638,12 +725,18 @@ token=(!loc||c.locId===loc)?c.sessionToken:null;
638
725
  var cp=c.params||{};
639
726
  p=Object.assign({},cp,p);
640
727
  }}catch(e){}
728
+ var bt=null;
729
+ try{
730
+ var cb=JSON.parse(localStorage.getItem(bkey));
731
+ if(cb&&typeof cb.token==='string'&&cb.expiresAt>Date.now()+10000)bt=cb.token;
732
+ }catch(e){}
733
+ var bp=bt?Promise.resolve({bootstrapToken:bt}):fetch('${endpoint}/v1/tracking/bootstrap?categoryId=${categoryId}',{credentials:'omit'}).then(function(r){if(!r.ok)return null;return r.json()}).then(function(b){if(!b||typeof b.bootstrapToken!=='string'||typeof b.expiresAt!=='number')return null;try{localStorage.setItem(bkey,JSON.stringify({token:b.bootstrapToken,expiresAt:b.expiresAt,tokenVersion:'b1'}))}catch(e){}return b}).catch(function(){return null});
641
734
  var url='${endpoint}/v1/tracking/session?categoryId=${categoryId}';
642
735
  if(loc)url+='&loc_physical_ms='+loc;
643
736
  if(token)url+='&sessionToken='+encodeURIComponent(token);
644
737
  var ks=Object.keys(p).sort();
645
738
  for(var j=0;j<ks.length;j++)url+='&'+ks[j]+'='+encodeURIComponent(p[ks[j]]);
646
- 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});
739
+ window.__cfTracking=bp.then(function(b){if(b&&b.bootstrapToken)url+='&bootstrapToken='+encodeURIComponent(b.bootstrapToken);return 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});
647
740
  })();`.replace(/\n/g, "");
648
741
  return `<link rel="preconnect" href="${endpoint}">
649
742
  <script>${script}</script>`;
package/dist/index.mjs CHANGED
@@ -179,9 +179,11 @@ var LocationCache = class {
179
179
  var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
180
180
  var FETCH_TIMEOUT_MS = 1e4;
181
181
  var CALL_INTENT_TIMEOUT_MS = 8e3;
182
+ var BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS = 1e4;
182
183
  var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid", "gad_campaignid", "gad_source"];
183
184
  var CallForge = class _CallForge {
184
185
  constructor(config) {
186
+ this.bootstrapMemoryCache = null;
185
187
  this.sessionPromise = null;
186
188
  this.locationPromise = null;
187
189
  this.customParams = {};
@@ -191,8 +193,10 @@ var CallForge = class _CallForge {
191
193
  ga4MeasurementId: config.ga4MeasurementId,
192
194
  siteKey: config.siteKey
193
195
  };
196
+ const resolvedSiteKey = config.siteKey || (typeof window !== "undefined" ? window.location.hostname : "unknown-site");
194
197
  this.cache = new TrackingCache(config.categoryId, config.siteKey);
195
198
  this.locationCache = new LocationCache(config.siteKey);
199
+ this.bootstrapCacheKey = `cf_bootstrap_v1_${resolvedSiteKey}_${config.categoryId}`;
196
200
  this.captureGA4ClientId();
197
201
  this.startGA4ClientIdPolling();
198
202
  }
@@ -440,7 +444,43 @@ var CallForge = class _CallForge {
440
444
  return params;
441
445
  }
442
446
  async fetchFromApi(locationId, sessionToken, params) {
443
- const url = this.buildUrl(locationId, sessionToken, params);
447
+ const controller = new AbortController();
448
+ const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
449
+ try {
450
+ let bootstrapToken = this.getCachedBootstrapToken();
451
+ let response = await fetch(
452
+ this.buildUrl(locationId, sessionToken, params, bootstrapToken),
453
+ {
454
+ credentials: "omit",
455
+ signal: controller.signal
456
+ }
457
+ );
458
+ if (response.status === 401) {
459
+ bootstrapToken = await this.getBootstrapToken(true);
460
+ if (bootstrapToken) {
461
+ response = await fetch(
462
+ this.buildUrl(locationId, sessionToken, params, bootstrapToken),
463
+ {
464
+ credentials: "omit",
465
+ signal: controller.signal
466
+ }
467
+ );
468
+ }
469
+ }
470
+ if (!response.ok) {
471
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
472
+ }
473
+ return await response.json();
474
+ } finally {
475
+ clearTimeout(timeoutId);
476
+ }
477
+ }
478
+ async getBootstrapToken(forceRefresh = false) {
479
+ const cached = this.getCachedBootstrapToken();
480
+ if (!forceRefresh && cached) {
481
+ return cached;
482
+ }
483
+ const url = this.buildBootstrapUrl();
444
484
  const controller = new AbortController();
445
485
  const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
446
486
  try {
@@ -449,9 +489,14 @@ var CallForge = class _CallForge {
449
489
  signal: controller.signal
450
490
  });
451
491
  if (!response.ok) {
452
- throw new Error(`API error: ${response.status} ${response.statusText}`);
492
+ return null;
453
493
  }
454
- return await response.json();
494
+ const data = await response.json();
495
+ if (typeof data.bootstrapToken !== "string" || typeof data.expiresAt !== "number") {
496
+ return null;
497
+ }
498
+ this.saveBootstrapToken(data.bootstrapToken, data.expiresAt);
499
+ return data.bootstrapToken;
455
500
  } finally {
456
501
  clearTimeout(timeoutId);
457
502
  }
@@ -473,6 +518,40 @@ var CallForge = class _CallForge {
473
518
  clearTimeout(timeoutId);
474
519
  }
475
520
  }
521
+ getCachedBootstrapToken() {
522
+ var _a;
523
+ const now = Date.now();
524
+ const fromMemory = this.bootstrapMemoryCache;
525
+ if (fromMemory && fromMemory.expiresAt - BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS > now) {
526
+ return fromMemory.token;
527
+ }
528
+ try {
529
+ const raw = localStorage.getItem(this.bootstrapCacheKey);
530
+ if (!raw) return null;
531
+ const cached = JSON.parse(raw);
532
+ if (typeof cached.token !== "string" || typeof cached.expiresAt !== "number") {
533
+ return null;
534
+ }
535
+ if (cached.expiresAt - BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS <= now) {
536
+ return null;
537
+ }
538
+ return cached.token;
539
+ } catch (e) {
540
+ return (_a = fromMemory == null ? void 0 : fromMemory.token) != null ? _a : null;
541
+ }
542
+ }
543
+ saveBootstrapToken(token, expiresAt) {
544
+ const cached = {
545
+ token,
546
+ expiresAt,
547
+ tokenVersion: "b1"
548
+ };
549
+ this.bootstrapMemoryCache = cached;
550
+ try {
551
+ localStorage.setItem(this.bootstrapCacheKey, JSON.stringify(cached));
552
+ } catch (e) {
553
+ }
554
+ }
476
555
  saveToCache(locationId, data, params) {
477
556
  const cached = {
478
557
  locId: locationId != null ? locationId : null,
@@ -538,7 +617,7 @@ var CallForge = class _CallForge {
538
617
  const data = await this.fetchFromApi(locationId, sessionToken, params);
539
618
  this.saveToCache(locationId, data, params);
540
619
  }
541
- buildUrl(locationId, sessionToken, params) {
620
+ buildUrl(locationId, sessionToken, params, bootstrapToken) {
542
621
  const { categoryId, endpoint } = this.config;
543
622
  const queryParams = {
544
623
  categoryId
@@ -549,6 +628,9 @@ var CallForge = class _CallForge {
549
628
  if (sessionToken) {
550
629
  queryParams.sessionToken = sessionToken;
551
630
  }
631
+ if (bootstrapToken) {
632
+ queryParams.bootstrapToken = bootstrapToken;
633
+ }
552
634
  for (const [key, value] of Object.entries(params)) {
553
635
  if (value !== void 0) {
554
636
  queryParams[key] = value;
@@ -558,6 +640,10 @@ var CallForge = class _CallForge {
558
640
  const qs = sorted.map((k) => `${k}=${encodeURIComponent(queryParams[k])}`).join("&");
559
641
  return `${endpoint}/v1/tracking/session?${qs}`;
560
642
  }
643
+ buildBootstrapUrl() {
644
+ const { endpoint, categoryId } = this.config;
645
+ return `${endpoint}/v1/tracking/bootstrap?categoryId=${encodeURIComponent(categoryId)}`;
646
+ }
561
647
  buildLocationUrl(locationId) {
562
648
  const { endpoint } = this.config;
563
649
  if (!locationId) {
@@ -595,6 +681,7 @@ for(var i=0;i<ap.length;i++){var v=u.get(ap[i]);if(v)p[ap[i]]=v}
595
681
  var site='${config.siteKey || ""}'||location.hostname||'unknown-site';
596
682
  var key='cf_tracking_v1_'+site+'_${categoryId}';
597
683
  var lkey='cf_location_v1_'+site;
684
+ var bkey='cf_bootstrap_v1_'+site+'_${categoryId}';
598
685
  try{
599
686
  var cl=JSON.parse(localStorage.getItem(lkey));
600
687
  if(cl&&cl.expiresAt>Date.now()+30000){
@@ -614,12 +701,18 @@ token=(!loc||c.locId===loc)?c.sessionToken:null;
614
701
  var cp=c.params||{};
615
702
  p=Object.assign({},cp,p);
616
703
  }}catch(e){}
704
+ var bt=null;
705
+ try{
706
+ var cb=JSON.parse(localStorage.getItem(bkey));
707
+ if(cb&&typeof cb.token==='string'&&cb.expiresAt>Date.now()+10000)bt=cb.token;
708
+ }catch(e){}
709
+ var bp=bt?Promise.resolve({bootstrapToken:bt}):fetch('${endpoint}/v1/tracking/bootstrap?categoryId=${categoryId}',{credentials:'omit'}).then(function(r){if(!r.ok)return null;return r.json()}).then(function(b){if(!b||typeof b.bootstrapToken!=='string'||typeof b.expiresAt!=='number')return null;try{localStorage.setItem(bkey,JSON.stringify({token:b.bootstrapToken,expiresAt:b.expiresAt,tokenVersion:'b1'}))}catch(e){}return b}).catch(function(){return null});
617
710
  var url='${endpoint}/v1/tracking/session?categoryId=${categoryId}';
618
711
  if(loc)url+='&loc_physical_ms='+loc;
619
712
  if(token)url+='&sessionToken='+encodeURIComponent(token);
620
713
  var ks=Object.keys(p).sort();
621
714
  for(var j=0;j<ks.length;j++)url+='&'+ks[j]+'='+encodeURIComponent(p[ks[j]]);
622
- 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});
715
+ window.__cfTracking=bp.then(function(b){if(b&&b.bootstrapToken)url+='&bootstrapToken='+encodeURIComponent(b.bootstrapToken);return 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});
623
716
  })();`.replace(/\n/g, "");
624
717
  return `<link rel="preconnect" href="${endpoint}">
625
718
  <script>${script}</script>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@callforge/tracking-client",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",