@adobe/alloy 2.29.0-beta.5 → 2.30.0-beta.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.
Files changed (177) hide show
  1. package/libEs5/components/ActivityCollector/utils/dom/findClickableElement.js +4 -0
  2. package/libEs5/components/Advertising/handlers/clickThroughHandler.js +2 -1
  3. package/libEs5/components/Advertising/handlers/onBeforeSendEventHandler.js +8 -3
  4. package/libEs5/components/Advertising/handlers/viewThroughHandler.js +2 -1
  5. package/libEs5/components/Advertising/identities/collectID5Id.js +1 -1
  6. package/libEs5/components/Advertising/identities/collectRampId.js +1 -1
  7. package/libEs5/components/Consent/createConsentRequestPayload.js +14 -3
  8. package/libEs5/components/Consent/types.js +18 -0
  9. package/libEs5/components/Personalization/dom-actions/action.js +7 -4
  10. package/libEs5/components/Personalization/dom-actions/initDomActionsModules.js +10 -10
  11. package/libEs5/components/PushNotifications/helpers/constants.js +19 -0
  12. package/libEs5/components/PushNotifications/helpers/getPushSubscriptionDetails.js +116 -0
  13. package/libEs5/components/PushNotifications/helpers/readFromIndexedDb.js +41 -0
  14. package/libEs5/components/PushNotifications/helpers/saveToIndexedDb.js +50 -0
  15. package/libEs5/components/PushNotifications/helpers/serviceWorkerNotificationClickListener.js +85 -0
  16. package/libEs5/components/PushNotifications/helpers/serviceWorkerPushListener.js +70 -0
  17. package/libEs5/components/PushNotifications/index.js +129 -0
  18. package/libEs5/components/PushNotifications/request/createSendPushSubscriptionPayload.js +62 -0
  19. package/libEs5/components/PushNotifications/request/createSendPushSubscriptionRequest.js +35 -0
  20. package/libEs5/components/PushNotifications/request/makeSendPushSubscriptionRequest.js +93 -0
  21. package/libEs5/components/PushNotifications/request/makeSendServiceWorkerTrackingData.js +146 -0
  22. package/libEs5/components/PushNotifications/serviceWorker.js +91 -0
  23. package/libEs5/components/PushNotifications/types.js +95 -0
  24. package/libEs5/constants/libraryVersion.js +1 -1
  25. package/libEs5/core/componentCreators.js +8 -1
  26. package/libEs5/core/consent/createConsent.js +10 -0
  27. package/libEs5/core/consent/createConsentStateMachine.js +36 -0
  28. package/libEs5/core/consent/types.js +24 -0
  29. package/libEs5/core/edgeNetwork/injectSendEdgeNetworkRequest.js +20 -0
  30. package/libEs5/core/edgeNetwork/types.js +10 -0
  31. package/libEs5/core/identity/createIdentity.js +36 -0
  32. package/libEs5/core/identity/types.js +15 -0
  33. package/libEs5/core/injectCreateResponse.js +9 -7
  34. package/libEs5/core/types.js +101 -11
  35. package/libEs5/utils/bytes.js +12 -1
  36. package/libEs5/utils/createLoggingCookieJar.js +14 -1
  37. package/libEs5/utils/createMerger.js +5 -4
  38. package/libEs5/utils/index.js +19 -0
  39. package/libEs5/utils/indexedDb.js +73 -0
  40. package/libEs5/utils/injectStorage.js +19 -0
  41. package/libEs5/utils/request/createDataCollectionRequestPayload.js +16 -8
  42. package/libEs5/utils/request/createRequest.js +28 -1
  43. package/libEs5/utils/request/createRequestPayload.js +15 -2
  44. package/libEs5/utils/request/types.js +52 -0
  45. package/libEs5/utils/types.js +39 -0
  46. package/libEs6/components/ActivityCollector/utils/dom/findClickableElement.js +4 -0
  47. package/libEs6/components/Advertising/handlers/clickThroughHandler.js +2 -1
  48. package/libEs6/components/Advertising/handlers/onBeforeSendEventHandler.js +8 -3
  49. package/libEs6/components/Advertising/handlers/viewThroughHandler.js +2 -1
  50. package/libEs6/components/Advertising/identities/collectID5Id.js +1 -1
  51. package/libEs6/components/Advertising/identities/collectRampId.js +1 -1
  52. package/libEs6/components/Consent/createConsentRequestPayload.js +16 -3
  53. package/libEs6/components/Consent/types.js +15 -0
  54. package/libEs6/components/Personalization/dom-actions/action.js +7 -4
  55. package/libEs6/components/Personalization/dom-actions/initDomActionsModules.js +10 -10
  56. package/libEs6/components/PushNotifications/helpers/constants.js +16 -0
  57. package/libEs6/components/PushNotifications/helpers/getPushSubscriptionDetails.js +114 -0
  58. package/libEs6/components/PushNotifications/helpers/readFromIndexedDb.js +40 -0
  59. package/libEs6/components/PushNotifications/helpers/saveToIndexedDb.js +48 -0
  60. package/libEs6/components/PushNotifications/helpers/serviceWorkerNotificationClickListener.js +82 -0
  61. package/libEs6/components/PushNotifications/helpers/serviceWorkerPushListener.js +69 -0
  62. package/libEs6/components/PushNotifications/index.js +126 -0
  63. package/libEs6/components/PushNotifications/request/createSendPushSubscriptionPayload.js +61 -0
  64. package/libEs6/components/PushNotifications/request/createSendPushSubscriptionRequest.js +34 -0
  65. package/libEs6/components/PushNotifications/request/makeSendPushSubscriptionRequest.js +89 -0
  66. package/libEs6/components/PushNotifications/request/makeSendServiceWorkerTrackingData.js +146 -0
  67. package/libEs6/components/PushNotifications/serviceWorker.js +90 -0
  68. package/libEs6/components/PushNotifications/types.js +92 -0
  69. package/libEs6/constants/libraryVersion.js +1 -1
  70. package/libEs6/core/componentCreators.js +2 -1
  71. package/libEs6/core/consent/createConsent.js +12 -0
  72. package/libEs6/core/consent/createConsentStateMachine.js +36 -0
  73. package/libEs6/core/consent/types.js +21 -0
  74. package/libEs6/core/edgeNetwork/injectSendEdgeNetworkRequest.js +20 -0
  75. package/libEs6/core/edgeNetwork/types.js +7 -0
  76. package/libEs6/core/identity/createIdentity.js +38 -0
  77. package/libEs6/core/identity/types.js +12 -0
  78. package/libEs6/core/injectCreateResponse.js +11 -7
  79. package/libEs6/core/types.js +101 -11
  80. package/libEs6/utils/bytes.js +12 -1
  81. package/libEs6/utils/createLoggingCookieJar.js +15 -1
  82. package/libEs6/utils/createMerger.js +5 -4
  83. package/libEs6/utils/index.js +1 -0
  84. package/libEs6/utils/indexedDb.js +67 -0
  85. package/libEs6/utils/injectStorage.js +20 -0
  86. package/libEs6/utils/request/createDataCollectionRequestPayload.js +19 -8
  87. package/libEs6/utils/request/createRequest.js +29 -1
  88. package/libEs6/utils/request/createRequestPayload.js +15 -2
  89. package/libEs6/utils/request/types.js +49 -0
  90. package/libEs6/utils/types.js +36 -0
  91. package/package.json +45 -52
  92. package/scripts/alloyBuilder.js +130 -4
  93. package/types/components/ActivityCollector/utils/dom/findClickableElement.d.ts.map +1 -1
  94. package/types/components/Advertising/handlers/onBeforeSendEventHandler.d.ts.map +1 -1
  95. package/types/components/Advertising/handlers/viewThroughHandler.d.ts.map +1 -1
  96. package/types/components/Consent/createConsentRequest.d.ts +1 -11
  97. package/types/components/Consent/createConsentRequest.d.ts.map +1 -1
  98. package/types/components/Consent/createConsentRequestPayload.d.ts +2 -9
  99. package/types/components/Consent/createConsentRequestPayload.d.ts.map +1 -1
  100. package/types/components/Consent/types.d.ts +28 -0
  101. package/types/components/Consent/types.d.ts.map +1 -0
  102. package/types/components/Identity/getIdentity/createIdentityRequest.d.ts +1 -11
  103. package/types/components/Identity/getIdentity/createIdentityRequest.d.ts.map +1 -1
  104. package/types/components/Identity/getIdentity/createIdentityRequestPayload.d.ts +1 -9
  105. package/types/components/Identity/getIdentity/createIdentityRequestPayload.d.ts.map +1 -1
  106. package/types/components/Personalization/dom-actions/action.d.ts +1 -1
  107. package/types/components/Personalization/dom-actions/action.d.ts.map +1 -1
  108. package/types/components/PushNotifications/helpers/constants.d.ts +5 -0
  109. package/types/components/PushNotifications/helpers/constants.d.ts.map +1 -0
  110. package/types/components/PushNotifications/helpers/getPushSubscriptionDetails.d.ts +30 -0
  111. package/types/components/PushNotifications/helpers/getPushSubscriptionDetails.d.ts.map +1 -0
  112. package/types/components/PushNotifications/helpers/readFromIndexedDb.d.ts +4 -0
  113. package/types/components/PushNotifications/helpers/readFromIndexedDb.d.ts.map +1 -0
  114. package/types/components/PushNotifications/helpers/saveToIndexedDb.d.ts +9 -0
  115. package/types/components/PushNotifications/helpers/saveToIndexedDb.d.ts.map +1 -0
  116. package/types/components/PushNotifications/helpers/serviceWorkerNotificationClickListener.d.ts +9 -0
  117. package/types/components/PushNotifications/helpers/serviceWorkerNotificationClickListener.d.ts.map +1 -0
  118. package/types/components/PushNotifications/helpers/serviceWorkerPushListener.d.ts +8 -0
  119. package/types/components/PushNotifications/helpers/serviceWorkerPushListener.d.ts.map +1 -0
  120. package/types/components/PushNotifications/index.d.ts +51 -0
  121. package/types/components/PushNotifications/index.d.ts.map +1 -0
  122. package/types/components/PushNotifications/request/createSendPushSubscriptionPayload.d.ts +10 -0
  123. package/types/components/PushNotifications/request/createSendPushSubscriptionPayload.d.ts.map +1 -0
  124. package/types/components/PushNotifications/request/createSendPushSubscriptionRequest.d.ts +7 -0
  125. package/types/components/PushNotifications/request/createSendPushSubscriptionRequest.d.ts.map +1 -0
  126. package/types/components/PushNotifications/request/makeSendPushSubscriptionRequest.d.ts +21 -0
  127. package/types/components/PushNotifications/request/makeSendPushSubscriptionRequest.d.ts.map +1 -0
  128. package/types/components/PushNotifications/request/makeSendServiceWorkerTrackingData.d.ts +11 -0
  129. package/types/components/PushNotifications/request/makeSendServiceWorkerTrackingData.d.ts.map +1 -0
  130. package/types/components/PushNotifications/serviceWorker.d.ts +2 -0
  131. package/types/components/PushNotifications/serviceWorker.d.ts.map +1 -0
  132. package/types/components/PushNotifications/types.d.ts +190 -0
  133. package/types/components/PushNotifications/types.d.ts.map +1 -0
  134. package/types/components/StreamingMedia/createMediaRequest.d.ts +1 -11
  135. package/types/components/StreamingMedia/createMediaRequest.d.ts.map +1 -1
  136. package/types/core/componentCreators.d.ts +1 -0
  137. package/types/core/consent/createConsent.d.ts +4 -10
  138. package/types/core/consent/createConsent.d.ts.map +1 -1
  139. package/types/core/consent/createConsentStateMachine.d.ts +4 -12
  140. package/types/core/consent/createConsentStateMachine.d.ts.map +1 -1
  141. package/types/core/consent/types.d.ts +42 -0
  142. package/types/core/consent/types.d.ts.map +1 -0
  143. package/types/core/edgeNetwork/injectSendEdgeNetworkRequest.d.ts +15 -13
  144. package/types/core/edgeNetwork/injectSendEdgeNetworkRequest.d.ts.map +1 -1
  145. package/types/core/edgeNetwork/types.d.ts +12 -0
  146. package/types/core/edgeNetwork/types.d.ts.map +1 -0
  147. package/types/core/identity/createIdentity.d.ts +9 -9
  148. package/types/core/identity/createIdentity.d.ts.map +1 -1
  149. package/types/core/identity/types.d.ts +23 -0
  150. package/types/core/identity/types.d.ts.map +1 -0
  151. package/types/core/injectCreateResponse.d.ts +3 -27
  152. package/types/core/injectCreateResponse.d.ts.map +1 -1
  153. package/types/core/types.d.ts +209 -22
  154. package/types/core/types.d.ts.map +1 -1
  155. package/types/utils/bytes.d.ts +3 -1
  156. package/types/utils/bytes.d.ts.map +1 -1
  157. package/types/utils/createLoggingCookieJar.d.ts +5 -3
  158. package/types/utils/createLoggingCookieJar.d.ts.map +1 -1
  159. package/types/utils/createMerger.d.ts +1 -1
  160. package/types/utils/createMerger.d.ts.map +1 -1
  161. package/types/utils/index.d.ts +1 -0
  162. package/types/utils/indexedDb.d.ts +4 -0
  163. package/types/utils/indexedDb.d.ts.map +1 -0
  164. package/types/utils/injectStorage.d.ts +2 -40
  165. package/types/utils/injectStorage.d.ts.map +1 -1
  166. package/types/utils/request/createDataCollectionRequest.d.ts +1 -11
  167. package/types/utils/request/createDataCollectionRequest.d.ts.map +1 -1
  168. package/types/utils/request/createDataCollectionRequestPayload.d.ts +2 -9
  169. package/types/utils/request/createDataCollectionRequestPayload.d.ts.map +1 -1
  170. package/types/utils/request/createRequest.d.ts +13 -11
  171. package/types/utils/request/createRequest.d.ts.map +1 -1
  172. package/types/utils/request/createRequestPayload.d.ts +7 -9
  173. package/types/utils/request/createRequestPayload.d.ts.map +1 -1
  174. package/types/utils/request/types.d.ts +91 -0
  175. package/types/utils/request/types.d.ts.map +1 -0
  176. package/types/utils/types.d.ts +91 -0
  177. package/types/utils/types.d.ts.map +1 -0
