@callforge/tracking-client 0.10.0 → 0.10.1
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 +35 -35
- package/dist/index.d.mts +24 -24
- package/dist/index.d.ts +24 -24
- package/dist/index.js +52 -59
- package/dist/index.mjs +52 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,17 +19,18 @@ Client integration requirements:
|
|
|
19
19
|
- Keep handling `phoneNumber` and `leaseId` separately. A request can return a phone number with `leaseId: null` when lease assignment is intentionally suppressed.
|
|
20
20
|
- For attribution/scale metrics, treat `leaseId` as the source of truth for lease-backed traffic.
|
|
21
21
|
|
|
22
|
-
## Realtime Data Layer Upgrade (v0.
|
|
22
|
+
## Realtime Data Layer Upgrade (v0.9+)
|
|
23
23
|
|
|
24
24
|
This release adds explicit browser helpers for the new realtime layer:
|
|
25
|
-
- `client.linkPhoneCall({ phoneNumber
|
|
25
|
+
- `client.linkPhoneCall({ phoneNumber })` for strict web-click to phone-call linkage.
|
|
26
|
+
- `client.setWebZip(zipCode, { source })` for immediate web ZIP availability to call processing.
|
|
26
27
|
|
|
27
28
|
Integration checklist:
|
|
28
29
|
- Call `linkPhoneCall` immediately before opening a `tel:` link.
|
|
29
30
|
- Pass the exact dialed number string (`+1...`) used by the link.
|
|
30
31
|
- Continue dialing even if `linkPhoneCall` fails (best-effort attribution assist, not UX-blocking).
|
|
31
|
-
-
|
|
32
|
-
-
|
|
32
|
+
- Call `setWebZip` when the visitor selects or types a ZIP.
|
|
33
|
+
- Keep `setParams` for broader attribution params; use `setWebZip` for fast ZIP propagation.
|
|
33
34
|
|
|
34
35
|
## Quick Start
|
|
35
36
|
|
|
@@ -96,40 +97,32 @@ Notes:
|
|
|
96
97
|
- `callIntentToken` is short-lived and single-use.
|
|
97
98
|
- Treat it as an opaque secret (do not log it).
|
|
98
99
|
|
|
99
|
-
### 4. Realtime call-link
|
|
100
|
+
### 4. Realtime call-link + web ZIP helpers (recommended)
|
|
100
101
|
|
|
101
|
-
Use
|
|
102
|
+
Use explicit helpers to write realtime data from the browser.
|
|
102
103
|
|
|
103
104
|
```typescript
|
|
104
105
|
const client = CallForge.init({ categoryId: 'your-category-id' });
|
|
105
106
|
|
|
106
|
-
async function dialTrackedNumber(phoneNumber: string
|
|
107
|
+
async function dialTrackedNumber(phoneNumber: string) {
|
|
107
108
|
try {
|
|
108
109
|
// Default TTL is 30s (clamped server-side to 5-120s).
|
|
109
|
-
await client.linkPhoneCall({
|
|
110
|
-
phoneNumber,
|
|
111
|
-
realtime: webZip
|
|
112
|
-
? {
|
|
113
|
-
webZip,
|
|
114
|
-
webZipSource: 'manual', // or 'suggested'
|
|
115
|
-
params: {
|
|
116
|
-
zipChoiceMethod: 'picker',
|
|
117
|
-
},
|
|
118
|
-
}
|
|
119
|
-
: undefined,
|
|
120
|
-
});
|
|
110
|
+
await client.linkPhoneCall({ phoneNumber });
|
|
121
111
|
} finally {
|
|
122
112
|
// Do not block dialing on telemetry failure.
|
|
123
113
|
window.location.href = `tel:${phoneNumber}`;
|
|
124
114
|
}
|
|
125
115
|
}
|
|
116
|
+
|
|
117
|
+
// Example ZIP picker handler
|
|
118
|
+
await client.setWebZip('30309', { source: 'manual' });
|
|
126
119
|
```
|
|
127
120
|
|
|
128
121
|
Notes:
|
|
129
122
|
- `linkPhoneCall` is optimized for mobile tap-to-call timing.
|
|
130
|
-
- `
|
|
131
|
-
- `
|
|
132
|
-
- `
|
|
123
|
+
- `setWebZip` accepts `manual` (typed) and `suggested` (picked from options).
|
|
124
|
+
- `setWebZip` validates ZIP format (`12345`) client-side before sending.
|
|
125
|
+
- `setWebZip` also sends `webZip` as a custom session param in a best-effort sync backup path.
|
|
133
126
|
|
|
134
127
|
### 5. GA4 Integration
|
|
135
128
|
|
|
@@ -263,19 +256,12 @@ console.log(intent.callIntentToken);
|
|
|
263
256
|
|
|
264
257
|
### `client.linkPhoneCall(input)`
|
|
265
258
|
|
|
266
|
-
Create a short-lived realtime call-link intent keyed by dialed number
|
|
259
|
+
Create a short-lived realtime call-link intent keyed by dialed number.
|
|
267
260
|
|
|
268
261
|
```typescript
|
|
269
262
|
const result = await client.linkPhoneCall({
|
|
270
263
|
phoneNumber: '+13105551234',
|
|
271
264
|
ttlSeconds: 30, // Optional, default 30s
|
|
272
|
-
realtime: {
|
|
273
|
-
webZip: '30309', // Optional
|
|
274
|
-
webZipSource: 'manual', // Optional, defaults to 'manual' when webZip is provided
|
|
275
|
-
params: { // Optional
|
|
276
|
-
zipChoiceMethod: 'picker',
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
265
|
});
|
|
280
266
|
|
|
281
267
|
console.log(result.status); // 'ready'
|
|
@@ -283,10 +269,25 @@ console.log(result.status); // 'ready'
|
|
|
283
269
|
|
|
284
270
|
Use this right before `tel:` navigation so inbound call handling can perform strict 1:1 consume.
|
|
285
271
|
|
|
272
|
+
### `client.setWebZip(zipCode, options?)`
|
|
273
|
+
|
|
274
|
+
Store visitor-selected ZIP in the realtime profile layer for immediate call-side enrichment.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const result = await client.setWebZip('30309', {
|
|
278
|
+
source: 'manual', // or 'suggested'
|
|
279
|
+
ttlSeconds: 3600, // Optional
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (result.status === 'ready') {
|
|
283
|
+
console.log(result.profile.webZipCode);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
286
287
|
Behavior:
|
|
287
|
-
- Rejects invalid
|
|
288
|
-
-
|
|
289
|
-
-
|
|
288
|
+
- Rejects invalid ZIP values unless they are 5 digits.
|
|
289
|
+
- Writes profile data keyed by `sessionId`.
|
|
290
|
+
- Also mirrors `webZip` and `webZipSource` into custom params and triggers a best-effort session sync backup.
|
|
290
291
|
|
|
291
292
|
### `client.onReady(callback)`
|
|
292
293
|
|
|
@@ -400,8 +401,7 @@ import type {
|
|
|
400
401
|
LocationReadyCallback,
|
|
401
402
|
CallIntentResponse,
|
|
402
403
|
CallLinkIntentResponse,
|
|
403
|
-
|
|
404
|
-
RealtimeLinkPayload,
|
|
404
|
+
RealtimeProfileResponse,
|
|
405
405
|
RealtimeProfileSource,
|
|
406
406
|
} from '@callforge/tracking-client';
|
|
407
407
|
```
|
package/dist/index.d.mts
CHANGED
|
@@ -92,26 +92,18 @@ interface CallLinkIntentResponse {
|
|
|
92
92
|
expiresAt: string;
|
|
93
93
|
}
|
|
94
94
|
type RealtimeProfileSource = 'manual' | 'suggested';
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*/
|
|
108
|
-
params?: Record<string, string>;
|
|
109
|
-
}
|
|
110
|
-
interface LinkPhoneCallInput {
|
|
111
|
-
phoneNumber: string;
|
|
112
|
-
ttlSeconds?: number;
|
|
113
|
-
realtime?: RealtimeLinkPayload;
|
|
114
|
-
}
|
|
95
|
+
type RealtimeProfileResponse = {
|
|
96
|
+
status: 'ready';
|
|
97
|
+
profile: {
|
|
98
|
+
sessionId: string;
|
|
99
|
+
webZipCode: string;
|
|
100
|
+
webZipSource: RealtimeProfileSource;
|
|
101
|
+
webZipUpdatedAt: string;
|
|
102
|
+
expiresAt: string;
|
|
103
|
+
};
|
|
104
|
+
} | {
|
|
105
|
+
status: 'unavailable';
|
|
106
|
+
};
|
|
115
107
|
/**
|
|
116
108
|
* Callback function for onReady subscription.
|
|
117
109
|
*/
|
|
@@ -168,9 +160,17 @@ declare class CallForge {
|
|
|
168
160
|
* Create a short-lived realtime call-link intent for a specific dialed number.
|
|
169
161
|
* Use this immediately before opening a `tel:` link.
|
|
170
162
|
*/
|
|
171
|
-
linkPhoneCall(input:
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
linkPhoneCall(input: {
|
|
164
|
+
phoneNumber: string;
|
|
165
|
+
ttlSeconds?: number;
|
|
166
|
+
}): Promise<CallLinkIntentResponse>;
|
|
167
|
+
/**
|
|
168
|
+
* Write visitor-selected web ZIP to the realtime data layer.
|
|
169
|
+
*/
|
|
170
|
+
setWebZip(zipCode: string, options?: {
|
|
171
|
+
source?: RealtimeProfileSource;
|
|
172
|
+
ttlSeconds?: number;
|
|
173
|
+
}): Promise<RealtimeProfileResponse>;
|
|
174
174
|
/**
|
|
175
175
|
* Subscribe to session ready event.
|
|
176
176
|
* Callback is called once session data is available.
|
|
@@ -234,4 +234,4 @@ declare class CallForge {
|
|
|
234
234
|
*/
|
|
235
235
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
236
236
|
|
|
237
|
-
export { CallForge, type CallForgeConfig, type CallIntentResponse, type CallLinkIntentResponse, type
|
|
237
|
+
export { CallForge, type CallForgeConfig, type CallIntentResponse, type CallLinkIntentResponse, type LocationReadyCallback, type ReadyCallback, type RealtimeProfileResponse, type RealtimeProfileSource, type TrackingLocation, type TrackingLocationSource, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
package/dist/index.d.ts
CHANGED
|
@@ -92,26 +92,18 @@ interface CallLinkIntentResponse {
|
|
|
92
92
|
expiresAt: string;
|
|
93
93
|
}
|
|
94
94
|
type RealtimeProfileSource = 'manual' | 'suggested';
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*/
|
|
108
|
-
params?: Record<string, string>;
|
|
109
|
-
}
|
|
110
|
-
interface LinkPhoneCallInput {
|
|
111
|
-
phoneNumber: string;
|
|
112
|
-
ttlSeconds?: number;
|
|
113
|
-
realtime?: RealtimeLinkPayload;
|
|
114
|
-
}
|
|
95
|
+
type RealtimeProfileResponse = {
|
|
96
|
+
status: 'ready';
|
|
97
|
+
profile: {
|
|
98
|
+
sessionId: string;
|
|
99
|
+
webZipCode: string;
|
|
100
|
+
webZipSource: RealtimeProfileSource;
|
|
101
|
+
webZipUpdatedAt: string;
|
|
102
|
+
expiresAt: string;
|
|
103
|
+
};
|
|
104
|
+
} | {
|
|
105
|
+
status: 'unavailable';
|
|
106
|
+
};
|
|
115
107
|
/**
|
|
116
108
|
* Callback function for onReady subscription.
|
|
117
109
|
*/
|
|
@@ -168,9 +160,17 @@ declare class CallForge {
|
|
|
168
160
|
* Create a short-lived realtime call-link intent for a specific dialed number.
|
|
169
161
|
* Use this immediately before opening a `tel:` link.
|
|
170
162
|
*/
|
|
171
|
-
linkPhoneCall(input:
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
linkPhoneCall(input: {
|
|
164
|
+
phoneNumber: string;
|
|
165
|
+
ttlSeconds?: number;
|
|
166
|
+
}): Promise<CallLinkIntentResponse>;
|
|
167
|
+
/**
|
|
168
|
+
* Write visitor-selected web ZIP to the realtime data layer.
|
|
169
|
+
*/
|
|
170
|
+
setWebZip(zipCode: string, options?: {
|
|
171
|
+
source?: RealtimeProfileSource;
|
|
172
|
+
ttlSeconds?: number;
|
|
173
|
+
}): Promise<RealtimeProfileResponse>;
|
|
174
174
|
/**
|
|
175
175
|
* Subscribe to session ready event.
|
|
176
176
|
* Callback is called once session data is available.
|
|
@@ -234,4 +234,4 @@ declare class CallForge {
|
|
|
234
234
|
*/
|
|
235
235
|
declare function getPreloadSnippet(config: CallForgeConfig): string;
|
|
236
236
|
|
|
237
|
-
export { CallForge, type CallForgeConfig, type CallIntentResponse, type CallLinkIntentResponse, type
|
|
237
|
+
export { CallForge, type CallForgeConfig, type CallIntentResponse, type CallLinkIntentResponse, type LocationReadyCallback, type ReadyCallback, type RealtimeProfileResponse, type RealtimeProfileSource, type TrackingLocation, type TrackingLocationSource, type TrackingParams, type TrackingSession, getPreloadSnippet };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
7
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
@@ -17,6 +19,7 @@ var __spreadValues = (a, b) => {
|
|
|
17
19
|
}
|
|
18
20
|
return a;
|
|
19
21
|
};
|
|
22
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
23
|
var __export = (target, all) => {
|
|
21
24
|
for (var name in all)
|
|
22
25
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -204,6 +207,7 @@ var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
|
|
|
204
207
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
205
208
|
var CALL_INTENT_TIMEOUT_MS = 8e3;
|
|
206
209
|
var REALTIME_CALL_LINK_TIMEOUT_MS = 5e3;
|
|
210
|
+
var REALTIME_PROFILE_TIMEOUT_MS = 5e3;
|
|
207
211
|
var BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS = 1e4;
|
|
208
212
|
var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid", "gad_campaignid", "gad_source"];
|
|
209
213
|
var ZIP_CODE_PATTERN = /^\d{5}$/;
|
|
@@ -297,11 +301,6 @@ var CallForge = class _CallForge {
|
|
|
297
301
|
*/
|
|
298
302
|
async linkPhoneCall(input) {
|
|
299
303
|
const session = await this.getSession();
|
|
300
|
-
const realtime = this.normalizeRealtimeLinkPayload(input.realtime);
|
|
301
|
-
const fallbackParams = this.buildRealtimeFallbackParams(realtime);
|
|
302
|
-
if (Object.keys(fallbackParams).length > 0) {
|
|
303
|
-
void this.setParams(fallbackParams);
|
|
304
|
-
}
|
|
305
304
|
const controller = new AbortController();
|
|
306
305
|
const timeoutId = setTimeout(() => controller.abort(), REALTIME_CALL_LINK_TIMEOUT_MS);
|
|
307
306
|
try {
|
|
@@ -312,11 +311,11 @@ var CallForge = class _CallForge {
|
|
|
312
311
|
},
|
|
313
312
|
credentials: "omit",
|
|
314
313
|
signal: controller.signal,
|
|
315
|
-
body: JSON.stringify(
|
|
314
|
+
body: JSON.stringify({
|
|
316
315
|
sessionToken: session.sessionToken,
|
|
317
316
|
phoneNumber: input.phoneNumber,
|
|
318
317
|
ttlSeconds: input.ttlSeconds
|
|
319
|
-
}
|
|
318
|
+
})
|
|
320
319
|
});
|
|
321
320
|
if (!response.ok) {
|
|
322
321
|
throw new Error(`Realtime call-link API error: ${response.status} ${response.statusText}`);
|
|
@@ -326,59 +325,52 @@ var CallForge = class _CallForge {
|
|
|
326
325
|
clearTimeout(timeoutId);
|
|
327
326
|
}
|
|
328
327
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
throw new Error("Invalid realtime.webZip. Expected a 5-digit ZIP.");
|
|
338
|
-
}
|
|
339
|
-
normalized.webZip = normalizedZip;
|
|
340
|
-
}
|
|
341
|
-
if (typeof realtime.webZipSource !== "undefined") {
|
|
342
|
-
if (realtime.webZipSource !== "manual" && realtime.webZipSource !== "suggested") {
|
|
343
|
-
throw new Error('Invalid realtime.webZipSource. Expected "manual" or "suggested".');
|
|
344
|
-
}
|
|
345
|
-
normalized.webZipSource = realtime.webZipSource;
|
|
346
|
-
}
|
|
347
|
-
if (normalized.webZipSource && !normalized.webZip) {
|
|
348
|
-
throw new Error("realtime.webZipSource requires realtime.webZip.");
|
|
328
|
+
/**
|
|
329
|
+
* Write visitor-selected web ZIP to the realtime data layer.
|
|
330
|
+
*/
|
|
331
|
+
async setWebZip(zipCode, options) {
|
|
332
|
+
var _a;
|
|
333
|
+
const normalizedZip = zipCode.trim();
|
|
334
|
+
if (!ZIP_CODE_PATTERN.test(normalizedZip)) {
|
|
335
|
+
throw new Error("Invalid ZIP code. Expected a 5-digit ZIP.");
|
|
349
336
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
337
|
+
const session = await this.getSession();
|
|
338
|
+
const fallbackSource = (_a = options == null ? void 0 : options.source) != null ? _a : "manual";
|
|
339
|
+
void this.setParams({
|
|
340
|
+
webZip: normalizedZip,
|
|
341
|
+
webZipSource: fallbackSource
|
|
342
|
+
});
|
|
343
|
+
const controller = new AbortController();
|
|
344
|
+
const timeoutId = setTimeout(() => controller.abort(), REALTIME_PROFILE_TIMEOUT_MS);
|
|
345
|
+
try {
|
|
346
|
+
const response = await fetch(`${this.config.endpoint}/v1/tracking/realtime-profile`, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
headers: {
|
|
349
|
+
"Content-Type": "application/json"
|
|
350
|
+
},
|
|
351
|
+
credentials: "omit",
|
|
352
|
+
signal: controller.signal,
|
|
353
|
+
body: JSON.stringify({
|
|
354
|
+
sessionToken: session.sessionToken,
|
|
355
|
+
webZip: normalizedZip,
|
|
356
|
+
source: options == null ? void 0 : options.source,
|
|
357
|
+
ttlSeconds: options == null ? void 0 : options.ttlSeconds
|
|
358
|
+
})
|
|
359
|
+
});
|
|
360
|
+
if (!response.ok) {
|
|
361
|
+
throw new Error(`Realtime profile API error: ${response.status} ${response.statusText}`);
|
|
360
362
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
+
const result = await response.json();
|
|
364
|
+
if (result.status === "ready") {
|
|
365
|
+
this.customParams = __spreadProps(__spreadValues({}, this.customParams), {
|
|
366
|
+
webZip: result.profile.webZipCode,
|
|
367
|
+
webZipSource: result.profile.webZipSource
|
|
368
|
+
});
|
|
363
369
|
}
|
|
370
|
+
return result;
|
|
371
|
+
} finally {
|
|
372
|
+
clearTimeout(timeoutId);
|
|
364
373
|
}
|
|
365
|
-
if (normalized.webZip && !normalized.webZipSource) {
|
|
366
|
-
normalized.webZipSource = "manual";
|
|
367
|
-
}
|
|
368
|
-
if (typeof normalized.webZip === "undefined" && typeof normalized.webZipSource === "undefined" && typeof normalized.params === "undefined") {
|
|
369
|
-
return void 0;
|
|
370
|
-
}
|
|
371
|
-
return normalized;
|
|
372
|
-
}
|
|
373
|
-
buildRealtimeFallbackParams(realtime) {
|
|
374
|
-
var _a, _b;
|
|
375
|
-
if (!realtime) {
|
|
376
|
-
return {};
|
|
377
|
-
}
|
|
378
|
-
return __spreadValues(__spreadValues({}, (_a = realtime.params) != null ? _a : {}), realtime.webZip ? {
|
|
379
|
-
webZip: realtime.webZip,
|
|
380
|
-
webZipSource: (_b = realtime.webZipSource) != null ? _b : "manual"
|
|
381
|
-
} : {});
|
|
382
374
|
}
|
|
383
375
|
/**
|
|
384
376
|
* Subscribe to session ready event.
|
|
@@ -519,14 +511,15 @@ var CallForge = class _CallForge {
|
|
|
519
511
|
return this.formatApiResponse(data);
|
|
520
512
|
}
|
|
521
513
|
async fetchLocation() {
|
|
522
|
-
var _a, _b;
|
|
514
|
+
var _a, _b, _c, _d;
|
|
523
515
|
const locationId = this.getLocationId();
|
|
524
516
|
if (typeof window !== "undefined" && window.__cfTrackingLocation) {
|
|
525
517
|
try {
|
|
526
518
|
const data2 = await window.__cfTrackingLocation;
|
|
527
519
|
const dataWithExtras = data2;
|
|
528
520
|
const effectiveLocId = (_a = dataWithExtras.locId) != null ? _a : locationId;
|
|
529
|
-
const
|
|
521
|
+
const zipOptions = (_c = dataWithExtras.zipOptions) != null ? _c : (_b = data2.location) == null ? void 0 : _b.zipOptions;
|
|
522
|
+
const location2 = this.toTrackingLocation(data2.location, zipOptions);
|
|
530
523
|
this.saveLocationToCache(effectiveLocId, location2, data2.expiresAt);
|
|
531
524
|
return location2;
|
|
532
525
|
} catch (e) {
|
|
@@ -534,7 +527,7 @@ var CallForge = class _CallForge {
|
|
|
534
527
|
}
|
|
535
528
|
const cached = this.locationCache.get(locationId);
|
|
536
529
|
if (cached) {
|
|
537
|
-
return this.toTrackingLocation(cached.location, (
|
|
530
|
+
return this.toTrackingLocation(cached.location, (_d = cached.location) == null ? void 0 : _d.zipOptions);
|
|
538
531
|
}
|
|
539
532
|
const data = await this.fetchLocationFromApi(locationId);
|
|
540
533
|
const location = this.toTrackingLocation(data.location, data.zipOptions);
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
2
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
6
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -14,6 +16,7 @@ var __spreadValues = (a, b) => {
|
|
|
14
16
|
}
|
|
15
17
|
return a;
|
|
16
18
|
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
17
20
|
|
|
18
21
|
// src/cache.ts
|
|
19
22
|
var EXPIRY_BUFFER_MS = 3e4;
|
|
@@ -180,6 +183,7 @@ var DEFAULT_ENDPOINT = "https://tracking.callforge.io";
|
|
|
180
183
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
181
184
|
var CALL_INTENT_TIMEOUT_MS = 8e3;
|
|
182
185
|
var REALTIME_CALL_LINK_TIMEOUT_MS = 5e3;
|
|
186
|
+
var REALTIME_PROFILE_TIMEOUT_MS = 5e3;
|
|
183
187
|
var BOOTSTRAP_TOKEN_EXPIRY_BUFFER_MS = 1e4;
|
|
184
188
|
var AUTO_PARAMS = ["gclid", "gbraid", "wbraid", "msclkid", "fbclid", "gad_campaignid", "gad_source"];
|
|
185
189
|
var ZIP_CODE_PATTERN = /^\d{5}$/;
|
|
@@ -273,11 +277,6 @@ var CallForge = class _CallForge {
|
|
|
273
277
|
*/
|
|
274
278
|
async linkPhoneCall(input) {
|
|
275
279
|
const session = await this.getSession();
|
|
276
|
-
const realtime = this.normalizeRealtimeLinkPayload(input.realtime);
|
|
277
|
-
const fallbackParams = this.buildRealtimeFallbackParams(realtime);
|
|
278
|
-
if (Object.keys(fallbackParams).length > 0) {
|
|
279
|
-
void this.setParams(fallbackParams);
|
|
280
|
-
}
|
|
281
280
|
const controller = new AbortController();
|
|
282
281
|
const timeoutId = setTimeout(() => controller.abort(), REALTIME_CALL_LINK_TIMEOUT_MS);
|
|
283
282
|
try {
|
|
@@ -288,11 +287,11 @@ var CallForge = class _CallForge {
|
|
|
288
287
|
},
|
|
289
288
|
credentials: "omit",
|
|
290
289
|
signal: controller.signal,
|
|
291
|
-
body: JSON.stringify(
|
|
290
|
+
body: JSON.stringify({
|
|
292
291
|
sessionToken: session.sessionToken,
|
|
293
292
|
phoneNumber: input.phoneNumber,
|
|
294
293
|
ttlSeconds: input.ttlSeconds
|
|
295
|
-
}
|
|
294
|
+
})
|
|
296
295
|
});
|
|
297
296
|
if (!response.ok) {
|
|
298
297
|
throw new Error(`Realtime call-link API error: ${response.status} ${response.statusText}`);
|
|
@@ -302,59 +301,52 @@ var CallForge = class _CallForge {
|
|
|
302
301
|
clearTimeout(timeoutId);
|
|
303
302
|
}
|
|
304
303
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
throw new Error("Invalid realtime.webZip. Expected a 5-digit ZIP.");
|
|
314
|
-
}
|
|
315
|
-
normalized.webZip = normalizedZip;
|
|
316
|
-
}
|
|
317
|
-
if (typeof realtime.webZipSource !== "undefined") {
|
|
318
|
-
if (realtime.webZipSource !== "manual" && realtime.webZipSource !== "suggested") {
|
|
319
|
-
throw new Error('Invalid realtime.webZipSource. Expected "manual" or "suggested".');
|
|
320
|
-
}
|
|
321
|
-
normalized.webZipSource = realtime.webZipSource;
|
|
322
|
-
}
|
|
323
|
-
if (normalized.webZipSource && !normalized.webZip) {
|
|
324
|
-
throw new Error("realtime.webZipSource requires realtime.webZip.");
|
|
304
|
+
/**
|
|
305
|
+
* Write visitor-selected web ZIP to the realtime data layer.
|
|
306
|
+
*/
|
|
307
|
+
async setWebZip(zipCode, options) {
|
|
308
|
+
var _a;
|
|
309
|
+
const normalizedZip = zipCode.trim();
|
|
310
|
+
if (!ZIP_CODE_PATTERN.test(normalizedZip)) {
|
|
311
|
+
throw new Error("Invalid ZIP code. Expected a 5-digit ZIP.");
|
|
325
312
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
313
|
+
const session = await this.getSession();
|
|
314
|
+
const fallbackSource = (_a = options == null ? void 0 : options.source) != null ? _a : "manual";
|
|
315
|
+
void this.setParams({
|
|
316
|
+
webZip: normalizedZip,
|
|
317
|
+
webZipSource: fallbackSource
|
|
318
|
+
});
|
|
319
|
+
const controller = new AbortController();
|
|
320
|
+
const timeoutId = setTimeout(() => controller.abort(), REALTIME_PROFILE_TIMEOUT_MS);
|
|
321
|
+
try {
|
|
322
|
+
const response = await fetch(`${this.config.endpoint}/v1/tracking/realtime-profile`, {
|
|
323
|
+
method: "POST",
|
|
324
|
+
headers: {
|
|
325
|
+
"Content-Type": "application/json"
|
|
326
|
+
},
|
|
327
|
+
credentials: "omit",
|
|
328
|
+
signal: controller.signal,
|
|
329
|
+
body: JSON.stringify({
|
|
330
|
+
sessionToken: session.sessionToken,
|
|
331
|
+
webZip: normalizedZip,
|
|
332
|
+
source: options == null ? void 0 : options.source,
|
|
333
|
+
ttlSeconds: options == null ? void 0 : options.ttlSeconds
|
|
334
|
+
})
|
|
335
|
+
});
|
|
336
|
+
if (!response.ok) {
|
|
337
|
+
throw new Error(`Realtime profile API error: ${response.status} ${response.statusText}`);
|
|
336
338
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
+
const result = await response.json();
|
|
340
|
+
if (result.status === "ready") {
|
|
341
|
+
this.customParams = __spreadProps(__spreadValues({}, this.customParams), {
|
|
342
|
+
webZip: result.profile.webZipCode,
|
|
343
|
+
webZipSource: result.profile.webZipSource
|
|
344
|
+
});
|
|
339
345
|
}
|
|
346
|
+
return result;
|
|
347
|
+
} finally {
|
|
348
|
+
clearTimeout(timeoutId);
|
|
340
349
|
}
|
|
341
|
-
if (normalized.webZip && !normalized.webZipSource) {
|
|
342
|
-
normalized.webZipSource = "manual";
|
|
343
|
-
}
|
|
344
|
-
if (typeof normalized.webZip === "undefined" && typeof normalized.webZipSource === "undefined" && typeof normalized.params === "undefined") {
|
|
345
|
-
return void 0;
|
|
346
|
-
}
|
|
347
|
-
return normalized;
|
|
348
|
-
}
|
|
349
|
-
buildRealtimeFallbackParams(realtime) {
|
|
350
|
-
var _a, _b;
|
|
351
|
-
if (!realtime) {
|
|
352
|
-
return {};
|
|
353
|
-
}
|
|
354
|
-
return __spreadValues(__spreadValues({}, (_a = realtime.params) != null ? _a : {}), realtime.webZip ? {
|
|
355
|
-
webZip: realtime.webZip,
|
|
356
|
-
webZipSource: (_b = realtime.webZipSource) != null ? _b : "manual"
|
|
357
|
-
} : {});
|
|
358
350
|
}
|
|
359
351
|
/**
|
|
360
352
|
* Subscribe to session ready event.
|
|
@@ -495,14 +487,15 @@ var CallForge = class _CallForge {
|
|
|
495
487
|
return this.formatApiResponse(data);
|
|
496
488
|
}
|
|
497
489
|
async fetchLocation() {
|
|
498
|
-
var _a, _b;
|
|
490
|
+
var _a, _b, _c, _d;
|
|
499
491
|
const locationId = this.getLocationId();
|
|
500
492
|
if (typeof window !== "undefined" && window.__cfTrackingLocation) {
|
|
501
493
|
try {
|
|
502
494
|
const data2 = await window.__cfTrackingLocation;
|
|
503
495
|
const dataWithExtras = data2;
|
|
504
496
|
const effectiveLocId = (_a = dataWithExtras.locId) != null ? _a : locationId;
|
|
505
|
-
const
|
|
497
|
+
const zipOptions = (_c = dataWithExtras.zipOptions) != null ? _c : (_b = data2.location) == null ? void 0 : _b.zipOptions;
|
|
498
|
+
const location2 = this.toTrackingLocation(data2.location, zipOptions);
|
|
506
499
|
this.saveLocationToCache(effectiveLocId, location2, data2.expiresAt);
|
|
507
500
|
return location2;
|
|
508
501
|
} catch (e) {
|
|
@@ -510,7 +503,7 @@ var CallForge = class _CallForge {
|
|
|
510
503
|
}
|
|
511
504
|
const cached = this.locationCache.get(locationId);
|
|
512
505
|
if (cached) {
|
|
513
|
-
return this.toTrackingLocation(cached.location, (
|
|
506
|
+
return this.toTrackingLocation(cached.location, (_d = cached.location) == null ? void 0 : _d.zipOptions);
|
|
514
507
|
}
|
|
515
508
|
const data = await this.fetchLocationFromApi(locationId);
|
|
516
509
|
const location = this.toTrackingLocation(data.location, data.zipOptions);
|