@adobe/alloy 2.29.0-beta.0 → 2.29.0-beta.13
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/libEs5/components/ActivityCollector/utils/dom/findClickableElement.js +4 -0
- package/libEs5/components/Advertising/createComponent.js +1 -5
- package/libEs5/components/Advertising/handlers/clickThroughHandler.js +2 -1
- package/libEs5/components/Advertising/handlers/sendAdConversion.js +6 -8
- package/libEs5/components/Advertising/handlers/viewThroughHandler.js +4 -3
- package/libEs5/components/Consent/createConsentRequestPayload.js +14 -3
- package/libEs5/components/Consent/types.js +18 -0
- package/libEs5/components/Identity/createComponent.js +5 -4
- package/libEs5/components/Identity/index.js +4 -9
- package/libEs5/components/Personalization/dom-actions/action.js +7 -4
- package/libEs5/components/Personalization/dom-actions/initDomActionsModules.js +10 -10
- package/libEs5/components/PushNotifications/helpers/getPushSubscriptionDetails.js +112 -0
- package/libEs5/components/PushNotifications/index.js +96 -0
- package/libEs5/components/PushNotifications/request/createSendPushSubscriptionPayload.js +62 -0
- package/libEs5/components/PushNotifications/request/createSendPushSubscriptionRequest.js +35 -0
- package/libEs5/components/PushNotifications/request/makeSendPushSubscriptionRequest.js +88 -0
- package/libEs5/components/PushNotifications/types.js +14 -0
- package/libEs5/constants/libraryVersion.js +1 -1
- package/libEs5/core/componentCreators.js +8 -1
- package/libEs5/core/consent/createConsent.js +10 -0
- package/libEs5/core/consent/createConsentStateMachine.js +36 -0
- package/libEs5/core/consent/types.js +24 -0
- package/libEs5/core/edgeNetwork/injectSendEdgeNetworkRequest.js +20 -0
- package/libEs5/core/edgeNetwork/types.js +10 -0
- package/libEs5/core/identity/createIdentity.js +82 -0
- package/libEs5/core/identity/types.js +15 -0
- package/libEs5/core/index.js +8 -0
- package/libEs5/core/injectCreateResponse.js +9 -7
- package/libEs5/core/types.js +101 -11
- package/libEs5/utils/bytes.js +38 -0
- package/libEs5/{components/Identity → utils}/createDecodeKndctrCookie.js +12 -20
- package/libEs5/utils/createLoggingCookieJar.js +14 -1
- package/libEs5/utils/createMerger.js +5 -4
- package/libEs5/utils/index.js +13 -0
- package/libEs5/utils/injectStorage.js +19 -0
- package/libEs5/utils/request/createDataCollectionRequestPayload.js +16 -8
- package/libEs5/utils/request/createRequest.js +28 -1
- package/libEs5/utils/request/createRequestPayload.js +68 -4
- package/libEs5/utils/request/types.js +52 -0
- package/libEs5/utils/types.js +39 -0
- package/libEs6/components/ActivityCollector/utils/dom/findClickableElement.js +4 -0
- package/libEs6/components/Advertising/createComponent.js +1 -5
- package/libEs6/components/Advertising/handlers/clickThroughHandler.js +2 -1
- package/libEs6/components/Advertising/handlers/sendAdConversion.js +6 -8
- package/libEs6/components/Advertising/handlers/viewThroughHandler.js +4 -3
- package/libEs6/components/Consent/createConsentRequestPayload.js +16 -3
- package/libEs6/components/Consent/types.js +15 -0
- package/libEs6/components/Identity/createComponent.js +5 -4
- package/libEs6/components/Identity/index.js +4 -9
- package/libEs6/components/Personalization/dom-actions/action.js +7 -4
- package/libEs6/components/Personalization/dom-actions/initDomActionsModules.js +10 -10
- package/libEs6/components/PushNotifications/helpers/getPushSubscriptionDetails.js +110 -0
- package/libEs6/components/PushNotifications/index.js +93 -0
- package/libEs6/components/PushNotifications/request/createSendPushSubscriptionPayload.js +61 -0
- package/libEs6/components/PushNotifications/request/createSendPushSubscriptionRequest.js +34 -0
- package/libEs6/components/PushNotifications/request/makeSendPushSubscriptionRequest.js +84 -0
- package/libEs6/components/PushNotifications/types.js +11 -0
- package/libEs6/constants/libraryVersion.js +1 -1
- package/libEs6/core/componentCreators.js +2 -1
- package/libEs6/core/consent/createConsent.js +12 -0
- package/libEs6/core/consent/createConsentStateMachine.js +36 -0
- package/libEs6/core/consent/types.js +21 -0
- package/libEs6/core/edgeNetwork/injectSendEdgeNetworkRequest.js +20 -0
- package/libEs6/core/edgeNetwork/types.js +7 -0
- package/libEs6/core/identity/createIdentity.js +81 -0
- package/libEs6/core/identity/types.js +12 -0
- package/libEs6/core/index.js +8 -0
- package/libEs6/core/injectCreateResponse.js +11 -7
- package/libEs6/core/types.js +101 -11
- package/libEs6/utils/bytes.js +33 -0
- package/libEs6/{components/Identity → utils}/createDecodeKndctrCookie.js +11 -19
- package/libEs6/utils/createLoggingCookieJar.js +15 -1
- package/libEs6/utils/createMerger.js +5 -4
- package/libEs6/utils/index.js +1 -0
- package/libEs6/utils/injectStorage.js +20 -0
- package/libEs6/utils/request/createDataCollectionRequestPayload.js +19 -8
- package/libEs6/utils/request/createRequest.js +29 -1
- package/libEs6/utils/request/createRequestPayload.js +67 -4
- package/libEs6/utils/request/types.js +49 -0
- package/libEs6/utils/types.js +36 -0
- package/package.json +30 -30
- package/types/components/ActivityCollector/utils/dom/findClickableElement.d.ts.map +1 -1
- package/types/components/Advertising/createComponent.d.ts +1 -1
- package/types/components/Advertising/createComponent.d.ts.map +1 -1
- package/types/components/Advertising/handlers/sendAdConversion.d.ts.map +1 -1
- package/types/components/Advertising/handlers/viewThroughHandler.d.ts.map +1 -1
- package/types/components/Advertising/index.d.ts +1 -1
- package/types/components/Consent/createConsentRequest.d.ts +1 -11
- package/types/components/Consent/createConsentRequest.d.ts.map +1 -1
- package/types/components/Consent/createConsentRequestPayload.d.ts +2 -9
- package/types/components/Consent/createConsentRequestPayload.d.ts.map +1 -1
- package/types/components/Consent/types.d.ts +28 -0
- package/types/components/Consent/types.d.ts.map +1 -0
- package/types/components/Identity/createComponent.d.ts +2 -2
- package/types/components/Identity/createComponent.d.ts.map +1 -1
- package/types/components/Identity/getIdentity/createIdentityRequest.d.ts +1 -11
- package/types/components/Identity/getIdentity/createIdentityRequest.d.ts.map +1 -1
- package/types/components/Identity/getIdentity/createIdentityRequestPayload.d.ts +1 -9
- package/types/components/Identity/getIdentity/createIdentityRequestPayload.d.ts.map +1 -1
- package/types/components/Identity/index.d.ts +2 -1
- package/types/components/Identity/index.d.ts.map +1 -1
- package/types/components/Personalization/dom-actions/action.d.ts +1 -1
- package/types/components/Personalization/dom-actions/action.d.ts.map +1 -1
- package/types/components/PushNotifications/helpers/getPushSubscriptionDetails.d.ts +30 -0
- package/types/components/PushNotifications/helpers/getPushSubscriptionDetails.d.ts.map +1 -0
- package/types/components/PushNotifications/index.d.ts +43 -0
- package/types/components/PushNotifications/index.d.ts.map +1 -0
- package/types/components/PushNotifications/request/createSendPushSubscriptionPayload.d.ts +10 -0
- package/types/components/PushNotifications/request/createSendPushSubscriptionPayload.d.ts.map +1 -0
- package/types/components/PushNotifications/request/createSendPushSubscriptionRequest.d.ts +7 -0
- package/types/components/PushNotifications/request/createSendPushSubscriptionRequest.d.ts.map +1 -0
- package/types/components/PushNotifications/request/makeSendPushSubscriptionRequest.d.ts +20 -0
- package/types/components/PushNotifications/request/makeSendPushSubscriptionRequest.d.ts.map +1 -0
- package/types/components/PushNotifications/types.d.ts +23 -0
- package/types/components/PushNotifications/types.d.ts.map +1 -0
- package/types/components/StreamingMedia/createMediaRequest.d.ts +1 -11
- package/types/components/StreamingMedia/createMediaRequest.d.ts.map +1 -1
- package/types/core/componentCreators.d.ts +1 -0
- package/types/core/consent/createConsent.d.ts +4 -10
- package/types/core/consent/createConsent.d.ts.map +1 -1
- package/types/core/consent/createConsentStateMachine.d.ts +4 -12
- package/types/core/consent/createConsentStateMachine.d.ts.map +1 -1
- package/types/core/consent/types.d.ts +42 -0
- package/types/core/consent/types.d.ts.map +1 -0
- package/types/core/edgeNetwork/injectSendEdgeNetworkRequest.d.ts +15 -13
- package/types/core/edgeNetwork/injectSendEdgeNetworkRequest.d.ts.map +1 -1
- package/types/core/edgeNetwork/types.d.ts +12 -0
- package/types/core/edgeNetwork/types.d.ts.map +1 -0
- package/types/core/identity/createIdentity.d.ts +12 -0
- package/types/core/identity/createIdentity.d.ts.map +1 -0
- package/types/core/identity/types.d.ts +23 -0
- package/types/core/identity/types.d.ts.map +1 -0
- package/types/core/index.d.ts.map +1 -1
- package/types/core/injectCreateResponse.d.ts +3 -27
- package/types/core/injectCreateResponse.d.ts.map +1 -1
- package/types/core/types.d.ts +209 -22
- package/types/core/types.d.ts.map +1 -1
- package/types/utils/bytes.d.ts +3 -0
- package/types/utils/bytes.d.ts.map +1 -0
- package/types/utils/createDecodeKndctrCookie.d.ts.map +1 -0
- package/types/utils/createLoggingCookieJar.d.ts +5 -3
- package/types/utils/createLoggingCookieJar.d.ts.map +1 -1
- package/types/utils/createMerger.d.ts +1 -1
- package/types/utils/createMerger.d.ts.map +1 -1
- package/types/utils/index.d.ts +1 -0
- package/types/utils/injectStorage.d.ts +2 -40
- package/types/utils/injectStorage.d.ts.map +1 -1
- package/types/utils/request/createDataCollectionRequest.d.ts +1 -11
- package/types/utils/request/createDataCollectionRequest.d.ts.map +1 -1
- package/types/utils/request/createDataCollectionRequestPayload.d.ts +2 -9
- package/types/utils/request/createDataCollectionRequestPayload.d.ts.map +1 -1
- package/types/utils/request/createRequest.d.ts +13 -11
- package/types/utils/request/createRequest.d.ts.map +1 -1
- package/types/utils/request/createRequestPayload.d.ts +7 -9
- package/types/utils/request/createRequestPayload.d.ts.map +1 -1
- package/types/utils/request/types.d.ts +91 -0
- package/types/utils/request/types.d.ts.map +1 -0
- package/types/utils/types.d.ts +91 -0
- package/types/utils/types.d.ts.map +1 -0
- package/types/components/Identity/createDecodeKndctrCookie.d.ts.map +0 -1
- /package/types/{components/Identity → utils}/createDecodeKndctrCookie.d.ts +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
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
|
+
auth: bytesToBase64(new Uint8Array(subscription.getKey("auth")))
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
} catch (e) {
|
|
96
|
+
const subscription = await serviceWorkerRegistration.pushManager.getSubscription();
|
|
97
|
+
if (!subscription) {
|
|
98
|
+
throw e;
|
|
99
|
+
}
|
|
100
|
+
const unsubscribeResult = await subscription.unsubscribe();
|
|
101
|
+
if (!unsubscribeResult) {
|
|
102
|
+
throw e;
|
|
103
|
+
}
|
|
104
|
+
return getPushSubscriptionDetails({
|
|
105
|
+
vapidPublicKey,
|
|
106
|
+
window
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
export default getPushSubscriptionDetails;
|
|
@@ -0,0 +1,93 @@
|
|
|
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 { StorageCreator } from '../../utils/types.js' */
|
|
14
|
+
/** @import { EventManager, Logger } from '../../core/types.js' */
|
|
15
|
+
/** @import { IdentityManager } from '../../core/identity/types.js' */
|
|
16
|
+
/** @import { ConsentManager } from '../../core/consent/types.js' */
|
|
17
|
+
/** @import { EdgeRequestExecutor } from '../../core/edgeNetwork/types.js' */
|
|
18
|
+
|
|
19
|
+
import { objectOf, string } from "../../utils/validation/index.js";
|
|
20
|
+
import { sanitizeOrgIdForCookieName } from "../../utils/index.js";
|
|
21
|
+
import makeSendPushSubscriptionRequest from "./request/makeSendPushSubscriptionRequest.js";
|
|
22
|
+
const isComponentConfigured = ({
|
|
23
|
+
orgId,
|
|
24
|
+
pushNotifications: {
|
|
25
|
+
vapidPublicKey
|
|
26
|
+
} = {
|
|
27
|
+
vapidPublicKey: undefined
|
|
28
|
+
}
|
|
29
|
+
}) => orgId && vapidPublicKey;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @function
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} options
|
|
35
|
+
* @param {{ orgId: string, pushNotifications: { vapidPublicKey: string }}} options.config
|
|
36
|
+
* @param {StorageCreator} options.createNamespacedStorage
|
|
37
|
+
* @param {EventManager} options.eventManager
|
|
38
|
+
* @param {Logger} options.logger
|
|
39
|
+
* @param {ConsentManager} options.consent
|
|
40
|
+
* @param {IdentityManager} options.identity
|
|
41
|
+
* @param {EdgeRequestExecutor} options.sendEdgeNetworkRequest
|
|
42
|
+
* @returns {{ commands: { sendPushSubscription: object } }}
|
|
43
|
+
*/
|
|
44
|
+
const createPushNotifications = ({
|
|
45
|
+
createNamespacedStorage,
|
|
46
|
+
eventManager,
|
|
47
|
+
config,
|
|
48
|
+
logger,
|
|
49
|
+
consent,
|
|
50
|
+
identity,
|
|
51
|
+
sendEdgeNetworkRequest
|
|
52
|
+
}) => {
|
|
53
|
+
return {
|
|
54
|
+
commands: {
|
|
55
|
+
sendPushSubscription: {
|
|
56
|
+
run: async () => {
|
|
57
|
+
if (!isComponentConfigured(config)) {
|
|
58
|
+
throw new Error("Push notifications module is not configured. No VAPID public key was provided.");
|
|
59
|
+
}
|
|
60
|
+
const {
|
|
61
|
+
orgId,
|
|
62
|
+
pushNotifications: {
|
|
63
|
+
vapidPublicKey
|
|
64
|
+
} = {
|
|
65
|
+
vapidPublicKey: undefined
|
|
66
|
+
}
|
|
67
|
+
} = config || {};
|
|
68
|
+
const storage = createNamespacedStorage(`${sanitizeOrgIdForCookieName(orgId)}.pushNotifications.`);
|
|
69
|
+
return makeSendPushSubscriptionRequest({
|
|
70
|
+
config: {
|
|
71
|
+
vapidPublicKey
|
|
72
|
+
},
|
|
73
|
+
storage: storage.persistent,
|
|
74
|
+
logger,
|
|
75
|
+
sendEdgeNetworkRequest,
|
|
76
|
+
consent,
|
|
77
|
+
eventManager,
|
|
78
|
+
identity,
|
|
79
|
+
window
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
optionsValidator: objectOf({}).noUnknownFields()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
createPushNotifications.namespace = "Push Notifications";
|
|
88
|
+
createPushNotifications.configValidators = objectOf({
|
|
89
|
+
pushNotifications: objectOf({
|
|
90
|
+
vapidPublicKey: string().required()
|
|
91
|
+
}).noUnknownFields()
|
|
92
|
+
});
|
|
93
|
+
export default createPushNotifications;
|
|
@@ -0,0 +1,61 @@
|
|
|
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 { EventManager } from "../../../core/types.js" */
|
|
14
|
+
/** @import { DataCollectionRequestPayload } from "../../../utils/request/types.js" */
|
|
15
|
+
|
|
16
|
+
import createDataCollectionRequestPayload from "../../../utils/request/createDataCollectionRequestPayload.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a data collection request payload for sending push subscription details to Adobe Experience Platform.
|
|
20
|
+
*
|
|
21
|
+
* This function constructs an event containing push notification subscription information,
|
|
22
|
+
* including the subscription details, platform information, and identity data. The event
|
|
23
|
+
* is then packaged into a data collection request payload for transmission to the edge network.
|
|
24
|
+
*
|
|
25
|
+
* @async
|
|
26
|
+
* @function
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} options
|
|
29
|
+
* @param {string} options.ecid
|
|
30
|
+
* @param {EventManager} options.eventManager
|
|
31
|
+
* @param {string} options.serializedPushSubscriptionDetails
|
|
32
|
+
* @param {Window} options.window
|
|
33
|
+
*
|
|
34
|
+
* @returns {Promise<DataCollectionRequestPayload>}
|
|
35
|
+
*/
|
|
36
|
+
export default async ({
|
|
37
|
+
ecid,
|
|
38
|
+
eventManager,
|
|
39
|
+
serializedPushSubscriptionDetails,
|
|
40
|
+
window
|
|
41
|
+
}) => {
|
|
42
|
+
const event = eventManager.createEvent();
|
|
43
|
+
event.setUserData({
|
|
44
|
+
pushNotificationDetails: [{
|
|
45
|
+
appID: window.location.host,
|
|
46
|
+
token: serializedPushSubscriptionDetails,
|
|
47
|
+
platform: "web",
|
|
48
|
+
denylisted: false,
|
|
49
|
+
identity: {
|
|
50
|
+
namespace: {
|
|
51
|
+
code: "ECID"
|
|
52
|
+
},
|
|
53
|
+
id: ecid
|
|
54
|
+
}
|
|
55
|
+
}]
|
|
56
|
+
});
|
|
57
|
+
event.finalize();
|
|
58
|
+
const payload = createDataCollectionRequestPayload();
|
|
59
|
+
payload.addEvent(event);
|
|
60
|
+
return payload;
|
|
61
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2023 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 { DataCollectionRequestPayload, Request } from '../../../utils/request/types.js' */
|
|
14
|
+
|
|
15
|
+
import { createRequest } from "../../../utils/request/index.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @function
|
|
19
|
+
*
|
|
20
|
+
* @param {{ payload: DataCollectionRequestPayload }} options
|
|
21
|
+
*
|
|
22
|
+
* @returns {Request}
|
|
23
|
+
*/
|
|
24
|
+
export default ({
|
|
25
|
+
payload
|
|
26
|
+
}) => createRequest({
|
|
27
|
+
payload,
|
|
28
|
+
getAction() {
|
|
29
|
+
return "interact";
|
|
30
|
+
},
|
|
31
|
+
getUseSendBeacon() {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
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 { Storage } from '../../../utils/types.js' */
|
|
14
|
+
/** @import { EventManager, Logger } from '../../../core/types.js' */
|
|
15
|
+
/** @import { IdentityManager } from '../../../core/identity/types.js' */
|
|
16
|
+
/** @import { ConsentManager } from '../../../core/consent/types.js' */
|
|
17
|
+
/** @import { EdgeRequestExecutor } from '../../../core/edgeNetwork/types.js' */
|
|
18
|
+
|
|
19
|
+
import { sortObjectKeysRecursively } from "../../../utils/index.js";
|
|
20
|
+
import getPushSubscriptionDetails from "../helpers/getPushSubscriptionDetails.js";
|
|
21
|
+
import createSendPushSubscriptionRequest from "./createSendPushSubscriptionRequest.js";
|
|
22
|
+
import createSendPushSubscriptionPayload from "./createSendPushSubscriptionPayload.js";
|
|
23
|
+
const SUBSCRIPTION_DETAILS = "subscriptionDetails";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves and returns push subscription details with sorted object keys.
|
|
27
|
+
*
|
|
28
|
+
* This function gets the push subscription details using the provided VAPID public key
|
|
29
|
+
* and returns the details with all object keys sorted recursively for consistent output.
|
|
30
|
+
*
|
|
31
|
+
* @async
|
|
32
|
+
* @function
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} options
|
|
35
|
+
* @param {{vapidPublicKey: string}} options.config
|
|
36
|
+
* @param {Storage} options.storage
|
|
37
|
+
* @param {Logger} options.logger
|
|
38
|
+
* @param {EventManager} options.eventManager
|
|
39
|
+
* @param {IdentityManager} options.identity
|
|
40
|
+
* @param {EdgeRequestExecutor} options.sendEdgeNetworkRequest
|
|
41
|
+
* @param {ConsentManager} options.consent
|
|
42
|
+
* @param {Window} options.window
|
|
43
|
+
*
|
|
44
|
+
* @returns {Promise<void>}
|
|
45
|
+
*/
|
|
46
|
+
export default async ({
|
|
47
|
+
config: {
|
|
48
|
+
vapidPublicKey
|
|
49
|
+
},
|
|
50
|
+
storage,
|
|
51
|
+
logger,
|
|
52
|
+
sendEdgeNetworkRequest,
|
|
53
|
+
consent,
|
|
54
|
+
eventManager,
|
|
55
|
+
identity,
|
|
56
|
+
window
|
|
57
|
+
}) => {
|
|
58
|
+
await identity.awaitIdentity();
|
|
59
|
+
const ecid = identity.getEcidFromCookie();
|
|
60
|
+
const pushSubscriptionDetails = await getPushSubscriptionDetails({
|
|
61
|
+
vapidPublicKey,
|
|
62
|
+
window
|
|
63
|
+
});
|
|
64
|
+
const serializedPushSubscriptionDetails = JSON.stringify(sortObjectKeysRecursively(pushSubscriptionDetails));
|
|
65
|
+
const cacheValue = `${ecid}${serializedPushSubscriptionDetails}`;
|
|
66
|
+
if (cacheValue === storage.getItem(SUBSCRIPTION_DETAILS)) {
|
|
67
|
+
logger.info("Subscription details have not changed. Not sending to the server.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
storage.setItem(SUBSCRIPTION_DETAILS, cacheValue);
|
|
71
|
+
const payload = await createSendPushSubscriptionPayload({
|
|
72
|
+
eventManager,
|
|
73
|
+
ecid,
|
|
74
|
+
serializedPushSubscriptionDetails,
|
|
75
|
+
window
|
|
76
|
+
});
|
|
77
|
+
const request = createSendPushSubscriptionRequest({
|
|
78
|
+
payload
|
|
79
|
+
});
|
|
80
|
+
await consent.awaitConsent();
|
|
81
|
+
await sendEdgeNetworkRequest({
|
|
82
|
+
request
|
|
83
|
+
});
|
|
84
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** @import { Identity } from '../../utils/request/types.js' */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} PushSubscription
|
|
5
|
+
* @property {string} endpoint - The push service endpoint URL
|
|
6
|
+
* @property {Object} keys - The subscription keys object
|
|
7
|
+
* @property {string|null} keys.p256dh - The P-256 ECDH public key as an ArrayBuffer, or null if not available
|
|
8
|
+
* @property {string|null} keys.auth - The authentication secret as an ArrayBuffer, or null if not available
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const Types = {};
|
|
@@ -22,4 +22,5 @@ export { default as mediaAnalyticsBridge } from "../components/MediaAnalyticsBri
|
|
|
22
22
|
export { default as personalization } from "../components/Personalization/index.js";
|
|
23
23
|
export { default as rulesEngine } from "../components/RulesEngine/index.js";
|
|
24
24
|
export { default as streamingMedia } from "../components/StreamingMedia/index.js";
|
|
25
|
-
export { default as advertising } from "../components/Advertising/index.js";
|
|
25
|
+
export { default as advertising } from "../components/Advertising/index.js";
|
|
26
|
+
export { default as pushNotifications } from "../components/PushNotifications/index.js";
|
|
@@ -10,9 +10,21 @@ 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 { ConsentManager } from './types.js' */
|
|
14
|
+
|
|
13
15
|
import { IN, OUT, PENDING } from "../../constants/consentStatus.js";
|
|
14
16
|
import { GENERAL } from "../../constants/consentPurpose.js";
|
|
15
17
|
import { CONSENT_SOURCE_DEFAULT, CONSENT_SOURCE_INITIAL, CONSENT_SOURCE_NEW } from "./createConsentStateMachine.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @function
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} options
|
|
23
|
+
* @param {ConsentStateMachine} options.generalConsentState
|
|
24
|
+
* @param {Logger} options.logger
|
|
25
|
+
*
|
|
26
|
+
* @returns {ConsentManager}
|
|
27
|
+
*/
|
|
16
28
|
export default ({
|
|
17
29
|
generalConsentState,
|
|
18
30
|
logger
|
|
@@ -10,18 +10,54 @@ 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 { Logger } from '../../core/types.js' */
|
|
14
|
+
/** @import { ConsentStateMachineUtils, ConsentStateMachine } from './types.js' */
|
|
15
|
+
|
|
13
16
|
import { defer } from "../../utils/index.js";
|
|
14
17
|
export const DECLINED_CONSENT = "The user declined consent.";
|
|
15
18
|
export const DECLINED_CONSENT_ERROR_CODE = "declinedConsent";
|
|
16
19
|
export const CONSENT_SOURCE_DEFAULT = "default";
|
|
17
20
|
export const CONSENT_SOURCE_INITIAL = "initial";
|
|
18
21
|
export const CONSENT_SOURCE_NEW = "new";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @private
|
|
25
|
+
* @param {string} errorMessage
|
|
26
|
+
* @returns {Error}
|
|
27
|
+
*/
|
|
19
28
|
const createDeclinedConsentError = errorMessage => {
|
|
20
29
|
const error = new Error(errorMessage);
|
|
21
30
|
error.code = DECLINED_CONSENT_ERROR_CODE;
|
|
22
31
|
error.message = errorMessage;
|
|
23
32
|
return error;
|
|
24
33
|
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a consent state machine that manages user consent preferences and
|
|
37
|
+
* coordinates deferred operations that require consent.
|
|
38
|
+
*
|
|
39
|
+
* The state machine supports the following states:
|
|
40
|
+
* - "in": User has provided consent (with default or explicit consent)
|
|
41
|
+
* - "out": User has declined consent (with default or explicit decline)
|
|
42
|
+
* - "pending": Consent decision is awaiting user input
|
|
43
|
+
*
|
|
44
|
+
* @param {object} options
|
|
45
|
+
* @param {Logger} options.logger
|
|
46
|
+
*
|
|
47
|
+
* @returns {ConsentStateMachine}
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const consentStateMachine = createConsentStateMachine({ logger });
|
|
51
|
+
*
|
|
52
|
+
* // Set consent state
|
|
53
|
+
* consentStateMachine.in('new');
|
|
54
|
+
*
|
|
55
|
+
* // Wait for consent
|
|
56
|
+
* await consentStateMachine.awaitConsent();
|
|
57
|
+
*
|
|
58
|
+
* // Check current state
|
|
59
|
+
* const { state, wasSet } = consentStateMachine.current();
|
|
60
|
+
*/
|
|
25
61
|
export default ({
|
|
26
62
|
logger
|
|
27
63
|
}) => {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} ConsentStateMachine
|
|
3
|
+
* @property {function(string): void} in
|
|
4
|
+
* @property {function(string): void} out
|
|
5
|
+
* @property {function(string): void} pending
|
|
6
|
+
* @property {function(boolean=): Promise<void>} awaitConsent
|
|
7
|
+
* @property {function(): Promise<void>} withConsent
|
|
8
|
+
* @property {function(): {state: string, wasSet: boolean}} current
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} ConsentManager
|
|
13
|
+
* @property {function(Object, Object): void} initializeConsent
|
|
14
|
+
* @property {function(Object): void} setConsent
|
|
15
|
+
* @property {function(): void} suspend
|
|
16
|
+
* @property {function(): Promise<void>} awaitConsent
|
|
17
|
+
* @property {function(): Promise<void>} withConsent
|
|
18
|
+
* @property {function(): {state: string, wasSet: boolean}} current
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export const Types = {};
|
|
@@ -10,6 +10,9 @@ 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 { EdgeRequestExecutor } from './types.js' */
|
|
14
|
+
/** @import { ResponseCreator } from '../types.js' */
|
|
15
|
+
|
|
13
16
|
import { ID_THIRD_PARTY as ID_THIRD_PARTY_DOMAIN } from "../../constants/domain.js";
|
|
14
17
|
import apiVersion from "../../constants/apiVersion.js";
|
|
15
18
|
import { createCallbackAggregator, noop } from "../../utils/index.js";
|
|
@@ -19,6 +22,23 @@ import handleRequestFailure from "./handleRequestFailure.js";
|
|
|
19
22
|
const isDemdexBlockedError = (error, request) => {
|
|
20
23
|
return request.getUseIdThirdPartyDomain() && isNetworkError(error);
|
|
21
24
|
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @function
|
|
28
|
+
*
|
|
29
|
+
* @param {object} options
|
|
30
|
+
* @param {{edgeDomain: string, edgeBasePath: string, datastreamId: string}} options.config
|
|
31
|
+
* @param {object} options.lifecycle
|
|
32
|
+
* @param {object} options.cookieTransfer
|
|
33
|
+
* @param {function(object): Promise<Object>} options.sendNetworkRequest
|
|
34
|
+
* @param {ResponseCreator} options.createResponse
|
|
35
|
+
* @param {function(object): void} options.processWarningsAndErrors
|
|
36
|
+
* @param {function(): string|undefined} options.getLocationHint
|
|
37
|
+
* @param {function(): string} options.getAssuranceValidationTokenParams
|
|
38
|
+
|
|
39
|
+
*
|
|
40
|
+
* @returns {EdgeRequestExecutor} A function that sends edge network requests with lifecycle management
|
|
41
|
+
*/
|
|
22
42
|
export default ({
|
|
23
43
|
config,
|
|
24
44
|
lifecycle,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** @import { Request } from '../../utils/request/types.js' */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {function({request: Request, runOnResponseCallbacks?: function(): void, runOnRequestFailureCallbacks?: function(): void}): Promise<Object>} EdgeRequestExecutor
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const Types = {};
|
|
@@ -0,0 +1,81 @@
|
|
|
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 { IdentityManager } from './types.js' */
|
|
14
|
+
/** @import { Logger } from '../types.js' */
|
|
15
|
+
/** @import { CookieJar } from '../../utils/types.js' */
|
|
16
|
+
|
|
17
|
+
import createDecodeKndctrCookie from "../../utils/createDecodeKndctrCookie.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates an identity management service for handling ECID (Experience Cloud ID) resolution and tracking.
|
|
21
|
+
*
|
|
22
|
+
* This factory function creates an identity manager service that manages the state of identity resolution,
|
|
23
|
+
* provides methods to check for existing identity cookies, and handles the asynchronous nature
|
|
24
|
+
* of identity acquisition in Adobe Experience Platform Web SDK.
|
|
25
|
+
*
|
|
26
|
+
* @function
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} options
|
|
29
|
+
* @param {Logger} options.logger
|
|
30
|
+
* @param {CookieJar} options.loggingCookieJar
|
|
31
|
+
* @param {{orgId: string}} options.config
|
|
32
|
+
*
|
|
33
|
+
* @returns {IdentityManager}
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Create an identity service
|
|
37
|
+
* const identityManager = createIdentity({
|
|
38
|
+
* logger: myLogger,
|
|
39
|
+
* loggingCookieJar: myCookieJar,
|
|
40
|
+
* config: { orgId: 'myOrgId@AdobeOrg' }
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Initialize and check for existing identity
|
|
44
|
+
* identityManager.initialize();
|
|
45
|
+
*
|
|
46
|
+
* // Wait for identity to be available
|
|
47
|
+
* await identityManager.awaitIdentity();
|
|
48
|
+
*
|
|
49
|
+
* // Get ECID from cookie
|
|
50
|
+
* const ecid = identityManager.getEcidFromCookie();
|
|
51
|
+
*/
|
|
52
|
+
export default ({
|
|
53
|
+
logger,
|
|
54
|
+
loggingCookieJar,
|
|
55
|
+
config
|
|
56
|
+
}) => {
|
|
57
|
+
let awaitIdentityResolve = null;
|
|
58
|
+
const awaitIdentityPromise = new Promise(resolve => {
|
|
59
|
+
awaitIdentityResolve = resolve;
|
|
60
|
+
});
|
|
61
|
+
const decodeKndctrCookie = createDecodeKndctrCookie({
|
|
62
|
+
orgId: config.orgId,
|
|
63
|
+
cookieJar: loggingCookieJar,
|
|
64
|
+
logger
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
initialize() {
|
|
68
|
+
const ecidFromCookie = decodeKndctrCookie();
|
|
69
|
+
if (ecidFromCookie) {
|
|
70
|
+
this.setIdentityAcquired();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
setIdentityAcquired() {
|
|
74
|
+
awaitIdentityResolve();
|
|
75
|
+
},
|
|
76
|
+
awaitIdentity() {
|
|
77
|
+
return awaitIdentityPromise;
|
|
78
|
+
},
|
|
79
|
+
getEcidFromCookie: () => decodeKndctrCookie()
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @import { Logger } from '../../core/types.js' */
|
|
2
|
+
/** @import { CookieJar } from '../../utils/types.js' */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} IdentityManager
|
|
6
|
+
* @property {Function} initialize
|
|
7
|
+
* @property {Function} setIdentityAcquired - Marks identity as acquired and resolves any pending identity promises
|
|
8
|
+
* @property {Function} awaitIdentity - Returns a promise that resolves when identity is acquired
|
|
9
|
+
* @property {Function} getEcidFromCookie
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const Types = {};
|
package/libEs6/core/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import createLifecycle from "./createLifecycle.js";
|
|
|
17
17
|
import createComponentRegistry from "./createComponentRegistry.js";
|
|
18
18
|
import injectSendNetworkRequest from "./network/injectSendNetworkRequest.js";
|
|
19
19
|
import injectExtractEdgeInfo from "./edgeNetwork/injectExtractEdgeInfo.js";
|
|
20
|
+
import createIdentity from "./identity/createIdentity.js";
|
|
20
21
|
import createConsent from "./consent/createConsent.js";
|
|
21
22
|
import createConsentStateMachine from "./consent/createConsentStateMachine.js";
|
|
22
23
|
import createEvent from "./createEvent.js";
|
|
@@ -156,6 +157,12 @@ export const createExecuteCommand = ({
|
|
|
156
157
|
generalConsentState,
|
|
157
158
|
logger
|
|
158
159
|
});
|
|
160
|
+
const identity = createIdentity({
|
|
161
|
+
config,
|
|
162
|
+
logger,
|
|
163
|
+
loggingCookieJar
|
|
164
|
+
});
|
|
165
|
+
identity.initialize();
|
|
159
166
|
const eventManager = createEventManager({
|
|
160
167
|
config,
|
|
161
168
|
logger,
|
|
@@ -177,6 +184,7 @@ export const createExecuteCommand = ({
|
|
|
177
184
|
config,
|
|
178
185
|
componentRegistry,
|
|
179
186
|
consent,
|
|
187
|
+
identity,
|
|
180
188
|
eventManager,
|
|
181
189
|
fireReferrerHideableImage,
|
|
182
190
|
logger: componentLogger,
|