@@ -14,6 +14,8 @@ OF ANY KIND, either express or implied. See the License for the specific languag
14
14
  governing permissions and limitations under the License.
15
15
  */
16
16
 
17
+ /** @import { RequestPayload, Identity } from './types.js' */
18
+
17
19
  /**
18
20
  * createMerger creates a function that does a deep merge. Example:
19
21
  * ```js
@@ -64,8 +66,19 @@ const createMergeConfigOverride = (content, key) => updates => {
64
66
  Object.assign(hostObjectForUpdates, updates);
65
67
  };
66
68
 
67
- // This provides the base functionality that all types of
68
- // request payloads share.
69
+ /**
70
+ * Creates a request payload object with methods for merging different types of data.
71
+ * This provides the base functionality that all types of request payloads share.
72
+ *
73
+ * @function
74
+ *
75
+ * @param {Object} options
76
+ * @param {Object} options.content
77
+ * @param {function(string, Identity): void} options.addIdentity
78
+ * @param {function(string): boolean} options.hasIdentity
79
+ *
80
+ * @returns {RequestPayload}
81
+ */
69
82
  var _default = options => {
70
83
  const {
71
84
  content,
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ exports.Types = void 0;
4
+ /**
5
+ * Request object with methods to access and modify request properties.
6
+ *
7
+ * @typedef {Object} Request
8
+ * @property {function(): string} getId
9
+ * @property {function(): RequestPayload} getPayload
10
+ * @property {function({isIdentityEstablished: boolean}): string} getAction
11
+ * @property {function(): string|undefined} getDatastreamIdOverride
12
+ * @property {Function} getUseSendBeacon
13
+ * @property {function(): string|undefined} getEdgeSubPath
14
+ * @property {Function} getUseIdThirdPartyDomain
15
+ * @property {Function} setUseIdThirdPartyDomain
16
+ * @property {Function} setIsIdentityEstablished
17
+ */
18
+
19
+ /**
20
+ * Request payload object with methods for merging different types of data.
21
+ *
22
+ * @typedef {Object} RequestPayload
23
+ * @property {function(object): void} mergeMeta
24
+ * @property {function(object): void} mergeState
25
+ * @property {function(object): void} mergeQuery
26
+ * @property {function(object): void} mergeConfigOverride
27
+ * @property {function(string, Identity): void} options.addIdentity
28
+ * @property {function(string): boolean} options.hasIdentity
29
+ * @property {function(): object} toJSON
30
+ */
31
+
32
+ /**
33
+ * Request payload object with methods for merging different types of data.
34
+ *
35
+ * @typedef {Object} DataCollectionRequestPayload
36
+ * @property {function(object): void} mergeMeta
37
+ * @property {function(object): void} mergeState
38
+ * @property {function(object): void} mergeQuery
39
+ * @property {function(object): void} mergeConfigOverride
40
+ * @property {function(string, Identity): void} options.addIdentity
41
+ * @property {function(string): boolean} options.hasIdentity
42
+ * @property {function(object): void} addEvent
43
+ * @property {function(): boolean} getDocumentMayUnload
44
+ * @property {function(): object} toJSON
45
+ */
46
+
47
+ /**
48
+ * @typedef {Object} Identity
49
+ * @property {string} id
50
+ */
51
+
52
+ const Types = exports.Types = {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ exports.Types = void 0;
4
+ /**
5
+ * @typedef {Object} Storage
6
+ * @property {function(string): string|null} getItem
7
+ * @property {function(string, string): void} setItem
8
+ * @property {function(): void} clear
9
+ */
10
+
11
+ /**
12
+ * @typedef {Object} CookieAttributes
13
+ * @property {number|Date} [expires] - Cookie expiration (number of days or Date object)
14
+ * @property {string} [path] - Cookie path (default: "/")
15
+ * @property {string} [domain] - Cookie domain
16
+ * @property {boolean} [secure] - Requires HTTPS transmission
17
+ * @property {"strict"|"lax"|"none"} [sameSite] - SameSite attribute
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} CookieConverter
22
+ * @property {function(string, string): string} [read] - Custom decoder function
23
+ * @property {function(string, string): string} [write] - Custom encoder function
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} CookieJar
28
+ * @property {function(string): string|undefined} get - Get cookie value by name, or get all cookies as object if no name provided
29
+ * @property {function(): Object<string, string>} get - Get all cookies as key-value object when called with no arguments
30
+ * @property {function(string, string, CookieAttributes=): string|undefined} set - Set cookie with optional attributes
31
+ * @property {function(string, CookieAttributes=): void} remove - Remove cookie with optional attributes (must match set attributes)
32
+ * @property {function(CookieConverter): CookieJar} withConverter - Create new instance with custom encoding/decoding
33
+ */
34
+
35
+ /**
36
+ * @typedef {function(string): { session: Storage, persistent: Storage }} StorageCreator
37
+ */
38
+
39
+ const Types = exports.Types = {};
@@ -17,6 +17,10 @@ import isButtonSubmitElement from "./isButtonSubmitElement.js";
17
17
  export default element => {
18
18
  let node = element;
19
19
  while (node) {
20
+ // Stop looking when BODY is reached
21
+ if (node.nodeName && node.nodeName === "BODY") {
22
+ break;
23
+ }
20
24
  if (isSupportedAnchorElement(node) || elementHasClickHandler(node) || isInputSubmitElement(node) || isButtonSubmitElement(node)) {
21
25
  return node;
22
26
  }
@@ -62,7 +62,8 @@ export default async function handleClickThrough({
62
62
  }
63
63
  }
64
64
  },
65
- eventType: AD_CONVERSION_CLICK_EVENT_TYPE
65
+ eventType: AD_CONVERSION_CLICK_EVENT_TYPE,
66
+ timestamp: new Date().toISOString()
66
67
  };
67
68
  event.setUserXdm(xdm);
68
69
  cookieManager.setValue(LAST_CONVERSION_TIME_KEY);
@@ -10,11 +10,12 @@ import { getRampId } from "../identities/collectRampId.js";
10
10
  import { appendAdvertisingIdQueryToEvent, getUrlParams, isThrottled } from "../utils/helpers.js";
11
11
  import { SURFER_ID, ID5_ID, RAMP_ID } from "../constants/index.js";
12
12
  import { AUTO, WAIT } from "../../../constants/consentStatus.js";
13
+ import { CHROME } from "../../../constants/browser.js";
13
14
  const isAdvertisingDisabled = advertising => {
14
- return ![AUTO, WAIT].includes(advertising?.handleAdvertisingData);
15
+ return ![AUTO, WAIT].includes(advertising?.handleAdvertisingData?.toLowerCase());
15
16
  };
16
17
  const waitForAdvertisingId = advertising => {
17
- return advertising?.handleAdvertisingData === WAIT;
18
+ return advertising?.handleAdvertisingData?.toLowerCase() === WAIT;
18
19
  };
19
20
 
20
21
  /**
@@ -44,7 +45,11 @@ export default async function handleOnBeforeSendEvent({
44
45
  if (isAdvertisingDisabled(advertising) || isClickThru || isThrottled(SURFER_ID, cookieManager) && isThrottled(ID5_ID, cookieManager) && isThrottled(RAMP_ID, cookieManager)) return;
45
46
  try {
46
47
  const useShortTimeout = waitForAdvertisingId(advertising);
47
- const [surferIdResult, id5IdResult, rampIdResult] = await Promise.allSettled([collectSurferId(cookieManager, getBrowser, useShortTimeout), getID5Id(logger, componentConfig.id5PartnerId, useShortTimeout, useShortTimeout), getRampId(logger, componentConfig.rampIdJSPath, cookieManager, useShortTimeout, useShortTimeout)]);
48
+ let rampIdPromise = null;
49
+ if (!getBrowser || getBrowser() !== CHROME) {
50
+ rampIdPromise = getRampId(logger, componentConfig.rampIdJSPath, cookieManager, useShortTimeout, useShortTimeout);
51
+ }
52
+ const [surferIdResult, id5IdResult, rampIdResult] = await Promise.allSettled([collectSurferId(cookieManager, getBrowser, useShortTimeout), getID5Id(logger, componentConfig.id5PartnerId, useShortTimeout, useShortTimeout), rampIdPromise]);
48
53
  const availableIds = {};
49
54
  if (surferIdResult.status === "fulfilled" && surferIdResult.value && !isThrottled(SURFER_ID, cookieManager)) {
50
55
  availableIds.surferId = surferIdResult.value;
@@ -23,7 +23,8 @@ export default async function handleViewThrough({
23
23
  try {
24
24
  const event = appendAdvertisingIdQueryToEvent(resolvedIds, eventManager.createEvent(), cookieManager, componentConfig, true);
25
25
  const xdm = {
26
- eventType: AD_CONVERSION_VIEW_EVENT_TYPE
26
+ eventType: AD_CONVERSION_VIEW_EVENT_TYPE,
27
+ timestamp: new Date().toISOString()
27
28
  };
28
29
  event.setUserXdm(xdm);
29
30
  const result = await adConversionHandler.trackAdConversion({
@@ -13,7 +13,7 @@ import { loadScript } from "../../../utils/dom/index.js";
13
13
  import { ID5_SCRIPT_URL } from "../constants/index.js";
14
14
  let id5Id = "";
15
15
  let inProgressId5Promise = null;
16
- const SHORT_TIMEOUT_MS = 5000;
16
+ const SHORT_TIMEOUT_MS = 2000;
17
17
  const DEFAULT_TIMEOUT_MS = 30000;
18
18
  const initiateID5Call = (partnerId, useShortTimeout, logger) => {
19
19
  partnerId = Math.floor(Number(partnerId));
@@ -17,7 +17,7 @@ const RETRY_CONFIG = {
17
17
  MAX_TIME_MS: 30000,
18
18
  DELAY_BASE_MS: 500,
19
19
  MAX_DELAY_MS: 5000,
20
- SHORT_TIMEOUT_MS: 5000
20
+ SHORT_TIMEOUT_MS: 2000
21
21
  };
22
22
  const state = {
23
23
  rampIdEnv: undefined,
@@ -10,10 +10,18 @@ OF ANY KIND, either express or implied. See the License for the specific languag
10
10
  governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ /** @import { ConsentRequestPayload } from './types.js' */
14
+
13
15
  import { createRequestPayload } from "../../utils/request/index.js";
16
+
17
+ /**
18
+ * @function
19
+ *
20
+ * @returns {ConsentRequestPayload}
21
+ */
14
22
  export default () => {
15
23
  const content = {};
16
- const payload = createRequestPayload({
24
+ const requestPayload = createRequestPayload({
17
25
  content,
18
26
  addIdentity: (namespaceCode, identity) => {
19
27
  content.identityMap = content.identityMap || {};
@@ -24,8 +32,13 @@ export default () => {
24
32
  return (content.identityMap && content.identityMap[namespaceCode]) !== undefined;
25
33
  }
26
34
  });
27
- payload.setConsent = consent => {
28
- content.consent = consent;
35
+
36
+ /** @type {ConsentRequestPayload} */
37
+ const payload = {
38
+ ...requestPayload,
39
+ setConsent: consent => {
40
+ content.consent = consent;
41
+ }
29
42
  };
30
43
  return payload;
31
44
  };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Request payload object with methods for merging different types of data.
3
+ *
4
+ * @typedef {Object} ConsentRequestPayload
5
+ * @property {Function} mergeMeta
6
+ * @property {Function} mergeState
7
+ * @property {Function} mergeQuery
8
+ * @property {Function} mergeConfigOverride
9
+ * @property {Function} addIdentity
10
+ * @property {Function} hasIdentity
11
+ * @property {Function} toJSON
12
+ * @property {Function} setConsent
13
+ */
14
+
15
+ export const Types = {};
@@ -40,9 +40,10 @@ const renderContent = ({
40
40
  content,
41
41
  decorateProposition,
42
42
  renderFunc,
43
- renderStatusHandler
43
+ renderStatusHandler,
44
+ alwaysRender
44
45
  }) => {
45
- const executions = containers.filter(renderStatusHandler.shouldRender).map(async container => {
46
+ const executions = containers.filter(element => alwaysRender || renderStatusHandler.shouldRender(element)).map(async container => {
46
47
  await renderFunc(container, content, decorateProposition);
47
48
  renderStatusHandler.markAsRendered(container);
48
49
  });
@@ -53,8 +54,9 @@ const renderContent = ({
53
54
  * Creates an action function that renders content into a container element.
54
55
  *
55
56
  * @param {Function} renderFunc - The function that performs the rendering.
57
+ * @param {boolean} alwaysRender - Whether to always render the content, even if it has already been rendered.
56
58
  */
57
- export const createAction = renderFunc => {
59
+ export const createAction = (renderFunc, alwaysRender = false) => {
58
60
  /**
59
61
  * Renders content into a container element.
60
62
  *
@@ -76,7 +78,8 @@ export const createAction = renderFunc => {
76
78
  content,
77
79
  decorateProposition,
78
80
  renderFunc,
79
- renderStatusHandler
81
+ renderStatusHandler,
82
+ alwaysRender
80
83
  });
81
84
  } finally {
82
85
  showElements(prehidingSelector);
@@ -45,21 +45,21 @@ export const DOM_ACTION_CLICK = "click";
45
45
  export const DOM_ACTION_COLLECT_INTERACTIONS = "collectInteractions";
46
46
  export default () => {
47
47
  return {
48
- [DOM_ACTION_SET_HTML]: createAction(setHtml),
48
+ [DOM_ACTION_SET_HTML]: createAction(setHtml, true),
49
49
  [DOM_ACTION_CUSTOM_CODE]: createAction(prependHtml),
50
- [DOM_ACTION_SET_TEXT]: createAction(setText),
51
- [DOM_ACTION_SET_ATTRIBUTE]: createAction(setAttributes),
52
- [DOM_ACTION_SET_IMAGE_SOURCE]: createAction(swapImage),
53
- [DOM_ACTION_SET_STYLE]: createAction(setStyles),
54
- [DOM_ACTION_MOVE]: createAction(move),
55
- [DOM_ACTION_RESIZE]: createAction(resize),
50
+ [DOM_ACTION_SET_TEXT]: createAction(setText, true),
51
+ [DOM_ACTION_SET_ATTRIBUTE]: createAction(setAttributes, true),
52
+ [DOM_ACTION_SET_IMAGE_SOURCE]: createAction(swapImage, true),
53
+ [DOM_ACTION_SET_STYLE]: createAction(setStyles, true),
54
+ [DOM_ACTION_MOVE]: createAction(move, true),
55
+ [DOM_ACTION_RESIZE]: createAction(resize, true),
56
56
  [DOM_ACTION_REARRANGE]: createAction(rearrangeChildren),
57
- [DOM_ACTION_REMOVE]: createAction(removeNode),
57
+ [DOM_ACTION_REMOVE]: createAction(removeNode, true),
58
58
  [DOM_ACTION_INSERT_AFTER]: createAction(insertHtmlAfter),
59
59
  [DOM_ACTION_INSERT_BEFORE]: createAction(insertHtmlBefore),
60
- [DOM_ACTION_REPLACE_HTML]: createAction(replaceHtml),
60
+ [DOM_ACTION_REPLACE_HTML]: createAction(replaceHtml, true),
61
61
  [DOM_ACTION_PREPEND_HTML]: createAction(prependHtml),
62
62
  [DOM_ACTION_APPEND_HTML]: createAction(appendHtml),
63
- [DOM_ACTION_COLLECT_INTERACTIONS]: createAction(collectInteractions)
63
+ [DOM_ACTION_COLLECT_INTERACTIONS]: createAction(collectInteractions, true)
64
64
  };
65
65
  };
@@ -0,0 +1,16 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ export const DB_NAME = "alloyPushNotifications";
14
+ export const DB_VERSION = 1;
15
+ export const STORE_NAME = "config";
16
+ export const INDEX_KEY = "alloyConfig";
@@ -0,0 +1,114 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ /** @import { PushSubscription } from '../types.js' */
14
+
15
+ import { base64ToBytes, bytesToBase64 } from "../../../utils/index.js";
16
+
17
+ /**
18
+ * Gets push subscription details for the current browser.
19
+ *
20
+ * @async
21
+ * @function
22
+ *
23
+ * @param {object} options
24
+ * @param {string} options.vapidPublicKey - The VAPID public key in base64 format used for push notification authentication.
25
+ * @param {Window} options.window
26
+ *
27
+ * @returns {Promise<PushSubscription>} A promise that resolves to an object containing the push subscription details.
28
+
29
+ * @throws {Error} Throws an error if service workers are not supported in the browser.
30
+ * @throws {Error} Throws an error if user didn't approve push notifications for the domain.
31
+ * @throws {Error} Throws an error if push notifications are not supported in the browser.
32
+ * @throws {Error} Throws an error if no VAPID public key was provided.
33
+ *
34
+ * @example
35
+ * // Get subscription details with VAPID key
36
+ * const vapidKey = "BEl62iUYgUivxIkv69yViEuiBIa40HI5hmjHbKPlXO...";
37
+ * const subscription = await getPushSubscriptionDetails(vapidKey);
38
+ * console.log(subscription.endpoint); // "https://fcm.googleapis.com/fcm/send/..."
39
+ */
40
+ const getPushSubscriptionDetails = async ({
41
+ vapidPublicKey,
42
+ window
43
+ }) => {
44
+ if (!("serviceWorker" in window.navigator)) {
45
+ throw new Error("Service workers are not supported in this browser.");
46
+ }
47
+ if (!("PushManager" in window) || !("Notification" in window)) {
48
+ throw new Error("Push notifications are not supported in this browser.");
49
+ }
50
+
51
+ /** @type {object} */
52
+ const notification = window.Notification;
53
+ if (notification.permission !== "granted") {
54
+ throw new Error("The user has not given permission to send push notifications.");
55
+ }
56
+ const serviceWorkerRegistration =
57
+ // eslint-disable-next-line compat/compat
58
+ await window.navigator.serviceWorker.getRegistration();
59
+ if (!serviceWorkerRegistration) {
60
+ throw new Error("No service worker registration was found.");
61
+ }
62
+
63
+ // Even `applicationServerKey` is not required in the spec, some browsers like Chrome are requiring it.
64
+ if (!vapidPublicKey) {
65
+ throw new Error("No VAPID public key was provided.");
66
+ }
67
+ const subscriptionOptions = {
68
+ userVisibleOnly: true,
69
+ applicationServerKey: base64ToBytes(vapidPublicKey)
70
+ };
71
+
72
+ // Push subscription handling strategy:
73
+ //
74
+ // 1. We always call subscribe() to either get the current subscription or create a new one.
75
+ // - If called with the same VAPID key as an existing subscription, it returns that subscription
76
+ // - If called with a different VAPID key when a subscription exists, it throws an error
77
+ //
78
+ // 2. Error handling approach:
79
+ // - Browser error messages vary and don't clearly indicate VAPID key conflicts
80
+ // - When subscribe() fails, we assume it's likely due to a VAPID key mismatch
81
+ // - We attempt recovery by unsubscribing the existing subscription and retrying
82
+ // - If the retry also fails, we re-throw the original error
83
+ //
84
+ // This strategy ensures we can handle both new subscriptions and VAPID key changes
85
+ // while gracefully falling back to error reporting when recovery isn't possible.
86
+ try {
87
+ const subscription = await serviceWorkerRegistration.pushManager.subscribe(subscriptionOptions);
88
+ return {
89
+ endpoint: subscription.endpoint,
90
+ keys: {
91
+ p256dh: bytesToBase64(new Uint8Array(subscription.getKey("p256dh")), {
92
+ urlSafe: true
93
+ }),
94
+ auth: bytesToBase64(new Uint8Array(subscription.getKey("auth")), {
95
+ urlSafe: true
96
+ })
97
+ }
98
+ };
99
+ } catch (e) {
100
+ const subscription = await serviceWorkerRegistration.pushManager.getSubscription();
101
+ if (!subscription) {
102
+ throw e;
103
+ }
104
+ const unsubscribeResult = await subscription.unsubscribe();
105
+ if (!unsubscribeResult) {
106
+ throw e;
107
+ }
108
+ return getPushSubscriptionDetails({
109
+ vapidPublicKey,
110
+ window
111
+ });
112
+ }
113
+ };
114
+ export default getPushSubscriptionDetails;
@@ -0,0 +1,40 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ /** @import { ServiceWorkerLogger } from '../types.js' */
14
+
15
+ import { getFromIndexedDbStore, openIndexedDb } from "../../../utils/indexedDb.js";
16
+ import { DB_NAME, DB_VERSION, STORE_NAME, INDEX_KEY } from "./constants.js";
17
+
18
+ /**
19
+ * @param {ServiceWorkerLogger} logger
20
+ * @returns {Promise<Object|undefined>}
21
+ * @throws {Error}
22
+ */
23
+ export default async logger => {
24
+ try {
25
+ const db = await openIndexedDb(DB_NAME, DB_VERSION, (/** @type {IDBDatabase} */db) => {
26
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
27
+ db.createObjectStore(STORE_NAME, {
28
+ keyPath: "id"
29
+ });
30
+ }
31
+ });
32
+ const existingConfigData = await getFromIndexedDbStore(db, STORE_NAME, INDEX_KEY);
33
+ db.close();
34
+ return existingConfigData;
35
+ } catch (error) {
36
+ logger.error("Failed to read data from IndexedDB", {
37
+ error
38
+ });
39
+ }
40
+ };
@@ -0,0 +1,48 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ /** @import { Logger } from '../../../core/types.js' */
14
+
15
+ import { openIndexedDb, getFromIndexedDbStore, putToIndexedDbStore } from "../../../utils/index.js";
16
+ import { DB_NAME, DB_VERSION, STORE_NAME, INDEX_KEY } from "./constants.js";
17
+
18
+ /**
19
+ * @param {Object} data
20
+ * @param {Logger} logger
21
+ *
22
+ * @returns {Promise<void>}
23
+ */
24
+ export default async function saveToIndexedDB(data, logger) {
25
+ try {
26
+ const db = await openIndexedDb(DB_NAME, DB_VERSION, (/** @type {IDBDatabase} */db) => {
27
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
28
+ db.createObjectStore(STORE_NAME, {
29
+ keyPath: "id"
30
+ });
31
+ }
32
+ });
33
+ const existingConfigData = await getFromIndexedDbStore(db, STORE_NAME, INDEX_KEY);
34
+ const updatedConfigData = {
35
+ ...(existingConfigData || {}),
36
+ ...data,
37
+ id: INDEX_KEY,
38
+ timestamp: Date.now()
39
+ };
40
+ await putToIndexedDbStore(db, STORE_NAME, updatedConfigData);
41
+ db.close();
42
+ logger.info("Successfully saved web SDK config to IndexedDB", updatedConfigData);
43
+ } catch (error) {
44
+ logger.error("Failed to save config to IndexedDB", {
45
+ error
46
+ });
47
+ }
48
+ }
@@ -0,0 +1,82 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ // @ts-check
14
+ /// <reference lib="webworker" />
15
+
16
+ /** @import { ServiceWorkerLogger } from '../types.js' */
17
+
18
+ import makeSendServiceWorkerTrackingData from "../request/makeSendServiceWorkerTrackingData.js";
19
+
20
+ /**
21
+ * @param {string} type
22
+ * @returns {boolean}
23
+ */
24
+ const canHandleUrl = type => ["DEEPLINK", "WEBURL"].includes(type);
25
+
26
+ /**
27
+ * @function
28
+ *
29
+ * @param {Object} options
30
+ * @param {ServiceWorkerGlobalScope} options.sw
31
+ * @param {NotificationEvent} options.event
32
+ * @param {(url: string, options: object) => Promise<Response>} options.fetch
33
+ * @param {ServiceWorkerLogger} options.logger
34
+ */
35
+ export default ({
36
+ event,
37
+ sw,
38
+ logger,
39
+ fetch
40
+ }) => {
41
+ event.notification.close();
42
+ const data = event.notification.data;
43
+ let targetUrl = null;
44
+ let actionLabel = null;
45
+ if (event.action) {
46
+ const actionIndex = parseInt(event.action.replace("action_", ""), 10);
47
+ if (data?.actions?.buttons[actionIndex]) {
48
+ const button = data.actions.buttons[actionIndex];
49
+ actionLabel = button.label;
50
+ if (canHandleUrl(button.type) && button.uri) {
51
+ targetUrl = button.uri;
52
+ }
53
+ }
54
+ } else if (canHandleUrl(data?.interaction?.type) && data?.interaction?.uri) {
55
+ targetUrl = data.interaction.uri;
56
+ }
57
+ makeSendServiceWorkerTrackingData({
58
+ // eslint-disable-next-line no-underscore-dangle
59
+ xdm: data._xdm.mixins,
60
+ actionLabel,
61
+ applicationLaunches: 1
62
+ }, {
63
+ logger,
64
+ fetch
65
+ }).catch(error => {
66
+ logger.error("Failed to send tracking call:", error);
67
+ });
68
+ if (targetUrl) {
69
+ event.waitUntil(sw.clients.matchAll({
70
+ type: "window"
71
+ }).then(clientList => {
72
+ for (const client of clientList) {
73
+ if (client.url === targetUrl && "focus" in client) {
74
+ return client.focus();
75
+ }
76
+ }
77
+ if (sw.clients.openWindow) {
78
+ return sw.clients.openWindow(targetUrl);
79
+ }
80
+ }));
81
+ }
82
+ };