@morgan-and-morgan/intake-agent 1.1.2 → 1.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/dist/mm-intake-widget.js +92 -0
- package/package.json +2 -1
package/dist/mm-intake-widget.js
CHANGED
|
@@ -11,6 +11,92 @@
|
|
|
11
11
|
chatUrl
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
+
const ATTRIBUTION_FIELD_MAX_LENGTH = {
|
|
15
|
+
tracked_landing_page: 4096,
|
|
16
|
+
tracked_landing_timestamp: 255,
|
|
17
|
+
conversion_page: 255,
|
|
18
|
+
ga_client_id: 255,
|
|
19
|
+
gclid_value: 255,
|
|
20
|
+
marketing_source: 255,
|
|
21
|
+
marketing_sub_source: 255
|
|
22
|
+
};
|
|
23
|
+
const ATTRIBUTION_FIELDS = Object.keys(ATTRIBUTION_FIELD_MAX_LENGTH);
|
|
24
|
+
const sanitizeIntakeChatAttribution = (value) => {
|
|
25
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const record = value;
|
|
29
|
+
const sanitized = {};
|
|
30
|
+
for (const field of ATTRIBUTION_FIELDS) {
|
|
31
|
+
const fieldValue = record[field];
|
|
32
|
+
const trimmed = typeof fieldValue === "string" ? fieldValue.trim() : "";
|
|
33
|
+
if (trimmed !== "") {
|
|
34
|
+
sanitized[field] = trimmed.slice(0, ATTRIBUTION_FIELD_MAX_LENGTH[field]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return Object.keys(sanitized).length === 0 ? null : sanitized;
|
|
38
|
+
};
|
|
39
|
+
const LANDING_PAGE_COOKIE = "tracked_landing_page";
|
|
40
|
+
const LANDING_TIMESTAMP_COOKIE = "tracked_landing_timestamp";
|
|
41
|
+
const readTimestampCookie = (cookie) => {
|
|
42
|
+
const raw = readCookie(cookie, LANDING_TIMESTAMP_COOKIE);
|
|
43
|
+
return raw === void 0 ? void 0 : decodeCookieValue(raw);
|
|
44
|
+
};
|
|
45
|
+
const readCookie = (cookie, name) => {
|
|
46
|
+
var _a;
|
|
47
|
+
const raw = (_a = cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith(`${name}=`))) == null ? void 0 : _a.slice(name.length + 1);
|
|
48
|
+
return raw === void 0 || raw === "" ? void 0 : raw;
|
|
49
|
+
};
|
|
50
|
+
const decodeCookieValue = (raw) => {
|
|
51
|
+
try {
|
|
52
|
+
return decodeURIComponent(raw);
|
|
53
|
+
} catch {
|
|
54
|
+
return raw;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const parseUrl = (value) => {
|
|
58
|
+
try {
|
|
59
|
+
return new URL(value);
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const readLandingPage = (cookie) => {
|
|
65
|
+
const raw = readCookie(cookie, LANDING_PAGE_COOKIE);
|
|
66
|
+
if (raw === void 0 || parseUrl(raw) !== null) {
|
|
67
|
+
return raw;
|
|
68
|
+
}
|
|
69
|
+
const decoded = decodeCookieValue(raw);
|
|
70
|
+
return parseUrl(decoded) === null ? raw : decoded;
|
|
71
|
+
};
|
|
72
|
+
const readGaClientId = (cookie) => {
|
|
73
|
+
const raw = readCookie(cookie, "_ga");
|
|
74
|
+
if (raw === void 0) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
const value = decodeCookieValue(raw);
|
|
78
|
+
const parts = value.split(".");
|
|
79
|
+
return parts.length >= 4 ? parts.slice(-2).join(".") : value;
|
|
80
|
+
};
|
|
81
|
+
const readGclid = (landingUrl) => {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
return landingUrl === void 0 ? void 0 : (_b = (_a = parseUrl(landingUrl)) == null ? void 0 : _a.searchParams.get("gclid")) != null ? _b : void 0;
|
|
84
|
+
};
|
|
85
|
+
const readPathname = (url) => {
|
|
86
|
+
var _a;
|
|
87
|
+
return (_a = parseUrl(url)) == null ? void 0 : _a.pathname;
|
|
88
|
+
};
|
|
89
|
+
const buildHostAttribution = (currentUrl = globalThis.location.href, cookie = document.cookie) => {
|
|
90
|
+
var _a;
|
|
91
|
+
const landingPage = readLandingPage(cookie);
|
|
92
|
+
return (_a = sanitizeIntakeChatAttribution({
|
|
93
|
+
tracked_landing_page: landingPage,
|
|
94
|
+
tracked_landing_timestamp: readTimestampCookie(cookie),
|
|
95
|
+
conversion_page: readPathname(currentUrl),
|
|
96
|
+
ga_client_id: readGaClientId(cookie),
|
|
97
|
+
gclid_value: readGclid(landingPage)
|
|
98
|
+
})) != null ? _a : {};
|
|
99
|
+
};
|
|
14
100
|
const OPEN_STATE_KEY = "mm-intake-widget-open";
|
|
15
101
|
const readOpenState = (storage = sessionStorage) => {
|
|
16
102
|
try {
|
|
@@ -241,6 +327,12 @@
|
|
|
241
327
|
const hostUrl = buildSigningReturnUrl(currentUrl);
|
|
242
328
|
postToChat(
|
|
243
329
|
createEmbedProtocolMessage(EMBED_MESSAGE_TYPE.HOST_READY, {
|
|
330
|
+
// Marketing attribution (MX2-25162): the marketing site's first-touch
|
|
331
|
+
// cookies and the page the chat opened on are only visible from this
|
|
332
|
+
// page, never from the cross-origin chat frame. The chat forwards them
|
|
333
|
+
// on its first /chat turn and Salesforce builds the intake's
|
|
334
|
+
// Marketing_Tracking_Info__c record from them.
|
|
335
|
+
attribution: buildHostAttribution(),
|
|
244
336
|
...hostUrl === null ? {} : { hostUrl },
|
|
245
337
|
...completedEnvelopeId === null ? {} : { completedEnvelopeId },
|
|
246
338
|
// Sent even without an envelope id: the Salesforce service is what
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morgan-and-morgan/intake-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Embeddable chat widget loader.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"files": [
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./dist/mm-intake-widget.js",
|
|
13
13
|
"./widget": "./src/widget.ts",
|
|
14
|
+
"./attribution": "./src/attribution.ts",
|
|
14
15
|
"./protocol": "./src/protocol.ts",
|
|
15
16
|
"./signing": "./src/signing.ts",
|
|
16
17
|
"./loader-config": "./src/loader-config.ts"
|