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