@qualified-salesforce/web 0.1.1 → 0.1.3
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/CHANGELOG.md +28 -3
- package/README.md +68 -2
- package/constants.generated.js +1 -1
- package/package.json +1 -1
- package/widget/constants.generated.js +1 -0
- package/widget/journey/schema.js +106 -0
- package/widget/journey/tracker.js +387 -0
- package/widget/journey/transport.js +104 -0
- package/widget/multimodal_v2/locales/de.js +1 -1
- package/widget/server-logger.js +4 -1
- package/widget/tracker.js +16 -0
- package/widget/widget/widget.js +29 -13
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable changes to `@qualified-salesforce/web` are recorded here. The format
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the package aims to follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.1.3]
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Doc links resolved to a private repo.** The `LICENSE.txt` and `CAPABILITY-REFERENCE.md`
|
|
12
|
+
references in the README, and the `CAPABILITY-REFERENCE.md` reference plus the version links in
|
|
13
|
+
this changelog, were relative or `github.com/FatbearLabs` links — the relative ones npm rewrites
|
|
14
|
+
against the package's `repository` field (a private repo), so a reader got a 404. The file
|
|
15
|
+
references are now plain text noting the files ship in the package (the full license text is
|
|
16
|
+
already reproduced inline, and `CAPABILITY-REFERENCE.md` ships in the tarball), and the changelog
|
|
17
|
+
version links now point at the public npm version pages. No runtime or API change.
|
|
18
|
+
|
|
19
|
+
## [0.1.2]
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **License text embedded in the README.** The `## License` section of the package `README.md`
|
|
24
|
+
now reproduces the full proprietary Terms of Use inline, so the license is readable on the npm
|
|
25
|
+
package page. npm shows only "Proprietary. See LICENSE.txt" in its License sidebar and does not
|
|
26
|
+
resolve the relative `LICENSE.txt` link in the rendered README, so a visitor previously could not
|
|
27
|
+
read the terms without opening the Code tab. `LICENSE.txt` still ships in the tarball unchanged.
|
|
28
|
+
No runtime or API change.
|
|
29
|
+
|
|
7
30
|
## [0.1.1]
|
|
8
31
|
|
|
9
32
|
### Added
|
|
@@ -74,11 +97,13 @@ All notable changes to `@qualified-salesforce/web` are recorded here. The format
|
|
|
74
97
|
- **Origins and CSP.** With every capability denied (the default), the package reaches only your
|
|
75
98
|
own origin and Qualified's own API / websocket / asset planes — no third-party origin. Each
|
|
76
99
|
capability's third-party origin and egress, and whether it needs a CSP change, is documented per
|
|
77
|
-
key in
|
|
100
|
+
key in `CAPABILITY-REFERENCE.md`, shipped in the package.
|
|
78
101
|
- **Why the gated code ships present-but-inert rather than removed.** A denied capability has no
|
|
79
102
|
execution path and Qualified cannot enable it remotely, so keeping it inert in one universal,
|
|
80
103
|
reviewed package is equivalent to absence for what runs in the browser — while letting you enable
|
|
81
104
|
a feature later by changing one `setup()` option, with no separate build.
|
|
82
105
|
|
|
83
|
-
[0.1.
|
|
84
|
-
[0.1.
|
|
106
|
+
[0.1.3]: https://www.npmjs.com/package/@qualified-salesforce/web/v/0.1.3
|
|
107
|
+
[0.1.2]: https://www.npmjs.com/package/@qualified-salesforce/web/v/0.1.2
|
|
108
|
+
[0.1.1]: https://www.npmjs.com/package/@qualified-salesforce/web/v/0.1.1
|
|
109
|
+
[0.1.0]: https://www.npmjs.com/package/@qualified-salesforce/web/v/0.1.0
|
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ renders the widget unstyled.
|
|
|
40
40
|
Every optional feature is **off unless you enroll it** in the `capabilities` option. Omitting
|
|
41
41
|
`capabilities` (or passing `[]`) leaves them all off.
|
|
42
42
|
|
|
43
|
-
See
|
|
43
|
+
See `CAPABILITY-REFERENCE.md`, shipped in the package, for the full list — what each capability
|
|
44
44
|
does, the data it sends, whether it needs a CSP change, and its default.
|
|
45
45
|
|
|
46
46
|
## TypeScript
|
|
@@ -62,4 +62,70 @@ const qualified = await setup({
|
|
|
62
62
|
|
|
63
63
|
## License
|
|
64
64
|
|
|
65
|
-
Proprietary.
|
|
65
|
+
Proprietary. The full text is reproduced below and also ships in the package as `LICENSE.txt`.
|
|
66
|
+
|
|
67
|
+
### Terms of Use
|
|
68
|
+
|
|
69
|
+
Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
70
|
+
|
|
71
|
+
These Terms of Use govern the download, installation, and/or use of this software provided by
|
|
72
|
+
Salesforce, Inc. ("Salesforce") (the "Software"), were last updated on April 15, 2025, and
|
|
73
|
+
constitute a legally binding agreement between you and Salesforce. If you do not agree to these
|
|
74
|
+
Terms of Use, do not install or use the Software.
|
|
75
|
+
|
|
76
|
+
Salesforce grants you a worldwide, non-exclusive, no-charge, royalty-free copyright license to
|
|
77
|
+
reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and
|
|
78
|
+
distribute the Software and derivative works subject to these Terms. These Terms shall be included
|
|
79
|
+
in all copies or substantial portions of the Software.
|
|
80
|
+
|
|
81
|
+
Subject to the limited rights expressly granted hereunder, Salesforce reserves all rights, title,
|
|
82
|
+
and interest in and to all intellectual property subsisting in the Software. No rights are granted
|
|
83
|
+
to you hereunder other than as expressly set forth herein. Users residing in countries on the
|
|
84
|
+
United States Office of Foreign Assets Control sanction list, or which are otherwise subject to a US
|
|
85
|
+
export embargo, may not use the Software.
|
|
86
|
+
|
|
87
|
+
Implementation of the Software may require development work, for which you are responsible. The
|
|
88
|
+
Software may contain bugs, errors and incompatibilities and is made available on an AS IS basis
|
|
89
|
+
without support, updates, or service level commitments.
|
|
90
|
+
|
|
91
|
+
Salesforce reserves the right at any time to modify, suspend, or discontinue, the Software (or any
|
|
92
|
+
part thereof) with or without notice. You agree that Salesforce shall not be liable to you or to any
|
|
93
|
+
third party for any modification, suspension, or discontinuance.
|
|
94
|
+
|
|
95
|
+
You agree to defend Salesforce against any claim, demand, suit or proceeding made or brought against
|
|
96
|
+
Salesforce by a third party arising out of or accruing from (a) your use of the Software, and (b)
|
|
97
|
+
any application you develop with the Software that infringes any copyright, trademark, trade secret,
|
|
98
|
+
trade dress, patent, or other intellectual property right of any person or defames any person or
|
|
99
|
+
violates their rights of publicity or privacy (each a "Claim Against Salesforce"), and will
|
|
100
|
+
indemnify Salesforce from any damages, attorney fees, and costs finally awarded against Salesforce
|
|
101
|
+
as a result of, or for any amounts paid by Salesforce under a settlement approved by you in writing
|
|
102
|
+
of, a Claim Against Salesforce, provided Salesforce (x) promptly gives you written notice of the
|
|
103
|
+
Claim Against Salesforce, (y) gives you sole control of the defense and settlement of the Claim
|
|
104
|
+
Against Salesforce (except that you may not settle any Claim Against Salesforce unless it
|
|
105
|
+
unconditionally releases Salesforce of all liability), and (z) gives you all reasonable assistance,
|
|
106
|
+
at your expense.
|
|
107
|
+
|
|
108
|
+
WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE SOFTWARE IS NOT SUPPORTED AND IS PROVIDED "AS
|
|
109
|
+
IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL SALESFORCE HAVE ANY
|
|
110
|
+
LIABILITY FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
|
|
111
|
+
PUNITIVE, OR CONSEQUENTIAL DAMAGES, OR DAMAGES BASED ON LOST PROFITS, DATA, OR USE, IN CONNECTION
|
|
112
|
+
WITH THE SOFTWARE, HOWEVER CAUSED AND WHETHER IN CONTRACT, TORT, OR UNDER ANY OTHER THEORY OF
|
|
113
|
+
LIABILITY, WHETHER OR NOT YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
114
|
+
|
|
115
|
+
These Terms of Use shall be governed exclusively by the internal laws of the State of California,
|
|
116
|
+
without regard to its conflicts of laws rules. Each party hereby consents to the exclusive
|
|
117
|
+
jurisdiction of the state and federal courts located in San Francisco County, California to
|
|
118
|
+
adjudicate any dispute arising out of or relating to these Terms of Use and the download,
|
|
119
|
+
installation, and/or use of the Software. Except as expressly stated herein, these Terms of Use
|
|
120
|
+
constitute the entire agreement between the parties, and supersede all prior and contemporaneous
|
|
121
|
+
agreements, proposals, or representations, written or oral, concerning their subject matter. No
|
|
122
|
+
modification, amendment, or waiver of any provision of these Terms of Use shall be effective unless
|
|
123
|
+
it is by an update to these Terms of Use that Salesforce makes available, or is in writing and
|
|
124
|
+
signed by the party against whom the modification, amendment, or waiver is to be asserted.
|
|
125
|
+
|
|
126
|
+
Data Privacy: Salesforce may collect, process, and store device, system, and other information
|
|
127
|
+
related to your use of the Software. This information includes, but is not limited to, IP address,
|
|
128
|
+
user metrics, and other data ("Usage Data"). Salesforce may use Usage Data for analytics, product
|
|
129
|
+
development, and marketing purposes. You acknowledge that files generated in conjunction with the
|
|
130
|
+
Software may contain sensitive or confidential data, and you are solely responsible for anonymizing
|
|
131
|
+
and protecting such data.
|
package/constants.generated.js
CHANGED
|
@@ -3,7 +3,7 @@ import { _tagged_template_literal_loose, lib } from "./js/4101.js";
|
|
|
3
3
|
|
|
4
4
|
function constants_generated_templateObject() {
|
|
5
5
|
const data = _tagged_template_literal_loose([
|
|
6
|
-
"\n query CurrentAccount {\n account {\n id\n name\n firstName\n lastName\n email\n phoneNumber\n jobTitle\n avatarUrl\n timeZone\n preferredConversationLocales\n emailSignature\n useGmailSignature\n mailingConnection {\n id\n type\n }\n meetingConferenceDetails\n calendarInviteBody\n calendarMinimumNotice\n calendarBuffer\n calendarAvailability {\n day\n start\n end\n __typename\n }\n mobileNotificationSchedule {\n day\n start\n end\n __typename\n }\n enableReturnVisitorAlerts\n enableVoiceAlerts\n enableBlurBackground\n defaultNotificationSound\n dismissedDesktopAnnouncement\n waitingForRepNotificationSound\n waitingForRepVoiceAlert\n role\n permissionsAdmin\n permissionsCsm\n permissionsChat\n permissionsMeeting\n permissionsViewAllVisitors\n permissionsEditAllTeamSettings\n permissionsEditTeammates\n permissionsEditGroups\n permissionsEditTeamCalendar\n permissionsEditBusinessHours\n permissionsEditSso\n permissionsEditMessenger\n permissionsEditVsiitorFields\n permissionsEditSegments\n permissionsEditLiveStreams\n permissionsEditRouting\n permissionsEditMacros\n permissionsEditSalesforce\n permissionsEditPardot\n permissionsEditPiperEmail\n permissionsPublishPiperEmail\n permissionsViewPiperEmailResponses\n permissionsEditMarketo\n permissionsEditHubspot\n permissionsEditEloqua\n permissionsEditOutreach\n permissionsEditClearbit\n permissionsEditSixsense\n permissionsEditDemandbase\n permissionsEditSlack\n permissionsEditMicrosoft\n permissionsEditZoominfo\n permissionsEditSalesloft\n permissionsEditLinkedin\n permissionsEditGoogle\n permissionsViewAllStatistics\n permissionsEditAnalytics\n permissionsExportReports\n permissionsBotView\n permissionsBotEdit\n permissionsBotPublish\n permissionsEditMeetingTypes\n permissionsEditMeetingRouting\n permissionsEditAutoPounce\n permissionsShareDashboards\n permissionsViewRevenueAndPipelineDashboard\n available\n videoAvailable\n autoPounceAvailable\n snoozeEndsAt\n hasMobile\n mobileRequestForChatSound\n mobileMessageReceivedSound\n __typename\n salesforceConnection {\n id\n __typename\n updatedAt\n username\n }\n calendarConnection {\n ...CalendarConnection\n }\n slackConnection {\n id\n __typename\n }\n microsoftConnection {\n id\n __typename\n invalid\n }\n externalMeetingConnection {\n id\n __typename\n updatedAt\n email\n type\n invalid\n invalidAt\n scopes\n }\n macros {\n ...Macro\n }\n announcements {\n ...Announcement\n }\n team {\n id\n allowlistedDomains\n denylistedVisitorStreamDomains\n company\n appexchangeCompanyName\n autoUpdateSalesforceExternalRecords\n token\n phoneNumber\n countryCode\n features\n featureAlternatives {\n flag\n alternative\n }\n preferencesSendGaEvents\n preferencesSendFbEvents\n preferencesShowIdleVisitorsDefault\n preferencesShowSalesforceMatchSettings\n preferencesSfdcOauthPromptSelectAccount\n preferencesDisableScimRoleSync\n preferencesDisableDesktopAppAnnouncement\n defaultSyncActionType\n samlEnabled\n samlVerified\n scimEnabled\n identityProvider\n maximumExportRows\n createdAt\n signalsToken\n privacyPolicyMaxLength\n companyEmail\n companyAddress\n address\n city\n state\n postalCode\n country\n mailboxesNeedAttention\n __typename\n activeLinkedinAdAccounts {\n ...LinkedInAdAccount\n }\n activeGoogleAdAccounts {\n ...GoogleAdAccount\n }\n salesforceConnection {\n id\n __typename\n updatedAt\n username\n invalid\n sandbox\n host\n instanceUrl\n isSyncing\n lastSyncTime\n lastSyncError\n matchSettings {\n __typename\n type\n contactExternalFieldId\n leadExternalFieldId\n }\n }\n pardotConnection {\n id\n __typename\n email\n dynamicContentConfigured\n activityTrackingConfigured\n invalid\n demo\n businessUnitId\n username\n }\n zoomInfoConnection {\n id\n __typename\n email\n invalid\n enterpriseEnabled\n websightsEnabled\n rateLimitedEnterprise\n rateLimitedWebsights\n }\n marketoConnection {\n id\n __typename\n munchkinId\n invalid\n }\n clearbitConnection {\n id\n __typename\n secretKeyMasked\n revealEnabled\n enrichmentEnabled\n confidenceThreshold\n }\n sixsenseConnection {\n id\n __typename\n secretKeyMasked\n confidenceThreshold\n productLines\n }\n slackConnection {\n id\n __typename\n invalid\n authorizedUserEmailScopes\n version\n }\n piperSlackConnection {\n id\n __typename\n invalid\n authorizedUserEmailScopes\n }\n microsoftConnection {\n id\n __typename\n invalid\n email\n }\n demandbaseConnection {\n id\n __typename\n secretKeyMasked\n version\n }\n demandbaseSharedRevealEnabled\n demandbaseSharedEnrichmentEnabled\n demandbaseSharedConfidenceThreshold\n demandbaseCustomizedMsa\n hubspotConnection {\n id\n __typename\n visitorAssociationConfigured\n hasAllScopes\n invalid\n identifier\n formsLastSyncedAt\n }\n linkedinConnection {\n ...LinkedInConnection\n }\n facebookConnection {\n ...FacebookConnection\n }\n eloquaConnection {\n id\n __typename\n visitorAssociationConfigured\n invalid\n }\n outreachGalaxyConnection {\n id\n __typename\n invalid\n }\n salesloftConnection {\n id\n __typename\n invalid\n }\n gongConnection {\n id\n __typename\n invalid\n }\n googleConnection {\n ...GoogleConnection\n }\n users {\n ...TeamUser\n }\n inactiveUsers {\n ...TeamUser\n }\n groups {\n id\n __typename\n name\n members {\n id\n __typename\n }\n }\n segments {\n ...SegmentMeta\n }\n calendarEventTypes {\n id\n name\n duration\n sendReminder\n eventTitle\n eventBody\n enableMeetingLink\n enableCalendarEventTypeInChat\n enableNoShowReporting\n removeCancelOrReschedule\n __typename\n }\n macros {\n ...Macro\n }\n fields {\n __typename\n id\n name\n apiName\n type\n hidden\n listVisible\n }\n variables {\n ...Variable\n }\n dashboards {\n __typename\n id\n type\n settings {\n ...DashboardSettings\n }\n }\n listGroupings {\n ...ListGrouping\n }\n businessHours {\n __typename\n days\n startHour\n endHour\n timeZone\n }\n calendarEventExclusionDates {\n ...CalendarEventExclusionDate\n }\n autoPounceSettings {\n missedConnections {\n responseTimeLimitSeconds\n }\n }\n copilotEnabledMessageTransformationTypes\n aiInteractionReports {\n nodes {\n userId\n messageId\n }\n }\n customWaitForIdentifiersTime\n }\n activeMeeting {\n id\n status\n token\n startedAt\n visitorId\n userId\n __typename\n }\n }\n }\n fragment TeamUser on User {\n __typename\n id\n email\n name\n avatarUrl\n available\n snoozeEndsAt\n role\n customAdminPermissions\n permissionsAdmin\n permissionsViewAllVisitors\n permissionsEditAllTeamSettings\n permissionsEditTeammates\n permissionsEditGroups\n permissionsEditTeamCalendar\n permissionsEditBusinessHours\n permissionsEditSso\n permissionsEditMessenger\n permissionsEditVsiitorFields\n permissionsEditSegments\n permissionsEditLiveStreams\n permissionsEditRouting\n permissionsEditMacros\n permissionsEditSalesforce\n permissionsEditPardot\n permissionsEditPiperEmail\n permissionsPublishPiperEmail\n permissionsViewPiperEmailResponses\n permissionsEditMarketo\n permissionsEditHubspot\n permissionsEditEloqua\n permissionsEditOutreach\n permissionsEditClearbit\n permissionsEditSixsense\n permissionsEditDemandbase\n permissionsEditSlack\n permissionsEditMicrosoft\n permissionsEditZoominfo\n permissionsEditSalesloft\n permissionsEditLinkedin\n permissionsEditGoogle\n permissionsViewAllStatistics\n permissionsEditAnalytics\n permissionsExportReports\n permissionsBotView\n permissionsBotEdit\n permissionsBotPublish\n permissionsEditMeetingTypes\n permissionsEditMeetingRouting\n permissionsEditAutoPounce\n permissionsShareDashboards\n permissionsViewRevenueAndPipelineDashboard\n status\n lastOnlineAt\n authenticationState\n lastInviteSentAt\n lastSamlBindingLinkSentAt\n calendarReminderSentAt\n mailingConnectionReminderSentAt\n actionRequiredReasons\n meetingQueuesActionRequiredReasons\n groups {\n name\n }\n salesforceConnection {\n __typename\n id\n updatedAt\n username\n }\n calendarInviteBody\n meetingConferenceDetails\n calendarConnection {\n ...CalendarConnection\n }\n mailingConnection {\n ...CalendarConnection\n }\n slackConnection {\n id\n __typename\n }\n microsoftConnection {\n id\n __typename\n invalid\n }\n externalMeetingConnection {\n id\n type\n __typename\n }\n userProfile {\n id\n __typename\n name\n type\n }\n }\n fragment CalendarConnection on CalendarConnection {\n id\n __typename\n createdAt\n updatedAt\n email\n type\n invalid\n invalidAt\n scopes\n }\n\n fragment Macro on Macro {\n __typename\n id\n shared\n name\n steps {\n __typename\n opcode\n operands {\n __typename\n text\n }\n }\n }\n\n fragment SegmentMeta on Segment {\n __typename\n id\n name\n }\n\n fragment Announcement on Announcement {\n __typename\n id\n ctaText\n ctaUrl\n message\n type\n startsAt\n endsAt\n }\n\n fragment Variable on Variable {\n __typename\n key\n category\n type\n group\n source\n label\n require\n }\n\n fragment DashboardSettings on DashboardSettings {\n __typename\n startDate\n endDate\n currencyField\n conversationDateThreshold\n opportunityTypes\n status\n stages\n attributedOpportunityTiles {\n title\n color\n attributionModel\n columns {\n expression\n width\n }\n orderBy\n orderReversed\n matchTypes\n leadSourceFieldValues\n }\n }\n\n fragment ListGrouping on ListGrouping {\n __typename\n id\n name\n color\n position\n bots {\n __typename\n id\n loadingDraftScript\n }\n segments {\n __typename\n id\n }\n listGroupingSetting {\n __typename\n id\n listGroupingId\n settingsSendPushNotification\n settingsDisableConsoleNotification\n settingsEnableVoiceNotifications\n sound\n }\n autoPounce\n }\n\n fragment LinkedInConnection on LinkedInConnection {\n id\n __typename\n firstName\n lastName\n displayName\n accountId\n invalid\n }\n\n fragment LinkedInAdAccount on LinkedInAdAccount {\n id\n __typename\n externalId\n name\n }\n\n fragment GoogleConnection on GoogleConnection {\n id\n __typename\n email\n loginCustomerId\n invalid\n }\n\n fragment GoogleAdAccount on GoogleAdAccount {\n id\n __typename\n externalId\n name\n }\n\n fragment FacebookConnection on FacebookConnection {\n id\n __typename\n name\n email\n }\n\n fragment CalendarEventExclusionDate on CalendarEventExclusionDate {\n __typename\n id\n startDate\n endDate\n }\n"
|
|
6
|
+
"\n query CurrentAccount {\n account {\n id\n name\n firstName\n lastName\n email\n phoneNumber\n jobTitle\n avatarUrl\n timeZone\n preferredConversationLocales\n emailSignature\n useGmailSignature\n mailingConnection {\n id\n type\n }\n meetingConferenceDetails\n calendarInviteBody\n calendarMinimumNotice\n calendarBuffer\n calendarAvailability {\n day\n start\n end\n __typename\n }\n mobileNotificationSchedule {\n day\n start\n end\n __typename\n }\n enableReturnVisitorAlerts\n enableVoiceAlerts\n enableBlurBackground\n defaultNotificationSound\n dismissedDesktopAnnouncement\n waitingForRepNotificationSound\n waitingForRepVoiceAlert\n role\n permissionsAdmin\n permissionsCsm\n permissionsChat\n permissionsMeeting\n permissionsViewAllVisitors\n permissionsEditAllTeamSettings\n permissionsEditTeammates\n permissionsEditGroups\n permissionsEditTeamCalendar\n permissionsEditBusinessHours\n permissionsEditSso\n permissionsEditMessenger\n permissionsEditVsiitorFields\n permissionsEditSegments\n permissionsEditLiveStreams\n permissionsEditRouting\n permissionsEditMacros\n permissionsEditSalesforce\n permissionsEditPardot\n permissionsEditPiperEmail\n permissionsPublishPiperEmail\n permissionsViewPiperEmailResponses\n permissionsEditMarketo\n permissionsEditHubspot\n permissionsEditEloqua\n permissionsEditOutreach\n permissionsEditClearbit\n permissionsEditSixsense\n permissionsEditDemandbase\n permissionsEditSlack\n permissionsEditMicrosoft\n permissionsEditZoominfo\n permissionsEditSalesloft\n permissionsEditLinkedin\n permissionsEditGoogle\n permissionsViewAllStatistics\n permissionsEditAnalytics\n permissionsExportReports\n permissionsBotView\n permissionsBotEdit\n permissionsBotPublish\n permissionsEditMeetingTypes\n permissionsEditMeetingRouting\n permissionsEditAutoPounce\n permissionsShareDashboards\n permissionsViewRevenueAndPipelineDashboard\n available\n videoAvailable\n autoPounceAvailable\n snoozeEndsAt\n hasMobile\n mobileRequestForChatSound\n mobileMessageReceivedSound\n __typename\n salesforceConnection {\n id\n __typename\n updatedAt\n username\n }\n calendarConnection {\n ...CalendarConnection\n }\n slackConnection {\n id\n __typename\n }\n microsoftConnection {\n id\n __typename\n invalid\n }\n externalMeetingConnection {\n id\n __typename\n updatedAt\n email\n type\n invalid\n invalidAt\n scopes\n }\n macros {\n ...Macro\n }\n announcements {\n ...Announcement\n }\n team {\n id\n allowlistedDomains\n denylistedVisitorStreamDomains\n company\n appexchangeCompanyName\n autoUpdateSalesforceExternalRecords\n token\n phoneNumber\n countryCode\n features\n featureAlternatives {\n flag\n alternative\n }\n preferencesSendGaEvents\n preferencesSendFbEvents\n preferencesShowIdleVisitorsDefault\n preferencesShowSalesforceMatchSettings\n preferencesSfdcOauthPromptSelectAccount\n preferencesDisableScimRoleSync\n preferencesDisableDesktopAppAnnouncement\n defaultSyncActionType\n samlEnabled\n samlVerified\n scimEnabled\n identityProvider\n maximumExportRows\n createdAt\n signalsToken\n privacyPolicyMaxLength\n companyEmail\n companyAddress\n address\n city\n state\n postalCode\n country\n mailboxesNeedAttention\n __typename\n activeLinkedinAdAccounts {\n ...LinkedInAdAccount\n }\n activeGoogleAdAccounts {\n ...GoogleAdAccount\n }\n salesforceConnection {\n id\n __typename\n updatedAt\n username\n invalid\n sandbox\n host\n instanceUrl\n isSyncing\n lastSyncTime\n lastSyncError\n matchSettings {\n __typename\n type\n contactExternalFieldId\n leadExternalFieldId\n }\n }\n pardotConnection {\n id\n __typename\n email\n dynamicContentConfigured\n activityTrackingConfigured\n invalid\n demo\n businessUnitId\n username\n }\n zoomInfoConnection {\n id\n __typename\n email\n invalid\n enterpriseEnabled\n websightsEnabled\n rateLimitedEnterprise\n rateLimitedWebsights\n }\n marketoConnection {\n id\n __typename\n munchkinId\n invalid\n }\n clearbitConnection {\n id\n __typename\n secretKeyMasked\n revealEnabled\n enrichmentEnabled\n confidenceThreshold\n }\n sixsenseConnection {\n id\n __typename\n secretKeyMasked\n confidenceThreshold\n productLines\n }\n slackConnection {\n id\n __typename\n invalid\n authorizedUserEmailScopes\n version\n }\n piperSlackConnection {\n id\n __typename\n invalid\n authorizedUserEmailScopes\n }\n microsoftConnection {\n id\n __typename\n invalid\n email\n }\n demandbaseConnection {\n id\n __typename\n secretKeyMasked\n version\n }\n demandbaseSharedRevealEnabled\n demandbaseSharedEnrichmentEnabled\n demandbaseSharedConfidenceThreshold\n demandbaseCustomizedMsa\n demandbaseEnrichmentMsaAcceptedAt\n demandbaseEnrichmentMsaAcceptedBy\n hubspotConnection {\n id\n __typename\n visitorAssociationConfigured\n hasAllScopes\n invalid\n identifier\n formsLastSyncedAt\n }\n linkedinConnection {\n ...LinkedInConnection\n }\n facebookConnection {\n ...FacebookConnection\n }\n eloquaConnection {\n id\n __typename\n visitorAssociationConfigured\n invalid\n }\n outreachGalaxyConnection {\n id\n __typename\n invalid\n }\n salesloftConnection {\n id\n __typename\n invalid\n }\n gongConnection {\n id\n __typename\n invalid\n }\n googleConnection {\n ...GoogleConnection\n }\n users {\n ...TeamUser\n }\n inactiveUsers {\n ...TeamUser\n }\n groups {\n id\n __typename\n name\n members {\n id\n __typename\n }\n }\n segments {\n ...SegmentMeta\n }\n calendarEventTypes {\n id\n name\n duration\n sendReminder\n eventTitle\n eventBody\n enableMeetingLink\n enableCalendarEventTypeInChat\n enableNoShowReporting\n removeCancelOrReschedule\n __typename\n }\n macros {\n ...Macro\n }\n fields {\n __typename\n id\n name\n apiName\n type\n hidden\n listVisible\n }\n variables {\n ...Variable\n }\n dashboards {\n __typename\n id\n type\n settings {\n ...DashboardSettings\n }\n }\n listGroupings {\n ...ListGrouping\n }\n businessHours {\n __typename\n days\n startHour\n endHour\n timeZone\n }\n calendarEventExclusionDates {\n ...CalendarEventExclusionDate\n }\n autoPounceSettings {\n missedConnections {\n responseTimeLimitSeconds\n }\n }\n copilotEnabledMessageTransformationTypes\n aiInteractionReports {\n nodes {\n userId\n messageId\n }\n }\n customWaitForIdentifiersTime\n }\n activeMeeting {\n id\n status\n token\n startedAt\n visitorId\n userId\n __typename\n }\n }\n }\n fragment TeamUser on User {\n __typename\n id\n email\n name\n avatarUrl\n available\n snoozeEndsAt\n role\n customAdminPermissions\n permissionsAdmin\n permissionsViewAllVisitors\n permissionsEditAllTeamSettings\n permissionsEditTeammates\n permissionsEditGroups\n permissionsEditTeamCalendar\n permissionsEditBusinessHours\n permissionsEditSso\n permissionsEditMessenger\n permissionsEditVsiitorFields\n permissionsEditSegments\n permissionsEditLiveStreams\n permissionsEditRouting\n permissionsEditMacros\n permissionsEditSalesforce\n permissionsEditPardot\n permissionsEditPiperEmail\n permissionsPublishPiperEmail\n permissionsViewPiperEmailResponses\n permissionsEditMarketo\n permissionsEditHubspot\n permissionsEditEloqua\n permissionsEditOutreach\n permissionsEditClearbit\n permissionsEditSixsense\n permissionsEditDemandbase\n permissionsEditSlack\n permissionsEditMicrosoft\n permissionsEditZoominfo\n permissionsEditSalesloft\n permissionsEditLinkedin\n permissionsEditGoogle\n permissionsViewAllStatistics\n permissionsEditAnalytics\n permissionsExportReports\n permissionsBotView\n permissionsBotEdit\n permissionsBotPublish\n permissionsEditMeetingTypes\n permissionsEditMeetingRouting\n permissionsEditAutoPounce\n permissionsShareDashboards\n permissionsViewRevenueAndPipelineDashboard\n status\n lastOnlineAt\n authenticationState\n lastInviteSentAt\n lastSamlBindingLinkSentAt\n calendarReminderSentAt\n mailingConnectionReminderSentAt\n actionRequiredReasons\n meetingQueuesActionRequiredReasons\n groups {\n name\n }\n salesforceConnection {\n __typename\n id\n updatedAt\n username\n }\n calendarInviteBody\n meetingConferenceDetails\n calendarConnection {\n ...CalendarConnection\n }\n mailingConnection {\n ...CalendarConnection\n }\n slackConnection {\n id\n __typename\n }\n microsoftConnection {\n id\n __typename\n invalid\n }\n externalMeetingConnection {\n id\n type\n __typename\n }\n userProfile {\n id\n __typename\n name\n type\n }\n }\n fragment CalendarConnection on CalendarConnection {\n id\n __typename\n createdAt\n updatedAt\n email\n type\n invalid\n invalidAt\n scopes\n }\n\n fragment Macro on Macro {\n __typename\n id\n shared\n name\n steps {\n __typename\n opcode\n operands {\n __typename\n text\n }\n }\n }\n\n fragment SegmentMeta on Segment {\n __typename\n id\n name\n }\n\n fragment Announcement on Announcement {\n __typename\n id\n ctaText\n ctaUrl\n message\n type\n startsAt\n endsAt\n }\n\n fragment Variable on Variable {\n __typename\n key\n category\n type\n group\n source\n label\n require\n }\n\n fragment DashboardSettings on DashboardSettings {\n __typename\n startDate\n endDate\n currencyField\n conversationDateThreshold\n opportunityTypes\n status\n stages\n attributedOpportunityTiles {\n title\n color\n attributionModel\n columns {\n expression\n width\n }\n orderBy\n orderReversed\n matchTypes\n leadSourceFieldValues\n }\n }\n\n fragment ListGrouping on ListGrouping {\n __typename\n id\n name\n color\n position\n bots {\n __typename\n id\n loadingDraftScript\n }\n segments {\n __typename\n id\n }\n listGroupingSetting {\n __typename\n id\n listGroupingId\n settingsSendPushNotification\n settingsDisableConsoleNotification\n settingsEnableVoiceNotifications\n sound\n }\n autoPounce\n }\n\n fragment LinkedInConnection on LinkedInConnection {\n id\n __typename\n firstName\n lastName\n displayName\n accountId\n invalid\n }\n\n fragment LinkedInAdAccount on LinkedInAdAccount {\n id\n __typename\n externalId\n name\n }\n\n fragment GoogleConnection on GoogleConnection {\n id\n __typename\n email\n loginCustomerId\n invalid\n }\n\n fragment GoogleAdAccount on GoogleAdAccount {\n id\n __typename\n externalId\n name\n }\n\n fragment FacebookConnection on FacebookConnection {\n id\n __typename\n name\n email\n }\n\n fragment CalendarEventExclusionDate on CalendarEventExclusionDate {\n __typename\n id\n startDate\n endDate\n }\n"
|
|
7
7
|
]);
|
|
8
8
|
constants_generated_templateObject = function _templateObject() {
|
|
9
9
|
return data;
|
package/package.json
CHANGED
|
@@ -282,6 +282,7 @@ const SNIPPET_FEATURE_FLAGS = (/* unused pure expression or super */ null && ([
|
|
|
282
282
|
"aisdrChatEmailValidation",
|
|
283
283
|
"bookerJourneySLI",
|
|
284
284
|
"clientAPIHandleWidgetEvents",
|
|
285
|
+
"clientExperienceJourneyTelemetry",
|
|
285
286
|
"customerZero",
|
|
286
287
|
"demo",
|
|
287
288
|
"demoKillSessionShortcut",
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const JOURNEY_SCHEMA_VERSION = 1;
|
|
2
|
+
const JOURNEY_ID_PATTERN = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
|
|
3
|
+
function isJourneyId(value) {
|
|
4
|
+
return typeof value === "string" && value.length === 36 && JOURNEY_ID_PATTERN.test(value);
|
|
5
|
+
}
|
|
6
|
+
function isJourneyReadyResult(value) {
|
|
7
|
+
return value === "qualification" || value === "booker" || value === "messenger";
|
|
8
|
+
}
|
|
9
|
+
function isJourneyResultType(value) {
|
|
10
|
+
return isJourneyReadyResult(value) || value === "fallback" || value === "no_content" || value === "error" || value === "unknown";
|
|
11
|
+
}
|
|
12
|
+
function isJourneyUiSurface(value) {
|
|
13
|
+
return value === "legacy" || value === "multimodal" || value === "inline";
|
|
14
|
+
}
|
|
15
|
+
function isJourneyBookerDetail(value) {
|
|
16
|
+
return value === undefined || value === "slots" || value === "no_availability";
|
|
17
|
+
}
|
|
18
|
+
function isJourneyResultId(value) {
|
|
19
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
20
|
+
}
|
|
21
|
+
const NON_SUCCESS_OUTCOMES = [
|
|
22
|
+
"fallback",
|
|
23
|
+
"no_content",
|
|
24
|
+
"visible_error",
|
|
25
|
+
"provider_error",
|
|
26
|
+
"timeout",
|
|
27
|
+
"abandoned",
|
|
28
|
+
"superseded",
|
|
29
|
+
"unknown",
|
|
30
|
+
"redirect_initiated"
|
|
31
|
+
];
|
|
32
|
+
function isJourneyNonSuccessOutcome(value) {
|
|
33
|
+
return typeof value === "string" && NON_SUCCESS_OUTCOMES.includes(value);
|
|
34
|
+
}
|
|
35
|
+
function isValidJourneyStart(start) {
|
|
36
|
+
if (!start || typeof start !== "object" || Array.isArray(start)) return false;
|
|
37
|
+
const fields = start;
|
|
38
|
+
if (!isJourneyId(fields.journeyId)) return false;
|
|
39
|
+
if (typeof fields.triggerKey !== "string") return false;
|
|
40
|
+
if (fields.supersessionKey !== undefined && typeof fields.supersessionKey !== "string") return false;
|
|
41
|
+
if (fields.navigationMode !== "same_page" && fields.navigationMode !== "redirect") return false;
|
|
42
|
+
if (typeof fields.entryPath !== "string" || typeof fields.startAnchor !== "string") return false;
|
|
43
|
+
if (fields.journeyKind === "button") {
|
|
44
|
+
return [
|
|
45
|
+
"smart_cta",
|
|
46
|
+
"manual",
|
|
47
|
+
"api",
|
|
48
|
+
"pre_init",
|
|
49
|
+
"inline_auto_start",
|
|
50
|
+
"tooling"
|
|
51
|
+
].includes(fields.entryPath) && [
|
|
52
|
+
"native_click",
|
|
53
|
+
"api_call",
|
|
54
|
+
"initialization"
|
|
55
|
+
].includes(fields.startAnchor);
|
|
56
|
+
}
|
|
57
|
+
if (fields.journeyKind === "smart_form") {
|
|
58
|
+
return [
|
|
59
|
+
"custom",
|
|
60
|
+
"hubspot_v3",
|
|
61
|
+
"hubspot_v4",
|
|
62
|
+
"marketo",
|
|
63
|
+
"pardot",
|
|
64
|
+
"api",
|
|
65
|
+
"pre_init",
|
|
66
|
+
"provider_only",
|
|
67
|
+
"replay_without_root",
|
|
68
|
+
"tooling"
|
|
69
|
+
].includes(fields.entryPath) && [
|
|
70
|
+
"validated_native_submit",
|
|
71
|
+
"api_call",
|
|
72
|
+
"initialization",
|
|
73
|
+
"provider_success",
|
|
74
|
+
"replay"
|
|
75
|
+
].includes(fields.startAnchor);
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
/** Runtime boundary shared by the tracker and transport; TypeScript is erased at iframe bridges. */ function isValidJourneyTerminal(record) {
|
|
80
|
+
if (!record || typeof record !== "object") return false;
|
|
81
|
+
if (!isValidJourneyStart(record.start)) return false;
|
|
82
|
+
const { outcome, resultType, uiSurface, correlationQuality, timingWithheldReason } = record;
|
|
83
|
+
if (outcome !== "success" && !isJourneyNonSuccessOutcome(outcome)) return false;
|
|
84
|
+
if (correlationQuality !== "exact" && correlationQuality !== "ambiguous") return false;
|
|
85
|
+
if (resultType !== undefined && !isJourneyResultType(resultType)) return false;
|
|
86
|
+
if (uiSurface !== undefined && !isJourneyUiSurface(uiSurface)) return false;
|
|
87
|
+
if (!isJourneyBookerDetail(record.bookerResultDetail)) return false;
|
|
88
|
+
if (timingWithheldReason !== undefined && ![
|
|
89
|
+
"backgrounded",
|
|
90
|
+
"ambiguous",
|
|
91
|
+
"missing_clock",
|
|
92
|
+
"invalid_clock"
|
|
93
|
+
].includes(timingWithheldReason)) return false;
|
|
94
|
+
const durations = [
|
|
95
|
+
record.durationMs,
|
|
96
|
+
record.preRequestMs,
|
|
97
|
+
record.requestToResponseMs,
|
|
98
|
+
record.responseToReadyMs
|
|
99
|
+
];
|
|
100
|
+
if (durations.some((value)=>value !== undefined && (typeof value !== "number" || !Number.isFinite(value) || value < 0))) return false;
|
|
101
|
+
if (timingWithheldReason !== undefined && durations.some((value)=>value !== undefined)) return false;
|
|
102
|
+
if (correlationQuality === "ambiguous" && timingWithheldReason === undefined) return false;
|
|
103
|
+
return outcome !== "success" || isJourneyReadyResult(resultType) && isJourneyUiSurface(uiSurface) && (record.durationMs !== undefined || timingWithheldReason !== undefined);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { JOURNEY_SCHEMA_VERSION, isJourneyBookerDetail, isJourneyId, isJourneyNonSuccessOutcome, isJourneyReadyResult, isJourneyResultId, isJourneyResultType, isJourneyUiSurface, isValidJourneyStart, isValidJourneyTerminal };
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { __webpack_require__ } from "../../js/runtime.js";
|
|
2
|
+
import { isValidJourneyTerminal, isJourneyId, isValidJourneyStart, isJourneyBookerDetail, isJourneyResultId, isJourneyResultType, isJourneyNonSuccessOutcome, isJourneyUiSurface, isJourneyReadyResult } from "./schema.js";
|
|
3
|
+
import { _extends } from "../../js/4101.js";
|
|
4
|
+
import "../utils/native_globals.js";
|
|
5
|
+
|
|
6
|
+
/* provided dependency */ var __nativeDocumentAddEventListener = __webpack_require__(68034).Qj;
|
|
7
|
+
/* provided dependency */ var __nativeWindowAddEventListener = __webpack_require__(68034).zh;
|
|
8
|
+
/* provided dependency */ var __nativeDocumentRemoveEventListener = __webpack_require__(68034).LA;
|
|
9
|
+
/* provided dependency */ var __nativeWindowRemoveEventListener = __webpack_require__(68034).kZ;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function elapsed(start, finish) {
|
|
13
|
+
if (start === undefined || finish === undefined) return undefined;
|
|
14
|
+
const value = Math.round(finish - start);
|
|
15
|
+
return Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
class ClientExperienceJourneyTracker {
|
|
18
|
+
start(input) {
|
|
19
|
+
try {
|
|
20
|
+
const suppliedJourneyId = isJourneyId(input.journeyId) ? input.journeyId : undefined;
|
|
21
|
+
if (suppliedJourneyId && this.activeById.has(suppliedJourneyId)) return suppliedJourneyId;
|
|
22
|
+
const overlapping = input.supersessionKey ? [
|
|
23
|
+
...this.activeById.values()
|
|
24
|
+
].filter((journey)=>journey.start.supersessionKey === input.supersessionKey) : [];
|
|
25
|
+
const journeyId = suppliedJourneyId || this.createId();
|
|
26
|
+
if (!isJourneyId(journeyId)) return "";
|
|
27
|
+
// Only an explicitly supplied active ID is a retry. A generator collision is a new attempt.
|
|
28
|
+
if (this.activeById.has(journeyId)) return "";
|
|
29
|
+
const startedAtMs = this.readNow();
|
|
30
|
+
if (overlapping.length > 0 && startedAtMs === undefined) return "";
|
|
31
|
+
const start = _extends({}, input, {
|
|
32
|
+
journeyId
|
|
33
|
+
});
|
|
34
|
+
if (!isValidJourneyStart(start)) return "";
|
|
35
|
+
const hidden = this.isHidden();
|
|
36
|
+
const journey = _extends({
|
|
37
|
+
start,
|
|
38
|
+
startedAtMs,
|
|
39
|
+
checkpoints: {},
|
|
40
|
+
diagnosticKeys: new Set(),
|
|
41
|
+
correlationQuality: "exact"
|
|
42
|
+
}, hidden ? {
|
|
43
|
+
timingWithheldReason: "backgrounded"
|
|
44
|
+
} : startedAtMs === undefined ? {
|
|
45
|
+
timingWithheldReason: "missing_clock"
|
|
46
|
+
} : {});
|
|
47
|
+
for (const existing of overlapping){
|
|
48
|
+
this.finishLifecycle(existing, "superseded");
|
|
49
|
+
}
|
|
50
|
+
this.activeById.set(journeyId, journey);
|
|
51
|
+
this.ensureListeners();
|
|
52
|
+
try {
|
|
53
|
+
this.transport.emitStart(start);
|
|
54
|
+
} catch (unused) {
|
|
55
|
+
// Telemetry cannot affect product behavior.
|
|
56
|
+
}
|
|
57
|
+
return journeyId;
|
|
58
|
+
} catch (unused) {
|
|
59
|
+
return isJourneyId(input == null ? void 0 : input.journeyId) ? input.journeyId : "";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
hasActiveJourney(journeyKind) {
|
|
63
|
+
return [
|
|
64
|
+
...this.activeById.values()
|
|
65
|
+
].some((journey)=>journey.start.journeyKind === journeyKind);
|
|
66
|
+
}
|
|
67
|
+
checkpoint(journeyId, checkpoint) {
|
|
68
|
+
try {
|
|
69
|
+
const journey = this.activeById.get(journeyId);
|
|
70
|
+
if (!journey || journey.checkpoints[checkpoint] !== undefined) return;
|
|
71
|
+
const value = this.readNow();
|
|
72
|
+
if (value === undefined) {
|
|
73
|
+
var _journey;
|
|
74
|
+
(_journey = journey).timingWithheldReason || (_journey.timingWithheldReason = "missing_clock");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
journey.checkpoints[checkpoint] = value;
|
|
78
|
+
} catch (unused) {
|
|
79
|
+
// Telemetry cannot affect product behavior.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
observeResponse(observation) {
|
|
83
|
+
try {
|
|
84
|
+
var // Compatible observations may enrich missing identity without moving the first-response clock.
|
|
85
|
+
_journey, _expectedResultId;
|
|
86
|
+
if (observation.kind === "ignore") return;
|
|
87
|
+
if (observation.journeyKind !== "button" && observation.journeyKind !== "smart_form") return;
|
|
88
|
+
if (observation.kind !== "terminal" && observation.kind !== "result") return;
|
|
89
|
+
// Runtime callers can bypass TypeScript; only renderer readiness can report success.
|
|
90
|
+
if (observation.kind === "terminal" && !isJourneyNonSuccessOutcome(observation.outcome)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (observation.kind === "result" && (!isJourneyReadyResult(observation.resultType) || observation.resultId !== undefined && !isJourneyResultId(observation.resultId) || observation.correlationQuality !== undefined && observation.correlationQuality !== "ambiguous")) return;
|
|
94
|
+
const journey = observation.journeyId ? this.activeById.get(observation.journeyId) : undefined;
|
|
95
|
+
if (!journey || journey.start.journeyKind !== observation.journeyKind) {
|
|
96
|
+
this.emitDiagnostic(_extends({
|
|
97
|
+
journeyKind: observation.journeyKind,
|
|
98
|
+
observationType: observation.kind === "terminal" ? "terminal" : "response",
|
|
99
|
+
attributionFailureReason: !observation.journeyId ? "missing_journey_id" : !isJourneyId(observation.journeyId) ? "malformed_journey_id" : !journey ? "unknown_journey_id" : "journey_kind_mismatch",
|
|
100
|
+
receivedJourneyId: observation.journeyId
|
|
101
|
+
}, observation.kind === "terminal" ? {
|
|
102
|
+
outcome: observation.outcome
|
|
103
|
+
} : {
|
|
104
|
+
resultType: observation.resultType
|
|
105
|
+
}));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (observation.kind === "terminal") {
|
|
109
|
+
this.finish(journey, observation.outcome);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (journey.expectedResultType && (journey.expectedResultType !== observation.resultType || journey.expectedResultId !== undefined && observation.resultId !== undefined && journey.expectedResultId !== observation.resultId)) {
|
|
113
|
+
this.emitDiagnostic({
|
|
114
|
+
journeyKind: observation.journeyKind,
|
|
115
|
+
observationType: "response",
|
|
116
|
+
attributionFailureReason: "result_identity_mismatch",
|
|
117
|
+
receivedJourneyId: observation.journeyId,
|
|
118
|
+
resultType: observation.resultType
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (observation.correlationQuality) {
|
|
123
|
+
var _journey1;
|
|
124
|
+
journey.correlationQuality = observation.correlationQuality;
|
|
125
|
+
(_journey1 = journey).timingWithheldReason || (_journey1.timingWithheldReason = observation.correlationQuality);
|
|
126
|
+
}
|
|
127
|
+
if (!journey.expectedResultType) {
|
|
128
|
+
journey.expectedResultType = observation.resultType;
|
|
129
|
+
this.recordCheckpoint(journey, "result_received");
|
|
130
|
+
}
|
|
131
|
+
(_expectedResultId = (_journey = journey).expectedResultId) != null ? _expectedResultId : _journey.expectedResultId = observation.resultId;
|
|
132
|
+
} catch (unused) {
|
|
133
|
+
// Telemetry cannot affect product behavior.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
markReady(input) {
|
|
137
|
+
try {
|
|
138
|
+
if (input.journeyKind !== "button" && input.journeyKind !== "smart_form" || !isJourneyReadyResult(input.resultType) || !isJourneyUiSurface(input.uiSurface) || !isJourneyResultId(input.resultId) || !isJourneyBookerDetail(input.bookerResultDetail)) return;
|
|
139
|
+
const journey = input.journeyId ? this.activeById.get(input.journeyId) : undefined;
|
|
140
|
+
if (!journey || journey.start.journeyKind !== input.journeyKind || journey.expectedResultType && (journey.expectedResultType !== input.resultType || journey.expectedResultId !== undefined && journey.expectedResultId !== input.resultId)) {
|
|
141
|
+
this.emitDiagnostic({
|
|
142
|
+
journeyKind: input.journeyKind,
|
|
143
|
+
observationType: "renderer_ready",
|
|
144
|
+
attributionFailureReason: !input.journeyId ? "missing_journey_id" : !isJourneyId(input.journeyId) ? "malformed_journey_id" : !journey ? "unknown_journey_id" : journey.start.journeyKind !== input.journeyKind ? "journey_kind_mismatch" : "result_identity_mismatch",
|
|
145
|
+
receivedJourneyId: input.journeyId,
|
|
146
|
+
resultType: input.resultType,
|
|
147
|
+
uiSurface: input.uiSurface,
|
|
148
|
+
bookerResultDetail: input.bookerResultDetail
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.finish(journey, "success", input.resultType, {
|
|
153
|
+
uiSurface: input.uiSurface,
|
|
154
|
+
bookerResultDetail: input.bookerResultDetail
|
|
155
|
+
});
|
|
156
|
+
} catch (unused) {
|
|
157
|
+
// Telemetry cannot affect product behavior.
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
terminate(journeyId, outcome, resultType) {
|
|
161
|
+
try {
|
|
162
|
+
if (!isJourneyNonSuccessOutcome(outcome)) return;
|
|
163
|
+
if (resultType !== undefined && !isJourneyResultType(resultType)) return;
|
|
164
|
+
const journey = this.activeById.get(journeyId);
|
|
165
|
+
if (journey) this.finishLifecycle(journey, outcome, resultType);
|
|
166
|
+
} catch (unused) {
|
|
167
|
+
// Telemetry cannot affect product behavior.
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
dispose(options) {
|
|
171
|
+
try {
|
|
172
|
+
const active = [
|
|
173
|
+
...this.activeById.values()
|
|
174
|
+
];
|
|
175
|
+
if (options == null ? void 0 : options.finishActiveAs) {
|
|
176
|
+
for (const journey of active){
|
|
177
|
+
this.finishLifecycle(journey, options.finishActiveAs, undefined, true);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
this.activeById.clear();
|
|
181
|
+
this.removeListeners();
|
|
182
|
+
}
|
|
183
|
+
} catch (unused) {
|
|
184
|
+
this.activeById.clear();
|
|
185
|
+
this.removeListeners();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
recordCheckpoint(journey, checkpoint) {
|
|
189
|
+
if (journey.checkpoints[checkpoint] !== undefined) return;
|
|
190
|
+
const value = this.readNow();
|
|
191
|
+
if (value === undefined) {
|
|
192
|
+
var _journey;
|
|
193
|
+
(_journey = journey).timingWithheldReason || (_journey.timingWithheldReason = "missing_clock");
|
|
194
|
+
} else {
|
|
195
|
+
journey.checkpoints[checkpoint] = value;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
emitDiagnostic(input) {
|
|
199
|
+
const candidates = [
|
|
200
|
+
...this.activeById.values()
|
|
201
|
+
].filter((journey)=>journey.start.journeyKind === input.journeyKind);
|
|
202
|
+
if (candidates.length === 0) return;
|
|
203
|
+
// Bound noise by finite observation/reason pairs, never by arbitrary received IDs.
|
|
204
|
+
// State lives only as long as the active attempts; ordinary and late traffic is ignored.
|
|
205
|
+
const diagnosticKey = input.observationType + ":" + input.attributionFailureReason;
|
|
206
|
+
if (candidates.every((journey)=>journey.diagnosticKeys.has(diagnosticKey))) return;
|
|
207
|
+
for (const journey of candidates)journey.diagnosticKeys.add(diagnosticKey);
|
|
208
|
+
const observedAtMs = this.readNow();
|
|
209
|
+
const candidate = this.selectDiagnosticCandidate(candidates, observedAtMs);
|
|
210
|
+
const attributionMethod = candidates.length === 0 ? "none" : candidates.length === 1 ? "single_active" : candidate ? "nearest_preceding" : "none";
|
|
211
|
+
const durationMs = candidate && !candidate.timingWithheldReason ? elapsed(candidate.startedAtMs, observedAtMs) : undefined;
|
|
212
|
+
const record = _extends({
|
|
213
|
+
journeyKind: input.journeyKind,
|
|
214
|
+
observationType: input.observationType,
|
|
215
|
+
attributionMethod,
|
|
216
|
+
attributionFailureReason: input.attributionFailureReason,
|
|
217
|
+
candidateCount: candidates.length
|
|
218
|
+
}, candidate ? {
|
|
219
|
+
candidateStart: candidate.start
|
|
220
|
+
} : {}, isJourneyId(input.receivedJourneyId) ? {
|
|
221
|
+
receivedJourneyId: input.receivedJourneyId
|
|
222
|
+
} : {}, input.resultType ? {
|
|
223
|
+
resultType: input.resultType
|
|
224
|
+
} : {}, input.outcome ? {
|
|
225
|
+
outcome: input.outcome
|
|
226
|
+
} : {}, input.uiSurface ? {
|
|
227
|
+
uiSurface: input.uiSurface
|
|
228
|
+
} : {}, input.bookerResultDetail ? {
|
|
229
|
+
bookerResultDetail: input.bookerResultDetail
|
|
230
|
+
} : {}, durationMs !== undefined ? {
|
|
231
|
+
durationMs
|
|
232
|
+
} : {});
|
|
233
|
+
try {
|
|
234
|
+
this.transport.emitDiagnostic(record);
|
|
235
|
+
} catch (unused) {
|
|
236
|
+
// Telemetry cannot affect product behavior.
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
selectDiagnosticCandidate(candidates, observedAtMs) {
|
|
240
|
+
if (candidates.length === 1) return candidates[0];
|
|
241
|
+
if (observedAtMs === undefined) return undefined;
|
|
242
|
+
return candidates.filter((candidate)=>candidate.startedAtMs !== undefined && candidate.startedAtMs <= observedAtMs).sort((left, right)=>{
|
|
243
|
+
var _right_startedAtMs, _left_startedAtMs;
|
|
244
|
+
return ((_right_startedAtMs = right.startedAtMs) != null ? _right_startedAtMs : 0) - ((_left_startedAtMs = left.startedAtMs) != null ? _left_startedAtMs : 0);
|
|
245
|
+
})[0];
|
|
246
|
+
}
|
|
247
|
+
finishLifecycle(journey, outcome, resultType, fireAndForget) {
|
|
248
|
+
if (fireAndForget === void 0) fireAndForget = false;
|
|
249
|
+
this.finish(journey, outcome, resultType, undefined, fireAndForget);
|
|
250
|
+
}
|
|
251
|
+
finish(journey, outcome, resultType, ready, fireAndForget) {
|
|
252
|
+
if (fireAndForget === void 0) fireAndForget = false;
|
|
253
|
+
var _journey_expectedResultType;
|
|
254
|
+
const { journeyId } = journey.start;
|
|
255
|
+
if (!this.activeById.has(journeyId)) return;
|
|
256
|
+
const finishedAtMs = this.readNow();
|
|
257
|
+
const timings = this.timings(journey, finishedAtMs, ready !== undefined);
|
|
258
|
+
const terminalResultType = (_journey_expectedResultType = journey.expectedResultType) != null ? _journey_expectedResultType : resultType;
|
|
259
|
+
const record = _extends({
|
|
260
|
+
start: journey.start,
|
|
261
|
+
outcome,
|
|
262
|
+
correlationQuality: journey.correlationQuality
|
|
263
|
+
}, terminalResultType ? {
|
|
264
|
+
resultType: terminalResultType
|
|
265
|
+
} : {}, (ready == null ? void 0 : ready.uiSurface) ? {
|
|
266
|
+
uiSurface: ready.uiSurface
|
|
267
|
+
} : {}, (ready == null ? void 0 : ready.bookerResultDetail) ? {
|
|
268
|
+
bookerResultDetail: ready.bookerResultDetail
|
|
269
|
+
} : {}, timings);
|
|
270
|
+
// Invalid runtime input must not consume an attempt before a valid callback can finish it.
|
|
271
|
+
if (!isValidJourneyTerminal(record)) return;
|
|
272
|
+
this.activeById.delete(journeyId);
|
|
273
|
+
if (this.activeById.size === 0) this.removeListeners();
|
|
274
|
+
try {
|
|
275
|
+
this.transport.emitTerminal(record, fireAndForget ? {
|
|
276
|
+
fireAndForget: true
|
|
277
|
+
} : undefined);
|
|
278
|
+
} catch (unused) {
|
|
279
|
+
// State is already terminal even when transport is unavailable.
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
timings(journey, finishedAtMs, rendererReady) {
|
|
283
|
+
// Construct a candidate record without mutating active state: validation may still reject it.
|
|
284
|
+
if (journey.timingWithheldReason) return {
|
|
285
|
+
timingWithheldReason: journey.timingWithheldReason
|
|
286
|
+
};
|
|
287
|
+
if (finishedAtMs === undefined) return {
|
|
288
|
+
timingWithheldReason: "missing_clock"
|
|
289
|
+
};
|
|
290
|
+
const { startedAtMs } = journey;
|
|
291
|
+
const request = journey.checkpoints.request_dispatched;
|
|
292
|
+
const response = journey.checkpoints.result_received;
|
|
293
|
+
const milestones = [
|
|
294
|
+
startedAtMs,
|
|
295
|
+
journey.checkpoints.provider_pending,
|
|
296
|
+
journey.checkpoints.provider_completed,
|
|
297
|
+
request,
|
|
298
|
+
journey.checkpoints.acknowledgement_received,
|
|
299
|
+
response,
|
|
300
|
+
journey.checkpoints.rendering,
|
|
301
|
+
finishedAtMs
|
|
302
|
+
].filter((milestone)=>milestone !== undefined);
|
|
303
|
+
const hasInvalidOrder = milestones.some((milestone, index)=>{
|
|
304
|
+
const previous = milestones[index - 1];
|
|
305
|
+
return previous !== undefined && milestone < previous;
|
|
306
|
+
});
|
|
307
|
+
if (hasInvalidOrder) {
|
|
308
|
+
return {
|
|
309
|
+
timingWithheldReason: "invalid_clock"
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const durationMs = elapsed(startedAtMs, finishedAtMs);
|
|
313
|
+
if (durationMs === undefined) {
|
|
314
|
+
return {
|
|
315
|
+
timingWithheldReason: "invalid_clock"
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
return _extends({
|
|
319
|
+
durationMs
|
|
320
|
+
}, elapsed(startedAtMs, request) !== undefined ? {
|
|
321
|
+
preRequestMs: elapsed(startedAtMs, request)
|
|
322
|
+
} : {}, elapsed(request, response) !== undefined ? {
|
|
323
|
+
requestToResponseMs: elapsed(request, response)
|
|
324
|
+
} : {}, rendererReady && elapsed(response, finishedAtMs) !== undefined ? {
|
|
325
|
+
responseToReadyMs: elapsed(response, finishedAtMs)
|
|
326
|
+
} : {});
|
|
327
|
+
}
|
|
328
|
+
readNow() {
|
|
329
|
+
try {
|
|
330
|
+
const value = this.now();
|
|
331
|
+
return Number.isFinite(value) ? value : undefined;
|
|
332
|
+
} catch (unused) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
isHidden() {
|
|
337
|
+
try {
|
|
338
|
+
return typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
339
|
+
} catch (unused) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
ensureListeners() {
|
|
344
|
+
if (this.listening || typeof document === "undefined" || typeof window === "undefined") return;
|
|
345
|
+
__nativeDocumentAddEventListener("visibilitychange", this.handleVisibilityChange);
|
|
346
|
+
__nativeWindowAddEventListener("pagehide", this.handlePageHide);
|
|
347
|
+
this.listening = true;
|
|
348
|
+
}
|
|
349
|
+
removeListeners() {
|
|
350
|
+
if (!this.listening || typeof document === "undefined" || typeof window === "undefined") return;
|
|
351
|
+
__nativeDocumentRemoveEventListener("visibilitychange", this.handleVisibilityChange);
|
|
352
|
+
__nativeWindowRemoveEventListener("pagehide", this.handlePageHide);
|
|
353
|
+
this.listening = false;
|
|
354
|
+
}
|
|
355
|
+
constructor({ transport, now = ()=>performance.now(), createId = ()=>crypto.randomUUID() }){
|
|
356
|
+
this.activeById = new Map();
|
|
357
|
+
this.listening = false;
|
|
358
|
+
this.handleVisibilityChange = ()=>{
|
|
359
|
+
try {
|
|
360
|
+
if (!this.isHidden()) return;
|
|
361
|
+
for (const journey of this.activeById.values()){
|
|
362
|
+
var _journey;
|
|
363
|
+
(_journey = journey).timingWithheldReason || (_journey.timingWithheldReason = "backgrounded");
|
|
364
|
+
}
|
|
365
|
+
} catch (unused) {
|
|
366
|
+
// Telemetry cannot affect product behavior.
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
this.handlePageHide = (event)=>{
|
|
370
|
+
try {
|
|
371
|
+
if (event.persisted) return;
|
|
372
|
+
for (const journey of [
|
|
373
|
+
...this.activeById.values()
|
|
374
|
+
]){
|
|
375
|
+
this.finishLifecycle(journey, "abandoned", undefined, true);
|
|
376
|
+
}
|
|
377
|
+
} catch (unused) {
|
|
378
|
+
// Telemetry cannot affect product behavior.
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
this.transport = transport;
|
|
382
|
+
this.now = now;
|
|
383
|
+
this.createId = createId;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export { ClientExperienceJourneyTracker };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { _extends } from "../../js/4101.js";
|
|
2
|
+
import { isValidJourneyTerminal, isValidJourneyStart } from "./schema.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const START_EVENT = "client_experience_journey_started";
|
|
7
|
+
const TERMINAL_EVENT = "client_experience_journey_terminal";
|
|
8
|
+
const DIAGNOSTIC_EVENT = "client_experience_journey_diagnostic";
|
|
9
|
+
function startExtra(start) {
|
|
10
|
+
return {
|
|
11
|
+
schema_version: (/* inlined export .JOURNEY_SCHEMA_VERSION */1),
|
|
12
|
+
journey_id: start.journeyId,
|
|
13
|
+
journey_kind: start.journeyKind,
|
|
14
|
+
entry_path: start.entryPath,
|
|
15
|
+
start_anchor: start.startAnchor,
|
|
16
|
+
navigation_mode: start.navigationMode
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function terminalExtra(record) {
|
|
20
|
+
return _extends({}, startExtra(record.start), {
|
|
21
|
+
outcome: record.outcome,
|
|
22
|
+
correlation_quality: record.correlationQuality
|
|
23
|
+
}, record.resultType ? {
|
|
24
|
+
result_type: record.resultType
|
|
25
|
+
} : {}, record.uiSurface ? {
|
|
26
|
+
ui_surface: record.uiSurface
|
|
27
|
+
} : {}, record.bookerResultDetail ? {
|
|
28
|
+
booker_result_detail: record.bookerResultDetail
|
|
29
|
+
} : {}, record.durationMs !== undefined ? {
|
|
30
|
+
duration_ms: record.durationMs
|
|
31
|
+
} : {}, record.preRequestMs !== undefined ? {
|
|
32
|
+
pre_request_ms: record.preRequestMs
|
|
33
|
+
} : {}, record.requestToResponseMs !== undefined ? {
|
|
34
|
+
request_to_response_ms: record.requestToResponseMs
|
|
35
|
+
} : {}, record.responseToReadyMs !== undefined ? {
|
|
36
|
+
response_to_ready_ms: record.responseToReadyMs
|
|
37
|
+
} : {}, record.timingWithheldReason ? {
|
|
38
|
+
timing_withheld_reason: record.timingWithheldReason
|
|
39
|
+
} : {});
|
|
40
|
+
}
|
|
41
|
+
function diagnosticExtra(record) {
|
|
42
|
+
const candidate = record.candidateStart;
|
|
43
|
+
return _extends({
|
|
44
|
+
schema_version: (/* inlined export .JOURNEY_SCHEMA_VERSION */1),
|
|
45
|
+
journey_kind: record.journeyKind,
|
|
46
|
+
observation_type: record.observationType,
|
|
47
|
+
has_journey_id: false,
|
|
48
|
+
attribution_method: record.attributionMethod,
|
|
49
|
+
attribution_failure_reason: record.attributionFailureReason,
|
|
50
|
+
candidate_count: record.candidateCount,
|
|
51
|
+
metric_eligible: false
|
|
52
|
+
}, candidate ? {
|
|
53
|
+
candidate_journey_id: candidate.journeyId
|
|
54
|
+
} : {}, record.receivedJourneyId ? {
|
|
55
|
+
received_journey_id: record.receivedJourneyId
|
|
56
|
+
} : {}, candidate ? {
|
|
57
|
+
entry_path: candidate.entryPath,
|
|
58
|
+
start_anchor: candidate.startAnchor,
|
|
59
|
+
navigation_mode: candidate.navigationMode
|
|
60
|
+
} : {}, record.resultType ? {
|
|
61
|
+
result_type: record.resultType
|
|
62
|
+
} : {}, record.outcome ? {
|
|
63
|
+
outcome: record.outcome
|
|
64
|
+
} : {}, record.uiSurface ? {
|
|
65
|
+
ui_surface: record.uiSurface
|
|
66
|
+
} : {}, record.bookerResultDetail ? {
|
|
67
|
+
booker_result_detail: record.bookerResultDetail
|
|
68
|
+
} : {}, record.durationMs !== undefined ? {
|
|
69
|
+
duration_ms: record.durationMs
|
|
70
|
+
} : {});
|
|
71
|
+
}
|
|
72
|
+
function createJourneyTransport(param) {
|
|
73
|
+
let { log } = param;
|
|
74
|
+
return {
|
|
75
|
+
emitStart (start) {
|
|
76
|
+
if (!isValidJourneyStart(start)) return;
|
|
77
|
+
log({
|
|
78
|
+
type: "log",
|
|
79
|
+
message: START_EVENT,
|
|
80
|
+
extra: startExtra(start)
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
emitTerminal (record, options) {
|
|
84
|
+
if (!isValidJourneyTerminal(record)) return;
|
|
85
|
+
log(_extends({
|
|
86
|
+
type: "log",
|
|
87
|
+
message: TERMINAL_EVENT,
|
|
88
|
+
extra: terminalExtra(record)
|
|
89
|
+
}, (options == null ? void 0 : options.fireAndForget) ? {
|
|
90
|
+
fireAndForget: true,
|
|
91
|
+
flushImmediately: true
|
|
92
|
+
} : {}));
|
|
93
|
+
},
|
|
94
|
+
emitDiagnostic (record) {
|
|
95
|
+
log({
|
|
96
|
+
type: "log",
|
|
97
|
+
message: DIAGNOSTIC_EVENT,
|
|
98
|
+
extra: diagnosticExtra(record)
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { createJourneyTransport };
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
askAnythingAbout: "Stellen Sie eine Frage zu {{company}}",
|
|
147
147
|
weakConnection: "Schwache Verbindung. Neuverbindung wird hergestellt…",
|
|
148
148
|
aiAssistantAvatar: "{{name}} Avatar",
|
|
149
|
-
yourAssistant: "
|
|
149
|
+
yourAssistant: "Ihrem Assistenten",
|
|
150
150
|
videoMode: "Video",
|
|
151
151
|
voiceMode: "Sprache",
|
|
152
152
|
videoModeAriaLabel: "Videomodus",
|
package/widget/server-logger.js
CHANGED
|
@@ -72,7 +72,7 @@ class ServerLogger {
|
|
|
72
72
|
* // Fire-and-forget for page unload
|
|
73
73
|
* serverLog({ message: "Page closing", fireAndForget: true });
|
|
74
74
|
*/ serverLog(param) {
|
|
75
|
-
let { type = "log", message: event, extra = {}, fireAndForget = false } = param;
|
|
75
|
+
let { type = "log", message: event, extra = {}, fireAndForget = false, flushImmediately = false } = param;
|
|
76
76
|
var _class_private_field_loose_base__config_featureFlags;
|
|
77
77
|
if (!_class_private_field_loose_base(this, _config)[_config].host || !_class_private_field_loose_base(this, _config)[_config].token) {
|
|
78
78
|
return;
|
|
@@ -84,6 +84,9 @@ class ServerLogger {
|
|
|
84
84
|
extra,
|
|
85
85
|
fireAndForget
|
|
86
86
|
});
|
|
87
|
+
if (flushImmediately) this.flushBatch({
|
|
88
|
+
fireAndForget
|
|
89
|
+
});
|
|
87
90
|
return;
|
|
88
91
|
}
|
|
89
92
|
this.sendEvent("visitor_events", {
|
package/widget/tracker.js
CHANGED
|
@@ -14,12 +14,14 @@ import { default as data360_visitor } from "./data360_visitor.js";
|
|
|
14
14
|
import { default as eloqua_visitor } from "./eloqua_visitor.js";
|
|
15
15
|
import { armFormCtaBookerIntent } from "./form_cta_booker_intent.js";
|
|
16
16
|
import { default as hubspot_visitor } from "./hubspot_visitor.js";
|
|
17
|
+
import { ClientExperienceJourneyTracker } from "./journey/tracker.js";
|
|
17
18
|
import { default as marketo_visitor } from "./marketo_visitor.js";
|
|
18
19
|
import { ButtonsFormsTriggeringTracer } from "./meetings/tracing/buttons_forms_triggering_tracer.js";
|
|
19
20
|
import { default as pardot_visitor } from "./pardot_visitor.js";
|
|
20
21
|
import { default as personalizations_config } from "./personalizations_config.js";
|
|
21
22
|
import { default as smart_website_config } from "./smart_website_config.js";
|
|
22
23
|
import "./telemetry/index.js";
|
|
24
|
+
import { createJourneyTransport } from "./journey/transport.js";
|
|
23
25
|
import { inject } from "../src/mixins/injectable.js";
|
|
24
26
|
import { ignoreIfDisposed } from "../src/mixins/disposable.js";
|
|
25
27
|
import { telemetry, withTelemetryCtx, getTelemetryCtx } from "./telemetry/telemetry.js";
|
|
@@ -54,6 +56,8 @@ var throttle_default = /*#__PURE__*/__webpack_require__.n(throttle);
|
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
|
|
59
|
+
|
|
60
|
+
|
|
57
61
|
|
|
58
62
|
|
|
59
63
|
|
|
@@ -852,6 +856,18 @@ class Tracker extends WidgetObject {
|
|
|
852
856
|
// AI Nudge
|
|
853
857
|
this.timerStartedAt = null;
|
|
854
858
|
this.timerTimeOnSite = 0;
|
|
859
|
+
this.clientExperienceJourneys = this.featureFlags.clientExperienceJourneyTelemetry ? new ClientExperienceJourneyTracker({
|
|
860
|
+
createId: esm_browser_v4,
|
|
861
|
+
transport: createJourneyTransport({
|
|
862
|
+
log: (params)=>this.widget.serverLog(params)
|
|
863
|
+
})
|
|
864
|
+
}) : undefined;
|
|
865
|
+
this.callOnDispose(()=>{
|
|
866
|
+
var _this_clientExperienceJourneys;
|
|
867
|
+
return (_this_clientExperienceJourneys = this.clientExperienceJourneys) == null ? void 0 : _this_clientExperienceJourneys.dispose({
|
|
868
|
+
finishActiveAs: "superseded"
|
|
869
|
+
});
|
|
870
|
+
});
|
|
855
871
|
if (this.config.smartCtaTriggers) {
|
|
856
872
|
this.smartWebsiteConfig = new smart_website_config({
|
|
857
873
|
smartCtaTriggers: this.config.smartCtaTriggers
|
package/widget/widget/widget.js
CHANGED
|
@@ -947,14 +947,16 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
947
947
|
console.warn("Error showing debug banner!");
|
|
948
948
|
}
|
|
949
949
|
}
|
|
950
|
-
const _this = this;
|
|
951
950
|
if (typeof window.MktoForms2 !== "undefined") {
|
|
952
951
|
this.debugState.logEvent("widget:marketo:MktoForms2Found", "success");
|
|
953
952
|
this.initMarketoForms();
|
|
954
953
|
} else if (Object.hasOwn(window, "MktoForms2")) {
|
|
955
|
-
|
|
954
|
+
// The trap is already installed from a previous initialization. Update the dispatch target
|
|
955
|
+
// so the setter calls the current widget instead of a potentially disposed one.
|
|
956
|
+
window.__qualifiedMarketoTarget = this;
|
|
956
957
|
} else {
|
|
957
958
|
this.debugState.logEvent("widget:marketo:MktoForms2Found", "error");
|
|
959
|
+
window.__qualifiedMarketoTarget = this;
|
|
958
960
|
let _MktoForms2;
|
|
959
961
|
Object.defineProperty(window, "MktoForms2", {
|
|
960
962
|
get () {
|
|
@@ -962,11 +964,12 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
962
964
|
},
|
|
963
965
|
set (value) {
|
|
964
966
|
_MktoForms2 = value;
|
|
967
|
+
const target = window.__qualifiedMarketoTarget;
|
|
965
968
|
widget_setTimeout(()=>{
|
|
966
969
|
try {
|
|
967
|
-
|
|
970
|
+
target == null ? void 0 : target.initMarketoForms();
|
|
968
971
|
} catch (error) {
|
|
969
|
-
|
|
972
|
+
target == null ? void 0 : target.debugState.logEvent("widget:marketo:initMarketoFormsComplete", "error", {
|
|
970
973
|
error
|
|
971
974
|
});
|
|
972
975
|
}
|
|
@@ -989,9 +992,12 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
989
992
|
this.debugState.logEvent("widget:hubspot:hbsptFound", "success");
|
|
990
993
|
this.initHubspotForms(contentWindow);
|
|
991
994
|
} else if (Object.hasOwn(contentWindow, "hbspt")) {
|
|
992
|
-
|
|
995
|
+
// The trap is already installed. Update the dispatch target so the setter calls the
|
|
996
|
+
// current widget instead of a potentially disposed one.
|
|
997
|
+
contentWindow.__qualifiedHubspotTarget = this;
|
|
993
998
|
} else {
|
|
994
999
|
this.debugState.logEvent("widget:hubspot:hbsptFound", "error");
|
|
1000
|
+
contentWindow.__qualifiedHubspotTarget = this;
|
|
995
1001
|
let _hbspt;
|
|
996
1002
|
Object.defineProperty(contentWindow, "hbspt", {
|
|
997
1003
|
get () {
|
|
@@ -999,11 +1005,12 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
999
1005
|
},
|
|
1000
1006
|
set (value) {
|
|
1001
1007
|
_hbspt = value;
|
|
1008
|
+
const target = contentWindow.__qualifiedHubspotTarget;
|
|
1002
1009
|
widget_setTimeout(()=>{
|
|
1003
1010
|
try {
|
|
1004
|
-
|
|
1011
|
+
target == null ? void 0 : target.initHubspotForms(contentWindow);
|
|
1005
1012
|
} catch (error) {
|
|
1006
|
-
|
|
1013
|
+
target == null ? void 0 : target.debugState.logEvent("widget:hubspot:initHubspotFormsComplete", "error", {
|
|
1007
1014
|
error
|
|
1008
1015
|
});
|
|
1009
1016
|
}
|
|
@@ -1034,15 +1041,15 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
1034
1041
|
if (typeof contentWindow.HubSpotFormsV4 !== "undefined") {
|
|
1035
1042
|
widget_clearInterval(pollId);
|
|
1036
1043
|
try {
|
|
1037
|
-
|
|
1044
|
+
this.initHubspotFormsV4(contentWindow);
|
|
1038
1045
|
} catch (error) {
|
|
1039
|
-
|
|
1046
|
+
this.debugState.logEvent("widget:hubspotV4:initComplete", "error", {
|
|
1040
1047
|
error
|
|
1041
1048
|
});
|
|
1042
1049
|
}
|
|
1043
1050
|
} else if (elapsed >= MAX_POLL_TIME_MS) {
|
|
1044
1051
|
widget_clearInterval(pollId);
|
|
1045
|
-
|
|
1052
|
+
this.debugState.logEvent("widget:hubspotV4:pollTimeout", "error");
|
|
1046
1053
|
}
|
|
1047
1054
|
}, POLL_INTERVAL_MS);
|
|
1048
1055
|
this.hubspotV4PollIds.push(pollId);
|
|
@@ -1081,12 +1088,17 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
1081
1088
|
this.debugState.logEvent("widget:marketo:MktoForms2Found", "error", {}, '"window.MktoForms" was previously defined, but is no longer present. This website may have executed javascript which is interfering with the Marketo Smart Form initialization process.');
|
|
1082
1089
|
return;
|
|
1083
1090
|
}
|
|
1091
|
+
// Marketo has no API to unregister whenReady/onSubmit/onSuccess callbacks, so after a teardown
|
|
1092
|
+
// they outlive this instance (and whenReady replays for forms that are already ready). Each
|
|
1093
|
+
// callback bails once this instance is disposed so only the live widget reacts to the form.
|
|
1084
1094
|
window.MktoForms2.whenReady((mktoForm)=>{
|
|
1095
|
+
if (this.isDisposed) return;
|
|
1085
1096
|
if (!this.tracker.config.formBotTriggers.marketo.includes(String(mktoForm.getId()))) return;
|
|
1086
1097
|
this.debugState.logEvent("widget:marketo:configuredFormFound", "success", {
|
|
1087
1098
|
formId: mktoForm.getId()
|
|
1088
1099
|
});
|
|
1089
1100
|
mktoForm.onSubmit((form)=>{
|
|
1101
|
+
if (this.isDisposed) return;
|
|
1090
1102
|
this.initMarketoFormsRedirectListeners();
|
|
1091
1103
|
const values = form.getValues();
|
|
1092
1104
|
const formId = values.formid;
|
|
@@ -1100,6 +1112,7 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
1100
1112
|
});
|
|
1101
1113
|
});
|
|
1102
1114
|
mktoForm.onSuccess((values, followUpUrl)=>{
|
|
1115
|
+
if (this.isDisposed) return;
|
|
1103
1116
|
this.marketoFormSucceededAt = Date.now();
|
|
1104
1117
|
const formId = values.formid;
|
|
1105
1118
|
const formEmail = values.Email;
|
|
@@ -1121,7 +1134,7 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
1121
1134
|
formId
|
|
1122
1135
|
});
|
|
1123
1136
|
widget_setTimeout(()=>{
|
|
1124
|
-
if (this.marketoFormRedirecting) return;
|
|
1137
|
+
if (this.isDisposed || this.marketoFormRedirecting) return;
|
|
1125
1138
|
this.debugState.logEvent("widget:marketo:showExperienceForForm", "success", {
|
|
1126
1139
|
formId
|
|
1127
1140
|
});
|
|
@@ -1191,14 +1204,17 @@ class Widget extends Injectable(Disposable(Object)) {
|
|
|
1191
1204
|
var _event_data;
|
|
1192
1205
|
if (event.origin !== window.location.origin || ((_event_data = event.data) == null ? void 0 : _event_data.type) !== "hsFormCallback" || !this.tracker.config.formBotTriggers.hubspot.includes(event.data.id)) return;
|
|
1193
1206
|
if (event.data.eventName === "onFormSubmit") {
|
|
1207
|
+
var _ref;
|
|
1208
|
+
var _email_;
|
|
1194
1209
|
const email = event.data.data.filter((field)=>field.name === "email");
|
|
1195
1210
|
values = event.data.data.reduce((acc, field)=>{
|
|
1196
1211
|
acc[field.name] = field.value;
|
|
1197
1212
|
return acc;
|
|
1198
1213
|
}, {});
|
|
1199
1214
|
const lengthCheckedValues = this.excludeLongFieldValue(values, "hubspot");
|
|
1200
|
-
|
|
1201
|
-
|
|
1215
|
+
// Reset per submission so onFormSubmitted never reuses an earlier submission's email.
|
|
1216
|
+
formEmail = (_ref = (_email_ = email[0]) == null ? void 0 : _email_.value) != null ? _ref : null;
|
|
1217
|
+
if (formEmail) {
|
|
1202
1218
|
this.visitor.setFormExperienceDataWithExpiry({
|
|
1203
1219
|
formType: "hubspot",
|
|
1204
1220
|
formData: _extends({}, lengthCheckedValues, {
|