@morgan-and-morgan/intake-agent 1.1.2 → 1.3.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.
@@ -11,6 +11,101 @@
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_COOKIES = ["trackedLandingPage", "tracked_landing_page"];
40
+ const LANDING_TIMESTAMP_COOKIES = ["trackedLandingPageTimestamp", "tracked_landing_timestamp"];
41
+ const readTimestampCookie = (cookie) => {
42
+ const raw = readFirstCookie(cookie, LANDING_TIMESTAMP_COOKIES);
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 readFirstCookie = (cookie, names) => {
51
+ for (const name of names) {
52
+ const raw = readCookie(cookie, name);
53
+ if (raw !== void 0) {
54
+ return raw;
55
+ }
56
+ }
57
+ return void 0;
58
+ };
59
+ const decodeCookieValue = (raw) => {
60
+ try {
61
+ return decodeURIComponent(raw);
62
+ } catch {
63
+ return raw;
64
+ }
65
+ };
66
+ const parseUrl = (value) => {
67
+ try {
68
+ return new URL(value);
69
+ } catch {
70
+ return null;
71
+ }
72
+ };
73
+ const readLandingPage = (cookie) => {
74
+ const raw = readFirstCookie(cookie, LANDING_PAGE_COOKIES);
75
+ if (raw === void 0 || parseUrl(raw) !== null) {
76
+ return raw;
77
+ }
78
+ const decoded = decodeCookieValue(raw);
79
+ return parseUrl(decoded) === null ? raw : decoded;
80
+ };
81
+ const readGaClientId = (cookie) => {
82
+ const raw = readCookie(cookie, "_ga");
83
+ if (raw === void 0) {
84
+ return void 0;
85
+ }
86
+ const value = decodeCookieValue(raw);
87
+ const parts = value.split(".");
88
+ return parts.length >= 4 ? parts.slice(-2).join(".") : value;
89
+ };
90
+ const readGclid = (landingUrl) => {
91
+ var _a, _b;
92
+ return landingUrl === void 0 ? void 0 : (_b = (_a = parseUrl(landingUrl)) == null ? void 0 : _a.searchParams.get("gclid")) != null ? _b : void 0;
93
+ };
94
+ const readPathname = (url) => {
95
+ var _a;
96
+ return (_a = parseUrl(url)) == null ? void 0 : _a.pathname;
97
+ };
98
+ const buildHostAttribution = (currentUrl = globalThis.location.href, cookie = document.cookie) => {
99
+ var _a;
100
+ const landingPage = readLandingPage(cookie);
101
+ return (_a = sanitizeIntakeChatAttribution({
102
+ tracked_landing_page: landingPage,
103
+ tracked_landing_timestamp: readTimestampCookie(cookie),
104
+ conversion_page: readPathname(currentUrl),
105
+ ga_client_id: readGaClientId(cookie),
106
+ gclid_value: readGclid(landingPage)
107
+ })) != null ? _a : {};
108
+ };
14
109
  const OPEN_STATE_KEY = "mm-intake-widget-open";
15
110
  const readOpenState = (storage = sessionStorage) => {
16
111
  try {
@@ -241,6 +336,12 @@
241
336
  const hostUrl = buildSigningReturnUrl(currentUrl);
242
337
  postToChat(
243
338
  createEmbedProtocolMessage(EMBED_MESSAGE_TYPE.HOST_READY, {
339
+ // Marketing attribution (MX2-25162): the marketing site's first-touch
340
+ // cookies and the page the chat opened on are only visible from this
341
+ // page, never from the cross-origin chat frame. The chat forwards them
342
+ // on its first /chat turn and Salesforce builds the intake's
343
+ // Marketing_Tracking_Info__c record from them.
344
+ attribution: buildHostAttribution(),
244
345
  ...hostUrl === null ? {} : { hostUrl },
245
346
  ...completedEnvelopeId === null ? {} : { completedEnvelopeId },
246
347
  // 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.1.2",
3
+ "version": "1.3.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"