Commits vergleichen
1 Commits
1f10389016
...
master
| Autor | SHA1 | Datum | |
|---|---|---|---|
| 904787c115 |
@@ -40,6 +40,7 @@
|
||||
"src/background/gvl-service.js",
|
||||
"src/core/binary-utils.js",
|
||||
"src/core/tcf-core-metadata-decoder.js",
|
||||
"src/core/consent-diff.js",
|
||||
"src/background.js"
|
||||
]
|
||||
},
|
||||
|
||||
+71
-6
@@ -224,6 +224,7 @@ async function handleVendorGetMessage(message, sender) {
|
||||
}
|
||||
|
||||
const latestPingData = tabId !== null ? getLatestTcfPing(tabId) : null;
|
||||
const activeConsentCaptureSession = getActiveConsentCaptureSession(sender);
|
||||
|
||||
const consentState = buildConsentStateV1(
|
||||
message.payload.payload,
|
||||
@@ -235,7 +236,7 @@ async function handleVendorGetMessage(message, sender) {
|
||||
stableStringify(consentState.fingerprintSource)
|
||||
);
|
||||
|
||||
const captureSessionId = getActiveConsentCaptureSessionId(sender);
|
||||
const captureSessionId = activeConsentCaptureSession?.captureSessionId ?? null;
|
||||
|
||||
rememberLatestConsentState(consentState);
|
||||
|
||||
@@ -245,12 +246,19 @@ async function handleVendorGetMessage(message, sender) {
|
||||
);
|
||||
|
||||
if (captureSessionId) {
|
||||
await persistConsentExecutionEvent({
|
||||
const consentExecutionEvent = await persistConsentExecutionEvent({
|
||||
captureSessionId,
|
||||
consentState,
|
||||
rawCapture: message.payload.payload,
|
||||
sender
|
||||
});
|
||||
|
||||
await persistConsentDiffEvent({
|
||||
captureSessionId,
|
||||
providerAnnouncementEvent:
|
||||
activeConsentCaptureSession?.providerAnnouncementEvents?.[0] ?? null,
|
||||
consentExecutionEvent
|
||||
});
|
||||
}
|
||||
|
||||
completeConsentCaptureSession(sender);
|
||||
@@ -1044,17 +1052,20 @@ async function handleStartPreConsentCaptureMessage(message) {
|
||||
|
||||
session.status = "recording";
|
||||
session.captureSessionId = createCaptureSessionId();
|
||||
session.providerAnnouncementEvents = [];
|
||||
updateConsentCaptureBadge(session);
|
||||
|
||||
const bufferedEvents = session.bufferedPreConsentEvents;
|
||||
session.bufferedPreConsentEvents = [];
|
||||
|
||||
for (const bufferedEvent of bufferedEvents) {
|
||||
await persistProviderAnnouncementEvent({
|
||||
const providerAnnouncementEvent = await persistProviderAnnouncementEvent({
|
||||
captureSessionId: session.captureSessionId,
|
||||
rawCapture: bufferedEvent.rawCapture,
|
||||
sender: bufferedEvent.sender
|
||||
});
|
||||
|
||||
session.providerAnnouncementEvents.push(providerAnnouncementEvent);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -2393,6 +2404,7 @@ function startAttentionConsentCaptureSession(rawCapture, sender) {
|
||||
status: "attention",
|
||||
captureSessionId: null,
|
||||
bufferedPreConsentEvents: [],
|
||||
providerAnnouncementEvents: [],
|
||||
timeoutId: null,
|
||||
page: buildConsentEventPageContext(rawCapture, sender),
|
||||
detectedAt: new Date().toISOString(),
|
||||
@@ -2440,11 +2452,14 @@ async function handlePreConsentTcfEvent(rawCapture, sender) {
|
||||
}
|
||||
|
||||
if (session?.status === "recording") {
|
||||
await persistProviderAnnouncementEvent({
|
||||
const providerAnnouncementEvent = await persistProviderAnnouncementEvent({
|
||||
captureSessionId: session.captureSessionId,
|
||||
rawCapture,
|
||||
sender
|
||||
});
|
||||
session.providerAnnouncementEvents =
|
||||
session.providerAnnouncementEvents ?? [];
|
||||
session.providerAnnouncementEvents.push(providerAnnouncementEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2452,6 +2467,12 @@ async function handlePreConsentTcfEvent(rawCapture, sender) {
|
||||
}
|
||||
|
||||
function getActiveConsentCaptureSessionId(sender) {
|
||||
const session = getActiveConsentCaptureSession(sender);
|
||||
|
||||
return session?.captureSessionId ?? null;
|
||||
}
|
||||
|
||||
function getActiveConsentCaptureSession(sender) {
|
||||
const sessionKey = getConsentCaptureSessionKey(sender);
|
||||
const session = sessionKey ? consentCaptureSessions.get(sessionKey) : null;
|
||||
|
||||
@@ -2459,7 +2480,7 @@ function getActiveConsentCaptureSessionId(sender) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return session.captureSessionId ?? null;
|
||||
return session;
|
||||
}
|
||||
|
||||
function hasCompletedConsentCaptureSessionForTab(tabId) {
|
||||
@@ -2492,6 +2513,7 @@ function completeConsentCaptureSession(sender) {
|
||||
status: "completed",
|
||||
captureSessionId: null,
|
||||
bufferedPreConsentEvents: [],
|
||||
providerAnnouncementEvents: [],
|
||||
timeoutId: null
|
||||
};
|
||||
|
||||
@@ -3094,16 +3116,59 @@ async function persistConsentExecutionEvent({
|
||||
});
|
||||
}
|
||||
|
||||
async function persistConsentDiffEvent({
|
||||
captureSessionId,
|
||||
providerAnnouncementEvent,
|
||||
consentExecutionEvent
|
||||
}) {
|
||||
const db = await openVendorGetDb();
|
||||
const now = new Date().toISOString();
|
||||
const consentDiff = buildConsentDiff({
|
||||
captureSessionId,
|
||||
providerAnnouncement: providerAnnouncementEvent,
|
||||
consentExecution: consentExecutionEvent,
|
||||
derivedAt: now
|
||||
});
|
||||
|
||||
return addConsentEventRecord(db, {
|
||||
eventType: "consent_diff",
|
||||
capturedAt: now,
|
||||
recordedAt: now,
|
||||
recordingSource: EVIDENCE_RECORDING_SOURCE,
|
||||
captureSessionId,
|
||||
stateFingerprint: consentExecutionEvent?.stateFingerprint ?? null,
|
||||
page: consentExecutionEvent?.page ?? null,
|
||||
rawEventName: "consent_diff",
|
||||
ruleId: CONSENT_DIFF_RULE_ID,
|
||||
ruleVersion: CONSENT_DIFF_RULE_VERSION,
|
||||
consentDiff,
|
||||
diagnostics: {
|
||||
consentEvidenceRole: "consentDiff",
|
||||
ruleId: CONSENT_DIFF_RULE_ID,
|
||||
ruleVersion: CONSENT_DIFF_RULE_VERSION,
|
||||
sourceProviderAnnouncementId: providerAnnouncementEvent?.id ?? null,
|
||||
sourceConsentExecutionId: consentExecutionEvent?.id ?? null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addConsentEventRecord(db, record) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(["consent_events"], "readwrite");
|
||||
const eventsStore = tx.objectStore("consent_events");
|
||||
const request = eventsStore.add(record);
|
||||
let storedRecord = record;
|
||||
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onsuccess = () => {
|
||||
storedRecord = {
|
||||
...record,
|
||||
id: request.result
|
||||
};
|
||||
};
|
||||
tx.onerror = () => reject(tx.error);
|
||||
tx.onabort = () => reject(tx.error);
|
||||
tx.oncomplete = () => resolve(record);
|
||||
tx.oncomplete = () => resolve(storedRecord);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ body {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -49,6 +50,11 @@ h3 {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 12px;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 760px;
|
||||
font-size: 13px;
|
||||
@@ -139,6 +145,165 @@ th {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.finding-overview {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.finding-note {
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.finding-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 4px;
|
||||
background: #334155;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.finding-list div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(160px, 220px) 1fr;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
padding: 10px 12px;
|
||||
background: #1f2937;
|
||||
}
|
||||
|
||||
.finding-list dt {
|
||||
color: #cbd5e1;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.finding-list dd {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: #e5edf5;
|
||||
}
|
||||
|
||||
.consent-diff {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 4px;
|
||||
background: #172033;
|
||||
}
|
||||
|
||||
.compact-definition-list {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.compact-definition-list div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(150px, 220px) 1fr;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.compact-definition-list dt {
|
||||
color: #cbd5e1;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.compact-definition-list dd {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.diff-note {
|
||||
max-width: none;
|
||||
font-size: 12px;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.consent-diff-primary {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.consent-diff-findings {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.consent-diff-findings li {
|
||||
padding: 9px 10px;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 4px;
|
||||
color: #e5edf5;
|
||||
background: #1f2937;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.consent-diff-finding-details summary {
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.consent-diff-finding-body {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #334155;
|
||||
}
|
||||
|
||||
.consent-change-categories,
|
||||
.consent-change-category-body {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.consent-change-category {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 4px;
|
||||
background: #172033;
|
||||
}
|
||||
|
||||
.consent-change-category summary,
|
||||
.consent-change-collapsed-list summary {
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.consent-change-list-block {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.inline-id-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.inline-id-list li {
|
||||
padding: 3px 6px;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 3px;
|
||||
background: #0f172a;
|
||||
font-size: 12px;
|
||||
color: #e5edf5;
|
||||
}
|
||||
|
||||
.inspector-table th,
|
||||
.inspector-table td {
|
||||
vertical-align: top;
|
||||
@@ -227,4 +392,12 @@ th {
|
||||
.inspector-table .inspector-explanation {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.finding-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.finding-list div {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<section class="consent-detail" aria-labelledby="consent-detail-title">
|
||||
<h2 id="consent-detail-title">Ausgewählter Consent-Zustand</h2>
|
||||
<div id="consent-detail-observation"></div>
|
||||
<div id="consent-detail-diff"></div>
|
||||
<div id="consent-detail-basics"></div>
|
||||
<div id="consent-detail-summary"></div>
|
||||
<div id="consent-detail-publisher"></div>
|
||||
|
||||
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@@ -0,0 +1,374 @@
|
||||
"use strict";
|
||||
|
||||
var CONSENT_DIFF_RULE_ID = "consent_diff";
|
||||
var CONSENT_DIFF_RULE_VERSION = 1;
|
||||
|
||||
const CONSENT_DIFF_SCALAR_AXES = [
|
||||
"tcString",
|
||||
"addtlConsent",
|
||||
"vendorListVersion",
|
||||
"cmpId",
|
||||
"cmpVersion",
|
||||
"tcfPolicyVersion",
|
||||
"gdprApplies",
|
||||
"cmpStatus"
|
||||
];
|
||||
|
||||
const CONSENT_DIFF_OBJECT_AXES = [
|
||||
"purpose.consents",
|
||||
"vendor.consents",
|
||||
"specialFeatureOptins"
|
||||
];
|
||||
|
||||
function buildConsentDiff({
|
||||
captureSessionId,
|
||||
providerAnnouncement,
|
||||
consentExecution,
|
||||
derivedAt
|
||||
}) {
|
||||
const providerRaw = cloneConsentDiffValue(
|
||||
providerAnnouncement?.rawTcData ?? null
|
||||
);
|
||||
const executionRaw = cloneConsentDiffValue(consentExecution?.rawTcData ?? null);
|
||||
const providerWork = buildConsentDiffWorkCopy(
|
||||
providerAnnouncement,
|
||||
providerRaw
|
||||
);
|
||||
const executionWork = buildConsentDiffWorkCopy(consentExecution, executionRaw);
|
||||
const axes = {};
|
||||
|
||||
CONSENT_DIFF_SCALAR_AXES.forEach((axis) => {
|
||||
axes[axis] = compareConsentDiffScalarAxis(
|
||||
axis,
|
||||
providerWork,
|
||||
executionWork
|
||||
);
|
||||
});
|
||||
|
||||
CONSENT_DIFF_OBJECT_AXES.forEach((axis) => {
|
||||
axes[axis] = compareConsentDiffObjectAxis(
|
||||
axis,
|
||||
providerWork,
|
||||
executionWork
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
ruleId: CONSENT_DIFF_RULE_ID,
|
||||
ruleVersion: CONSENT_DIFF_RULE_VERSION,
|
||||
derivedAt,
|
||||
captureSessionId,
|
||||
sources: {
|
||||
providerAnnouncement: buildConsentDiffSourceReference(
|
||||
providerAnnouncement
|
||||
),
|
||||
consentExecution: buildConsentDiffSourceReference(consentExecution)
|
||||
},
|
||||
sourceSelection: {
|
||||
providerAnnouncement:
|
||||
"first_provider_announcement_for_capture_session_available_in_memory",
|
||||
consentExecution: "current_useractioncomplete_execution_event"
|
||||
},
|
||||
comparisonAxes: [
|
||||
...CONSENT_DIFF_SCALAR_AXES,
|
||||
...CONSENT_DIFF_OBJECT_AXES
|
||||
],
|
||||
axes,
|
||||
notes: buildConsentDiffNotes(providerAnnouncement, consentExecution)
|
||||
};
|
||||
}
|
||||
|
||||
function buildConsentDiffWorkCopy(eventRecord, rawTcData) {
|
||||
return {
|
||||
eventRecord: cloneConsentDiffValue(eventRecord ?? null),
|
||||
rawTcData: rawTcData ?? null
|
||||
};
|
||||
}
|
||||
|
||||
function compareConsentDiffScalarAxis(axis, providerWork, executionWork) {
|
||||
const providerValue = readConsentDiffAxisValue(providerWork, axis);
|
||||
const executionValue = readConsentDiffAxisValue(executionWork, axis);
|
||||
|
||||
return {
|
||||
kind: "scalar",
|
||||
status: getConsentDiffScalarStatus(providerValue, executionValue),
|
||||
provider: buildConsentDiffAxisSide(providerValue),
|
||||
execution: buildConsentDiffAxisSide(executionValue)
|
||||
};
|
||||
}
|
||||
|
||||
function compareConsentDiffObjectAxis(axis, providerWork, executionWork) {
|
||||
const providerValue = readConsentDiffAxisValue(providerWork, axis);
|
||||
const executionValue = readConsentDiffAxisValue(executionWork, axis);
|
||||
const providerObject = isConsentDiffPlainObject(providerValue.value)
|
||||
? providerValue.value
|
||||
: null;
|
||||
const executionObject = isConsentDiffPlainObject(executionValue.value)
|
||||
? executionValue.value
|
||||
: null;
|
||||
const providerKeys = providerObject ? Object.keys(providerObject).sort() : [];
|
||||
const executionKeys = executionObject ? Object.keys(executionObject).sort() : [];
|
||||
const allKeys = Array.from(new Set([...providerKeys, ...executionKeys])).sort();
|
||||
const added = [];
|
||||
const removed = [];
|
||||
const changed = [];
|
||||
const unchanged = [];
|
||||
|
||||
allKeys.forEach((key) => {
|
||||
const inProvider = providerObject
|
||||
? Object.prototype.hasOwnProperty.call(providerObject, key)
|
||||
: false;
|
||||
const inExecution = executionObject
|
||||
? Object.prototype.hasOwnProperty.call(executionObject, key)
|
||||
: false;
|
||||
|
||||
if (inProvider && !inExecution) {
|
||||
removed.push({
|
||||
key,
|
||||
providerValue: cloneConsentDiffValue(providerObject[key])
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inProvider && inExecution) {
|
||||
added.push({
|
||||
key,
|
||||
executionValue: cloneConsentDiffValue(executionObject[key])
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
stableConsentDiffValue(providerObject[key]) ===
|
||||
stableConsentDiffValue(executionObject[key])
|
||||
) {
|
||||
unchanged.push(key);
|
||||
return;
|
||||
}
|
||||
|
||||
changed.push({
|
||||
key,
|
||||
providerValue: cloneConsentDiffValue(providerObject[key]),
|
||||
executionValue: cloneConsentDiffValue(executionObject[key])
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
kind: "object",
|
||||
status: getConsentDiffObjectStatus(
|
||||
providerValue,
|
||||
executionValue,
|
||||
added,
|
||||
removed,
|
||||
changed
|
||||
),
|
||||
provider: {
|
||||
present: providerValue.present,
|
||||
sourcePath: providerValue.sourcePath,
|
||||
keys: providerKeys
|
||||
},
|
||||
execution: {
|
||||
present: executionValue.present,
|
||||
sourcePath: executionValue.sourcePath,
|
||||
keys: executionKeys
|
||||
},
|
||||
addedKeys: added,
|
||||
removedKeys: removed,
|
||||
changedKeys: changed,
|
||||
unchangedKeys: unchanged,
|
||||
counts: {
|
||||
providerKeys: providerKeys.length,
|
||||
executionKeys: executionKeys.length,
|
||||
added: added.length,
|
||||
removed: removed.length,
|
||||
changed: changed.length,
|
||||
unchanged: unchanged.length
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function readConsentDiffAxisValue(workCopy, axis) {
|
||||
const rawValue = readConsentDiffPath(workCopy.rawTcData, axis);
|
||||
|
||||
if (rawValue.present) {
|
||||
return {
|
||||
...rawValue,
|
||||
sourcePath: `rawTcData.${axis}`
|
||||
};
|
||||
}
|
||||
|
||||
const eventValue = readConsentDiffPath(workCopy.eventRecord, axis);
|
||||
|
||||
if (eventValue.present) {
|
||||
return {
|
||||
...eventValue,
|
||||
sourcePath: axis
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
present: false,
|
||||
value: undefined,
|
||||
sourcePath: null
|
||||
};
|
||||
}
|
||||
|
||||
function readConsentDiffPath(value, path) {
|
||||
if (value === null || value === undefined) {
|
||||
return {
|
||||
present: false,
|
||||
value: undefined
|
||||
};
|
||||
}
|
||||
|
||||
const parts = path.split(".");
|
||||
let current = value;
|
||||
|
||||
for (const part of parts) {
|
||||
if (
|
||||
current === null ||
|
||||
current === undefined ||
|
||||
!Object.prototype.hasOwnProperty.call(Object(current), part)
|
||||
) {
|
||||
return {
|
||||
present: false,
|
||||
value: undefined
|
||||
};
|
||||
}
|
||||
|
||||
current = current[part];
|
||||
}
|
||||
|
||||
return {
|
||||
present: true,
|
||||
value: cloneConsentDiffValue(current)
|
||||
};
|
||||
}
|
||||
|
||||
function getConsentDiffScalarStatus(providerValue, executionValue) {
|
||||
if (!providerValue.present && !executionValue.present) {
|
||||
return "absent";
|
||||
}
|
||||
|
||||
if (providerValue.present && !executionValue.present) {
|
||||
return "removed";
|
||||
}
|
||||
|
||||
if (!providerValue.present && executionValue.present) {
|
||||
return "added";
|
||||
}
|
||||
|
||||
return stableConsentDiffValue(providerValue.value) ===
|
||||
stableConsentDiffValue(executionValue.value)
|
||||
? "unchanged"
|
||||
: "changed";
|
||||
}
|
||||
|
||||
function getConsentDiffObjectStatus(
|
||||
providerValue,
|
||||
executionValue,
|
||||
added,
|
||||
removed,
|
||||
changed
|
||||
) {
|
||||
if (!providerValue.present && !executionValue.present) {
|
||||
return "absent";
|
||||
}
|
||||
|
||||
if (providerValue.present && !executionValue.present) {
|
||||
return "removed";
|
||||
}
|
||||
|
||||
if (!providerValue.present && executionValue.present) {
|
||||
return "added";
|
||||
}
|
||||
|
||||
return added.length === 0 && removed.length === 0 && changed.length === 0
|
||||
? "unchanged"
|
||||
: "changed";
|
||||
}
|
||||
|
||||
function buildConsentDiffAxisSide(axisValue) {
|
||||
return {
|
||||
present: axisValue.present,
|
||||
sourcePath: axisValue.sourcePath,
|
||||
value: axisValue.present ? cloneConsentDiffValue(axisValue.value) : null
|
||||
};
|
||||
}
|
||||
|
||||
function buildConsentDiffSourceReference(eventRecord) {
|
||||
if (!eventRecord) {
|
||||
return {
|
||||
available: false,
|
||||
reason: "missing_source_event"
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
available: true,
|
||||
store: "consent_events",
|
||||
id: eventRecord.id ?? null,
|
||||
eventType: eventRecord.eventType ?? null,
|
||||
rawEventName: eventRecord.rawEventName ?? null,
|
||||
capturedAt: eventRecord.capturedAt ?? null,
|
||||
recordedAt: eventRecord.recordedAt ?? null,
|
||||
captureSessionId: eventRecord.captureSessionId ?? null
|
||||
};
|
||||
}
|
||||
|
||||
function buildConsentDiffNotes(providerAnnouncement, consentExecution) {
|
||||
const notes = [];
|
||||
|
||||
if (!providerAnnouncement) {
|
||||
notes.push("provider_announcement_source_missing");
|
||||
}
|
||||
|
||||
if (!consentExecution) {
|
||||
notes.push("consent_execution_source_missing");
|
||||
}
|
||||
|
||||
notes.push("primary_evidence_not_modified");
|
||||
notes.push("no_legal_assessment");
|
||||
notes.push("no_gvl_semantic_assessment");
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
function stableConsentDiffValue(value) {
|
||||
if (value === undefined) {
|
||||
return "[[undefined]]";
|
||||
}
|
||||
|
||||
if (value === null || typeof value !== "object") {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return `[${value.map((item) => stableConsentDiffValue(item)).join(",")}]`;
|
||||
}
|
||||
|
||||
return `{${Object.keys(value)
|
||||
.sort()
|
||||
.map((key) => `${JSON.stringify(key)}:${stableConsentDiffValue(value[key])}`)
|
||||
.join(",")}}`;
|
||||
}
|
||||
|
||||
function cloneConsentDiffValue(value) {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (value === null || typeof value !== "object") {
|
||||
return value;
|
||||
}
|
||||
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
}
|
||||
|
||||
function isConsentDiffPlainObject(value) {
|
||||
return (
|
||||
value !== null &&
|
||||
typeof value === "object" &&
|
||||
!Array.isArray(value)
|
||||
);
|
||||
}
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren