@kywi-software/js 0.15.0 → 0.15.2
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/dist/audience/behavioral-collector.d.ts +2 -1
- package/dist/audience/behavioral-collector.d.ts.map +1 -1
- package/dist/audience/evaluate.d.ts +9 -1
- package/dist/audience/evaluate.d.ts.map +1 -1
- package/dist/audience/query-collector.d.ts +15 -0
- package/dist/audience/query-collector.d.ts.map +1 -0
- package/dist/audience/selfid-api.d.ts +57 -0
- package/dist/audience/selfid-api.d.ts.map +1 -0
- package/dist/audience/selfid-collector.d.ts +2 -0
- package/dist/audience/selfid-collector.d.ts.map +1 -1
- package/dist/consent.d.ts.map +1 -1
- package/dist/feed.d.ts.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kywi.esm.js +244 -128
- package/dist/kywi.esm.js.map +4 -4
- package/dist/kywi.js +244 -128
- package/dist/kywi.js.map +4 -4
- package/package.json +2 -2
package/dist/kywi.js
CHANGED
|
@@ -30,13 +30,15 @@ var Kywi = (() => {
|
|
|
30
30
|
bootAudienceEngine: () => bootAudienceEngine,
|
|
31
31
|
bootExperiments: () => bootExperiments,
|
|
32
32
|
clearDeniedCookies: () => clearDeniedCookies,
|
|
33
|
+
clearSelfId: () => clearSelfId,
|
|
33
34
|
enhanceNav: () => enhanceNav,
|
|
34
35
|
getConsentState: () => getConsentState,
|
|
35
36
|
hasConsent: () => hasConsent,
|
|
36
37
|
initNavMenus: () => initNavMenus,
|
|
37
38
|
openTransparencyPanel: () => openTransparencyPanel,
|
|
38
39
|
renderTransparencyPanel: () => renderTransparencyPanel,
|
|
39
|
-
setConsent: () => setConsent
|
|
40
|
+
setConsent: () => setConsent,
|
|
41
|
+
setSelfId: () => setSelfId
|
|
40
42
|
});
|
|
41
43
|
|
|
42
44
|
// src/context.ts
|
|
@@ -577,6 +579,98 @@ var Kywi = (() => {
|
|
|
577
579
|
return runtime;
|
|
578
580
|
}
|
|
579
581
|
|
|
582
|
+
// src/audience/selfid-collector.ts
|
|
583
|
+
var SELFID_KEY = "kywi_selfid";
|
|
584
|
+
function collectSelfId() {
|
|
585
|
+
const raw = readStorage(SELFID_KEY);
|
|
586
|
+
if (!raw) return {};
|
|
587
|
+
try {
|
|
588
|
+
const parsed = JSON.parse(raw);
|
|
589
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
590
|
+
return parsed;
|
|
591
|
+
} catch {
|
|
592
|
+
return {};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function storeSelfId(responses) {
|
|
596
|
+
try {
|
|
597
|
+
localStorage.setItem(SELFID_KEY, JSON.stringify(responses));
|
|
598
|
+
} catch {
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function clearStoredSelfId() {
|
|
602
|
+
try {
|
|
603
|
+
localStorage.removeItem(SELFID_KEY);
|
|
604
|
+
} catch {
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function readStorage(key) {
|
|
608
|
+
try {
|
|
609
|
+
return localStorage.getItem(key);
|
|
610
|
+
} catch {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/audience/selfid-api.ts
|
|
616
|
+
var DEFAULT_API_BASE = "/api/v1";
|
|
617
|
+
function resolveApiBase(explicit) {
|
|
618
|
+
if (explicit) return explicit;
|
|
619
|
+
const basePath = globalThis.Kywi?.context?.basePath;
|
|
620
|
+
if (!basePath || basePath === "/") return DEFAULT_API_BASE;
|
|
621
|
+
return `${basePath}/api/v1`;
|
|
622
|
+
}
|
|
623
|
+
function resolveCsrfToken(explicit) {
|
|
624
|
+
if (explicit !== void 0) return explicit;
|
|
625
|
+
return globalThis.Kywi?.context?.csrfToken ?? null;
|
|
626
|
+
}
|
|
627
|
+
function reloadPage() {
|
|
628
|
+
try {
|
|
629
|
+
window.location.reload();
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async function setSelfId(fields, options = {}) {
|
|
634
|
+
const merged = { ...collectSelfId() };
|
|
635
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
636
|
+
if (value === "") delete merged[key];
|
|
637
|
+
else merged[key] = value;
|
|
638
|
+
}
|
|
639
|
+
if (Object.keys(merged).length > 0) storeSelfId(merged);
|
|
640
|
+
else clearStoredSelfId();
|
|
641
|
+
const result = await submit(merged, options);
|
|
642
|
+
if (options.reload !== false && result !== null) reloadPage();
|
|
643
|
+
return result ?? { audience: null, persisted: false, fields: merged };
|
|
644
|
+
}
|
|
645
|
+
async function clearSelfId(options = {}) {
|
|
646
|
+
clearStoredSelfId();
|
|
647
|
+
const result = await submit({}, options);
|
|
648
|
+
if (options.reload !== false && result !== null) reloadPage();
|
|
649
|
+
return result ?? { audience: null, persisted: false, fields: {} };
|
|
650
|
+
}
|
|
651
|
+
async function submit(fields, options) {
|
|
652
|
+
const csrfToken = resolveCsrfToken(options.csrfToken);
|
|
653
|
+
try {
|
|
654
|
+
const res = await fetch(`${resolveApiBase(options.apiBase)}/kywi/self-id`, {
|
|
655
|
+
method: "POST",
|
|
656
|
+
headers: { "Content-Type": "application/json", ...csrfToken ? { "X-CSRF-Token": csrfToken } : {} },
|
|
657
|
+
body: JSON.stringify({
|
|
658
|
+
fields,
|
|
659
|
+
...options.audienceId ? { audienceId: options.audienceId } : {}
|
|
660
|
+
})
|
|
661
|
+
});
|
|
662
|
+
if (!res.ok) return null;
|
|
663
|
+
const body = await res.json().catch(() => null);
|
|
664
|
+
return {
|
|
665
|
+
audience: body?.data?.audience ?? null,
|
|
666
|
+
persisted: body?.data?.persisted === true,
|
|
667
|
+
fields
|
|
668
|
+
};
|
|
669
|
+
} catch {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
580
674
|
// ../core/dist/audiences/types.js
|
|
581
675
|
function createEmptySignals() {
|
|
582
676
|
return {
|
|
@@ -584,6 +678,7 @@ var Kywi = (() => {
|
|
|
584
678
|
referrer: { raw: null, domain: null, sourceType: "direct" },
|
|
585
679
|
session: { pageViewCount: 0, totalPageViews: 0, sessionDuration: 0, entryPage: "/", pagesVisited: [] },
|
|
586
680
|
behavioral: { categoryScores: {}, totalScore: 0 },
|
|
681
|
+
query: {},
|
|
587
682
|
selfId: {},
|
|
588
683
|
identity: { visitorId: "", isKnown: false, maLeadId: null },
|
|
589
684
|
adapters: {},
|
|
@@ -644,6 +739,10 @@ var Kywi = (() => {
|
|
|
644
739
|
return signals.session.entryPage;
|
|
645
740
|
if (ref === "session.pagesVisited")
|
|
646
741
|
return signals.session.pagesVisited.join(",");
|
|
742
|
+
if (ref.startsWith("query.")) {
|
|
743
|
+
const key = ref.slice(6);
|
|
744
|
+
return signals.query[key] ?? null;
|
|
745
|
+
}
|
|
647
746
|
if (ref.startsWith("selfId.")) {
|
|
648
747
|
const key = ref.slice(7);
|
|
649
748
|
return signals.selfId[key] ?? null;
|
|
@@ -811,6 +910,121 @@ var Kywi = (() => {
|
|
|
811
910
|
return { winningAudienceId: allMatchedIds[0] ?? null, allMatchedIds, traces };
|
|
812
911
|
}
|
|
813
912
|
|
|
913
|
+
// src/audience/query-collector.ts
|
|
914
|
+
function collectQuery() {
|
|
915
|
+
const out = {};
|
|
916
|
+
if (typeof location === "undefined") return out;
|
|
917
|
+
new URLSearchParams(location.search).forEach((value, key) => {
|
|
918
|
+
if (key !== "" && !(key in out)) out[key] = value;
|
|
919
|
+
});
|
|
920
|
+
return out;
|
|
921
|
+
}
|
|
922
|
+
function entryPageFor(currentPath) {
|
|
923
|
+
if (currentPath.includes("?")) return currentPath;
|
|
924
|
+
const search = typeof location === "undefined" ? "" : location.search;
|
|
925
|
+
return search ? `${currentPath}${search}` : currentPath;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/cookies.ts
|
|
929
|
+
var COOKIE_NAMES = {
|
|
930
|
+
SIGNALS: "kywi_signals",
|
|
931
|
+
VISITOR: "kywi_visitor",
|
|
932
|
+
UTM: "kywi_utm",
|
|
933
|
+
AUDIENCE: "kywi_audience",
|
|
934
|
+
OPTOUT: "kywi_optout",
|
|
935
|
+
KNOWN: "kywi_known",
|
|
936
|
+
PREVIEW_INIT: "kywi_preview_init"
|
|
937
|
+
};
|
|
938
|
+
function readCookies() {
|
|
939
|
+
const map = /* @__PURE__ */ new Map();
|
|
940
|
+
if (typeof document === "undefined") return map;
|
|
941
|
+
for (const pair of document.cookie.split(";")) {
|
|
942
|
+
const trimmed = pair.trim();
|
|
943
|
+
const eqIdx = trimmed.indexOf("=");
|
|
944
|
+
if (eqIdx < 0) continue;
|
|
945
|
+
map.set(trimmed.slice(0, eqIdx), decodeURIComponent(trimmed.slice(eqIdx + 1)));
|
|
946
|
+
}
|
|
947
|
+
return map;
|
|
948
|
+
}
|
|
949
|
+
function setCookie(name, value, maxAge) {
|
|
950
|
+
let str = `${name}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
|
|
951
|
+
if (maxAge !== void 0 && maxAge >= 0) str += `; Max-Age=${maxAge}`;
|
|
952
|
+
document.cookie = str;
|
|
953
|
+
}
|
|
954
|
+
function deleteCookie(name) {
|
|
955
|
+
document.cookie = `${name}=; Path=/; Max-Age=0`;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// src/consent.ts
|
|
959
|
+
var CONSENT_COOKIE = "kywi_consent";
|
|
960
|
+
var AUDIENCE_PIN_COOKIE = "kywi_audience";
|
|
961
|
+
var SWITCHER_PIN_PREFIX = "switcher:";
|
|
962
|
+
var COOKIE_CONSENT_CATEGORY = {
|
|
963
|
+
kywi_consent: "essential",
|
|
964
|
+
kywi_optout: "essential",
|
|
965
|
+
kywi_preview_init: "essential",
|
|
966
|
+
kywi_signals: "personalization",
|
|
967
|
+
kywi_visitor: "personalization",
|
|
968
|
+
kywi_utm: "personalization",
|
|
969
|
+
kywi_audience: "personalization",
|
|
970
|
+
kywi_known: "personalization",
|
|
971
|
+
kywi_entry: "personalization",
|
|
972
|
+
kywi_selfid: "personalization"
|
|
973
|
+
};
|
|
974
|
+
function getConsentState() {
|
|
975
|
+
const cookies = readCookies();
|
|
976
|
+
const raw = cookies.get(CONSENT_COOKIE);
|
|
977
|
+
if (!raw) return null;
|
|
978
|
+
let parsed;
|
|
979
|
+
try {
|
|
980
|
+
parsed = JSON.parse(raw);
|
|
981
|
+
} catch {
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
985
|
+
const obj = parsed;
|
|
986
|
+
return {
|
|
987
|
+
essential: true,
|
|
988
|
+
analytics: obj.analytics === true,
|
|
989
|
+
marketing: obj.marketing === true,
|
|
990
|
+
personalization: obj.personalization === true
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
function setConsent(prefs) {
|
|
994
|
+
const state = {
|
|
995
|
+
essential: true,
|
|
996
|
+
analytics: prefs.analytics ?? false,
|
|
997
|
+
marketing: prefs.marketing ?? false,
|
|
998
|
+
personalization: prefs.personalization ?? false
|
|
999
|
+
};
|
|
1000
|
+
setCookie(CONSENT_COOKIE, JSON.stringify(state), 31536e3);
|
|
1001
|
+
clearDeniedCookies(state);
|
|
1002
|
+
}
|
|
1003
|
+
function clearDeniedCookies(state) {
|
|
1004
|
+
if (typeof document === "undefined") return [];
|
|
1005
|
+
const cookies = readCookies();
|
|
1006
|
+
const deleted = [];
|
|
1007
|
+
for (const [name, category] of Object.entries(COOKIE_CONSENT_CATEGORY)) {
|
|
1008
|
+
if (category === "essential") continue;
|
|
1009
|
+
if (state && state[category] === true) continue;
|
|
1010
|
+
if (isDeliberatePreference(name, cookies.get(name))) continue;
|
|
1011
|
+
deleteCookie(name);
|
|
1012
|
+
deleted.push(name);
|
|
1013
|
+
}
|
|
1014
|
+
return deleted;
|
|
1015
|
+
}
|
|
1016
|
+
function isDeliberatePreference(name, value) {
|
|
1017
|
+
if (name !== AUDIENCE_PIN_COOKIE || value === void 0) return false;
|
|
1018
|
+
return value.startsWith(SWITCHER_PIN_PREFIX) && value.length > SWITCHER_PIN_PREFIX.length;
|
|
1019
|
+
}
|
|
1020
|
+
function hasConsent(category, options = {}) {
|
|
1021
|
+
if (options.requireConsent === false) return true;
|
|
1022
|
+
if (category === "essential") return true;
|
|
1023
|
+
const state = getConsentState();
|
|
1024
|
+
if (!state) return false;
|
|
1025
|
+
return state[category] === true;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
814
1028
|
// src/audience/behavioral-collector.ts
|
|
815
1029
|
var SESSION_KEY = "kywi_session";
|
|
816
1030
|
var TOTAL_VIEWS_KEY = "kywi_total_views";
|
|
@@ -843,10 +1057,11 @@ var Kywi = (() => {
|
|
|
843
1057
|
}
|
|
844
1058
|
return {};
|
|
845
1059
|
}
|
|
846
|
-
function collectBehavioral(currentPath, category) {
|
|
1060
|
+
function collectBehavioral(currentPath, category, gate = {}) {
|
|
847
1061
|
const session = getSession();
|
|
848
1062
|
session.pageViewCount += 1;
|
|
849
|
-
|
|
1063
|
+
const entryPage = session.entryPage || entryPageFor(currentPath);
|
|
1064
|
+
if (!session.entryPage && hasConsent("personalization", gate)) session.entryPage = entryPage;
|
|
850
1065
|
session.pagesVisited.push(currentPath);
|
|
851
1066
|
sessionStorage.setItem(SESSION_KEY, JSON.stringify(session));
|
|
852
1067
|
const totalViews = (Number(localStorage.getItem(TOTAL_VIEWS_KEY)) || 0) + 1;
|
|
@@ -858,26 +1073,11 @@ var Kywi = (() => {
|
|
|
858
1073
|
localStorage.setItem(CATEGORY_SCORES_KEY, JSON.stringify(categoryScores));
|
|
859
1074
|
}
|
|
860
1075
|
return {
|
|
861
|
-
session: { pageViewCount: session.pageViewCount, totalPageViews: totalViews, sessionDuration: Math.floor((Date.now() - sessionStart) / 1e3), entryPage
|
|
1076
|
+
session: { pageViewCount: session.pageViewCount, totalPageViews: totalViews, sessionDuration: Math.floor((Date.now() - sessionStart) / 1e3), entryPage, pagesVisited: session.pagesVisited },
|
|
862
1077
|
behavioral: { categoryScores, totalScore: 0 }
|
|
863
1078
|
};
|
|
864
1079
|
}
|
|
865
1080
|
|
|
866
|
-
// src/audience/selfid-collector.ts
|
|
867
|
-
var SELFID_KEY = "kywi_selfid";
|
|
868
|
-
function collectSelfId() {
|
|
869
|
-
const raw = localStorage.getItem(SELFID_KEY);
|
|
870
|
-
if (!raw) return {};
|
|
871
|
-
try {
|
|
872
|
-
return JSON.parse(raw);
|
|
873
|
-
} catch {
|
|
874
|
-
return {};
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
function storeSelfId(responses) {
|
|
878
|
-
localStorage.setItem(SELFID_KEY, JSON.stringify(responses));
|
|
879
|
-
}
|
|
880
|
-
|
|
881
1081
|
// src/audience/evaluate.ts
|
|
882
1082
|
function merge(base, partial) {
|
|
883
1083
|
return {
|
|
@@ -885,17 +1085,18 @@ var Kywi = (() => {
|
|
|
885
1085
|
referrer: partial.referrer ?? base.referrer,
|
|
886
1086
|
session: partial.session ?? base.session,
|
|
887
1087
|
behavioral: partial.behavioral ?? base.behavioral,
|
|
1088
|
+
query: { ...base.query, ...partial.query },
|
|
888
1089
|
selfId: { ...base.selfId, ...partial.selfId },
|
|
889
1090
|
identity: partial.identity ?? base.identity,
|
|
890
1091
|
adapters: { ...base.adapters, ...partial.adapters },
|
|
891
1092
|
meta: { ...base.meta, ...partial.meta }
|
|
892
1093
|
};
|
|
893
1094
|
}
|
|
894
|
-
function evaluateClientSide(audiences, serverSignals, currentPath, pageCategory) {
|
|
1095
|
+
function evaluateClientSide(audiences, serverSignals, currentPath, pageCategory, gate = {}) {
|
|
895
1096
|
let signals = merge(createEmptySignals(), serverSignals);
|
|
896
|
-
const client = collectBehavioral(currentPath, pageCategory);
|
|
1097
|
+
const client = collectBehavioral(currentPath, pageCategory, gate);
|
|
897
1098
|
const selfId = collectSelfId();
|
|
898
|
-
signals = merge(signals, { session: client.session, behavioral: client.behavioral, selfId, meta: { ...signals.meta, resolvedAt: "client" } });
|
|
1099
|
+
signals = merge(signals, { session: client.session, behavioral: client.behavioral, query: collectQuery(), selfId, meta: { ...signals.meta, resolvedAt: "client" } });
|
|
899
1100
|
const { winningAudienceId, allMatchedIds } = resolveAudiences(audiences, signals);
|
|
900
1101
|
const isOptedOut2 = signals.meta.isOptedOut;
|
|
901
1102
|
return { winningAudienceId: isOptedOut2 ? null : winningAudienceId, allMatchedIds, signals, isOptedOut: isOptedOut2 };
|
|
@@ -923,36 +1124,6 @@ var Kywi = (() => {
|
|
|
923
1124
|
}
|
|
924
1125
|
}
|
|
925
1126
|
|
|
926
|
-
// src/cookies.ts
|
|
927
|
-
var COOKIE_NAMES = {
|
|
928
|
-
SIGNALS: "kywi_signals",
|
|
929
|
-
VISITOR: "kywi_visitor",
|
|
930
|
-
UTM: "kywi_utm",
|
|
931
|
-
AUDIENCE: "kywi_audience",
|
|
932
|
-
OPTOUT: "kywi_optout",
|
|
933
|
-
KNOWN: "kywi_known",
|
|
934
|
-
PREVIEW_INIT: "kywi_preview_init"
|
|
935
|
-
};
|
|
936
|
-
function readCookies() {
|
|
937
|
-
const map = /* @__PURE__ */ new Map();
|
|
938
|
-
if (typeof document === "undefined") return map;
|
|
939
|
-
for (const pair of document.cookie.split(";")) {
|
|
940
|
-
const trimmed = pair.trim();
|
|
941
|
-
const eqIdx = trimmed.indexOf("=");
|
|
942
|
-
if (eqIdx < 0) continue;
|
|
943
|
-
map.set(trimmed.slice(0, eqIdx), decodeURIComponent(trimmed.slice(eqIdx + 1)));
|
|
944
|
-
}
|
|
945
|
-
return map;
|
|
946
|
-
}
|
|
947
|
-
function setCookie(name, value, maxAge) {
|
|
948
|
-
let str = `${name}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
|
|
949
|
-
if (maxAge !== void 0 && maxAge >= 0) str += `; Max-Age=${maxAge}`;
|
|
950
|
-
document.cookie = str;
|
|
951
|
-
}
|
|
952
|
-
function deleteCookie(name) {
|
|
953
|
-
document.cookie = `${name}=; Path=/; Max-Age=0`;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
1127
|
// src/audience/transparency.ts
|
|
957
1128
|
var COLLAPSED_KEY = "kywi_transparency_collapsed";
|
|
958
1129
|
var LEGACY_DISMISSED_KEY = "kywi_transparency_dismissed";
|
|
@@ -960,10 +1131,10 @@ var Kywi = (() => {
|
|
|
960
1131
|
var PANEL_ID = "kywi-transparency-panel";
|
|
961
1132
|
var TITLE_ID = "kywi-transparency-title";
|
|
962
1133
|
var SWITCHER_LABEL_ID = "kywi-transparency-switcher-label";
|
|
963
|
-
var
|
|
1134
|
+
var DEFAULT_API_BASE2 = "/api/v1";
|
|
964
1135
|
var lastState = null;
|
|
965
1136
|
var forcedOpen = false;
|
|
966
|
-
function
|
|
1137
|
+
function readStorage2(key) {
|
|
967
1138
|
try {
|
|
968
1139
|
return localStorage.getItem(key);
|
|
969
1140
|
} catch {
|
|
@@ -983,12 +1154,12 @@ var Kywi = (() => {
|
|
|
983
1154
|
}
|
|
984
1155
|
}
|
|
985
1156
|
function isCollapsed() {
|
|
986
|
-
if (
|
|
1157
|
+
if (readStorage2(LEGACY_DISMISSED_KEY) === "1") {
|
|
987
1158
|
removeStorage(LEGACY_DISMISSED_KEY);
|
|
988
1159
|
writeStorage(COLLAPSED_KEY, "1");
|
|
989
1160
|
return true;
|
|
990
1161
|
}
|
|
991
|
-
return
|
|
1162
|
+
return readStorage2(COLLAPSED_KEY) !== "0";
|
|
992
1163
|
}
|
|
993
1164
|
function setCollapsed(collapsed) {
|
|
994
1165
|
writeStorage(COLLAPSED_KEY, collapsed ? "1" : "0");
|
|
@@ -1007,7 +1178,7 @@ var Kywi = (() => {
|
|
|
1007
1178
|
return readCookies().has(COOKIE_NAMES.AUDIENCE);
|
|
1008
1179
|
}
|
|
1009
1180
|
async function pinAudience(audienceId, opts = {}) {
|
|
1010
|
-
const apiBase = opts.apiBase ??
|
|
1181
|
+
const apiBase = opts.apiBase ?? DEFAULT_API_BASE2;
|
|
1011
1182
|
try {
|
|
1012
1183
|
const res = await fetch(`${apiBase}/kywi/audience`, {
|
|
1013
1184
|
method: "POST",
|
|
@@ -1269,74 +1440,6 @@ var Kywi = (() => {
|
|
|
1269
1440
|
return { audienceId: m("audience-id"), experimentId: m("experiment-id"), variantId: m("variant-id"), visitorId: m("visitor-id") ?? "" };
|
|
1270
1441
|
}
|
|
1271
1442
|
|
|
1272
|
-
// src/consent.ts
|
|
1273
|
-
var CONSENT_COOKIE = "kywi_consent";
|
|
1274
|
-
var AUDIENCE_PIN_COOKIE = "kywi_audience";
|
|
1275
|
-
var SWITCHER_PIN_PREFIX = "switcher:";
|
|
1276
|
-
var COOKIE_CONSENT_CATEGORY = {
|
|
1277
|
-
kywi_consent: "essential",
|
|
1278
|
-
kywi_optout: "essential",
|
|
1279
|
-
kywi_preview_init: "essential",
|
|
1280
|
-
kywi_signals: "personalization",
|
|
1281
|
-
kywi_visitor: "personalization",
|
|
1282
|
-
kywi_utm: "personalization",
|
|
1283
|
-
kywi_audience: "personalization",
|
|
1284
|
-
kywi_known: "personalization"
|
|
1285
|
-
};
|
|
1286
|
-
function getConsentState() {
|
|
1287
|
-
const cookies = readCookies();
|
|
1288
|
-
const raw = cookies.get(CONSENT_COOKIE);
|
|
1289
|
-
if (!raw) return null;
|
|
1290
|
-
let parsed;
|
|
1291
|
-
try {
|
|
1292
|
-
parsed = JSON.parse(raw);
|
|
1293
|
-
} catch {
|
|
1294
|
-
return null;
|
|
1295
|
-
}
|
|
1296
|
-
if (typeof parsed !== "object" || parsed === null) return null;
|
|
1297
|
-
const obj = parsed;
|
|
1298
|
-
return {
|
|
1299
|
-
essential: true,
|
|
1300
|
-
analytics: obj.analytics === true,
|
|
1301
|
-
marketing: obj.marketing === true,
|
|
1302
|
-
personalization: obj.personalization === true
|
|
1303
|
-
};
|
|
1304
|
-
}
|
|
1305
|
-
function setConsent(prefs) {
|
|
1306
|
-
const state = {
|
|
1307
|
-
essential: true,
|
|
1308
|
-
analytics: prefs.analytics ?? false,
|
|
1309
|
-
marketing: prefs.marketing ?? false,
|
|
1310
|
-
personalization: prefs.personalization ?? false
|
|
1311
|
-
};
|
|
1312
|
-
setCookie(CONSENT_COOKIE, JSON.stringify(state), 31536e3);
|
|
1313
|
-
clearDeniedCookies(state);
|
|
1314
|
-
}
|
|
1315
|
-
function clearDeniedCookies(state) {
|
|
1316
|
-
if (typeof document === "undefined") return [];
|
|
1317
|
-
const cookies = readCookies();
|
|
1318
|
-
const deleted = [];
|
|
1319
|
-
for (const [name, category] of Object.entries(COOKIE_CONSENT_CATEGORY)) {
|
|
1320
|
-
if (category === "essential") continue;
|
|
1321
|
-
if (state && state[category] === true) continue;
|
|
1322
|
-
if (isDeliberatePreference(name, cookies.get(name))) continue;
|
|
1323
|
-
deleteCookie(name);
|
|
1324
|
-
deleted.push(name);
|
|
1325
|
-
}
|
|
1326
|
-
return deleted;
|
|
1327
|
-
}
|
|
1328
|
-
function isDeliberatePreference(name, value) {
|
|
1329
|
-
if (name !== AUDIENCE_PIN_COOKIE || value === void 0) return false;
|
|
1330
|
-
return value.startsWith(SWITCHER_PIN_PREFIX) && value.length > SWITCHER_PIN_PREFIX.length;
|
|
1331
|
-
}
|
|
1332
|
-
function hasConsent(category, options = {}) {
|
|
1333
|
-
if (options.requireConsent === false) return true;
|
|
1334
|
-
if (category === "essential") return true;
|
|
1335
|
-
const state = getConsentState();
|
|
1336
|
-
if (!state) return false;
|
|
1337
|
-
return state[category] === true;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
1443
|
// src/audience/adapter-runner.ts
|
|
1341
1444
|
var CACHE_PREFIX = "kywi_adapter_";
|
|
1342
1445
|
function getCached(adapterId) {
|
|
@@ -1659,7 +1762,7 @@ var Kywi = (() => {
|
|
|
1659
1762
|
}
|
|
1660
1763
|
|
|
1661
1764
|
// src/audience/index.ts
|
|
1662
|
-
function
|
|
1765
|
+
function resolveApiBase2(explicit) {
|
|
1663
1766
|
if (explicit) return explicit;
|
|
1664
1767
|
const basePath = Kywi.context?.basePath;
|
|
1665
1768
|
if (!basePath || basePath === "/") return "/api/v1";
|
|
@@ -1667,7 +1770,7 @@ var Kywi = (() => {
|
|
|
1667
1770
|
}
|
|
1668
1771
|
async function bootAudienceEngine(config) {
|
|
1669
1772
|
const { audiences, currentPath, pageCategory, serverSignals } = config;
|
|
1670
|
-
const apiBase =
|
|
1773
|
+
const apiBase = resolveApiBase2(config.apiBase);
|
|
1671
1774
|
checkPreviewInit();
|
|
1672
1775
|
const serverMeta = readServerMeta();
|
|
1673
1776
|
const previewId = getPreviewAudienceId();
|
|
@@ -1694,7 +1797,7 @@ var Kywi = (() => {
|
|
|
1694
1797
|
const adapterResults = await runClientAdapters(config.adapters, merged, apiBase);
|
|
1695
1798
|
merged.adapters = { ...merged.adapters, ...adapterResults };
|
|
1696
1799
|
}
|
|
1697
|
-
const result = evaluateClientSide(audiences, merged, currentPath, pageCategory);
|
|
1800
|
+
const result = evaluateClientSide(audiences, merged, currentPath, pageCategory, consentGate);
|
|
1698
1801
|
const rawPin = cookies.get(COOKIE_NAMES.AUDIENCE) ?? null;
|
|
1699
1802
|
const pinnedId = rawPin?.startsWith("switcher:") ? rawPin.slice("switcher:".length) || null : rawPin;
|
|
1700
1803
|
const isPinned = !result.isOptedOut && pinnedId !== null && activeAudiences.some((a) => a.id === pinnedId);
|
|
@@ -1798,6 +1901,19 @@ var Kywi = (() => {
|
|
|
1798
1901
|
}
|
|
1799
1902
|
}
|
|
1800
1903
|
},
|
|
1904
|
+
/**
|
|
1905
|
+
* Record who the visitor says they are, and resolve their audience
|
|
1906
|
+
* (kywi-cms#200). Writes the browser-side answers, posts them to the
|
|
1907
|
+
* consent-gated runtime endpoint, and reloads so the SERVER can re-render
|
|
1908
|
+
* with the resolved audience pinned — which is the only way to reach a
|
|
1909
|
+
* variant arm, since every losing arm is pruned server-side (#167).
|
|
1910
|
+
*
|
|
1911
|
+
* On the global because a site's own "What brings you here?" control needs
|
|
1912
|
+
* it: the built-in widget's write path is module-private.
|
|
1913
|
+
*/
|
|
1914
|
+
setSelfId,
|
|
1915
|
+
/** Forget the visitor's self-identification and un-pin their audience. */
|
|
1916
|
+
clearSelfId,
|
|
1801
1917
|
getEntity: null,
|
|
1802
1918
|
getFeed: null,
|
|
1803
1919
|
renderFeed: null,
|