@wix/web5-core 1.63.48 → 1.63.50

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 (61) hide show
  1. package/dist/cjs/components/ui/PoweredByBadge.js +1 -1
  2. package/dist/cjs/components/ui/PoweredByBadge.js.map +1 -1
  3. package/dist/cjs/index.js +4 -26
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/esm/components/ui/PoweredByBadge.js +1 -1
  6. package/dist/esm/components/ui/PoweredByBadge.js.map +1 -1
  7. package/dist/esm/index.js +0 -3
  8. package/dist/esm/index.js.map +1 -1
  9. package/dist/types/components/ui/PoweredByBadge.d.ts +2 -2
  10. package/dist/types/components/ui/PoweredByBadge.d.ts.map +1 -1
  11. package/dist/types/index.d.ts +0 -1
  12. package/dist/types/index.d.ts.map +1 -1
  13. package/package.json +2 -2
  14. package/dist/cjs/privacy/consentGate.js +0 -129
  15. package/dist/cjs/privacy/consentGate.js.map +0 -1
  16. package/dist/cjs/privacy/consentOverride.js +0 -94
  17. package/dist/cjs/privacy/consentOverride.js.map +0 -1
  18. package/dist/cjs/privacy/detectProvider.js +0 -71
  19. package/dist/cjs/privacy/detectProvider.js.map +0 -1
  20. package/dist/cjs/privacy/index.js +0 -33
  21. package/dist/cjs/privacy/index.js.map +0 -1
  22. package/dist/cjs/privacy/providers/hostSuppliedProvider.js +0 -93
  23. package/dist/cjs/privacy/providers/hostSuppliedProvider.js.map +0 -1
  24. package/dist/cjs/privacy/providers/oneTrustProvider.js +0 -72
  25. package/dist/cjs/privacy/providers/oneTrustProvider.js.map +0 -1
  26. package/dist/cjs/privacy/providers/shopifyProvider.js +0 -181
  27. package/dist/cjs/privacy/providers/shopifyProvider.js.map +0 -1
  28. package/dist/cjs/privacy/types.js +0 -34
  29. package/dist/cjs/privacy/types.js.map +0 -1
  30. package/dist/esm/privacy/consentGate.js +0 -122
  31. package/dist/esm/privacy/consentGate.js.map +0 -1
  32. package/dist/esm/privacy/consentOverride.js +0 -87
  33. package/dist/esm/privacy/consentOverride.js.map +0 -1
  34. package/dist/esm/privacy/detectProvider.js +0 -65
  35. package/dist/esm/privacy/detectProvider.js.map +0 -1
  36. package/dist/esm/privacy/index.js +0 -8
  37. package/dist/esm/privacy/index.js.map +0 -1
  38. package/dist/esm/privacy/providers/hostSuppliedProvider.js +0 -85
  39. package/dist/esm/privacy/providers/hostSuppliedProvider.js.map +0 -1
  40. package/dist/esm/privacy/providers/oneTrustProvider.js +0 -66
  41. package/dist/esm/privacy/providers/oneTrustProvider.js.map +0 -1
  42. package/dist/esm/privacy/providers/shopifyProvider.js +0 -178
  43. package/dist/esm/privacy/providers/shopifyProvider.js.map +0 -1
  44. package/dist/esm/privacy/types.js +0 -30
  45. package/dist/esm/privacy/types.js.map +0 -1
  46. package/dist/types/privacy/consentGate.d.ts +0 -52
  47. package/dist/types/privacy/consentGate.d.ts.map +0 -1
  48. package/dist/types/privacy/consentOverride.d.ts +0 -33
  49. package/dist/types/privacy/consentOverride.d.ts.map +0 -1
  50. package/dist/types/privacy/detectProvider.d.ts +0 -25
  51. package/dist/types/privacy/detectProvider.d.ts.map +0 -1
  52. package/dist/types/privacy/index.d.ts +0 -8
  53. package/dist/types/privacy/index.d.ts.map +0 -1
  54. package/dist/types/privacy/providers/hostSuppliedProvider.d.ts +0 -29
  55. package/dist/types/privacy/providers/hostSuppliedProvider.d.ts.map +0 -1
  56. package/dist/types/privacy/providers/oneTrustProvider.d.ts +0 -15
  57. package/dist/types/privacy/providers/oneTrustProvider.d.ts.map +0 -1
  58. package/dist/types/privacy/providers/shopifyProvider.d.ts +0 -26
  59. package/dist/types/privacy/providers/shopifyProvider.d.ts.map +0 -1
  60. package/dist/types/privacy/types.d.ts +0 -32
  61. package/dist/types/privacy/types.d.ts.map +0 -1
@@ -1,122 +0,0 @@
1
- /**
2
- * The consent gate — DL #218 (revised).
3
- *
4
- * Legal cleared analytics/BI transmission to need no consent gate, so the old
5
- * `transmit()` + buffer machinery is gone. What analytics emitters used to route
6
- * through the gate now goes straight to the wire.
7
- *
8
- * Analytics AND performance telemetry both go straight to the wire now, so there
9
- * is nothing left to "transmit"-gate. What remains is the consent SIGNAL —
10
- * snapshot, engagement, provider — behind a single predicate:
11
- *
12
- * - `mayPersistToken()` — whether the visitor's OAuth token may be written to
13
- * localStorage. Persisting the token persists a cross-visit visitor identity
14
- * (its refresh token), which is the one thing consent still governs. Truth
15
- * table: granted → yes; denied → never; unknown → only once the visitor
16
- * engaged.
17
- *
18
- * Engagement (`unlockOnUserAction`, fired when the visitor submits a prompt)
19
- * resolves the `unknown` case — typing a question is a reason to remember this
20
- * visitor for the session — but it never overrides a `denied`.
21
- */
22
-
23
- import { UNKNOWN_CONSENT } from './types.js';
24
- const LOG_PREFIX = '[w5-consent]';
25
- const log = function () {
26
- try {
27
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
28
- args[_key] = arguments[_key];
29
- }
30
- console.debug(LOG_PREFIX, ...args);
31
- } catch {
32
- // A console that throws must never break anything.
33
- }
34
- };
35
- const describe = s => `analytics=${s.analytics} performance=${s.performance} marketing=${s.marketing}`;
36
- let snapshot = UNKNOWN_CONSENT;
37
- let engaged = false;
38
- let unsubscribeProvider = null;
39
- const listeners = new Set();
40
- const notify = () => {
41
- for (const listener of listeners) {
42
- try {
43
- listener();
44
- } catch {
45
- // A subscriber that throws must not stop the others.
46
- }
47
- }
48
- };
49
-
50
- /**
51
- * Whether the visitor's OAuth token may be persisted to `localStorage`.
52
- *
53
- * Persisting the token persists a cross-visit visitor identity (the refresh
54
- * token re-identifies the visitor on their next visit), so it answers to
55
- * consent — the one thing that still does. Explicit consent wins in both
56
- * directions; engagement resolves only the `unknown` case. The token still
57
- * lives in memory for the session regardless — only the write to storage is
58
- * gated.
59
- */
60
- export const mayPersistToken = () => {
61
- const state = snapshot.analytics;
62
- if (state === 'denied') {
63
- return false;
64
- }
65
- if (state === 'granted') {
66
- return true;
67
- }
68
- return engaged;
69
- };
70
-
71
- /**
72
- * The visitor did something deliberate — submitted a prompt — so we may remember
73
- * them for the session even though no CMP has answered. Unlocks the `unknown`
74
- * case only; it cannot override a `denied`.
75
- */
76
- export const unlockOnUserAction = () => {
77
- if (engaged) {
78
- return;
79
- }
80
- engaged = true;
81
- log('UNLOCK — visitor submitted a prompt');
82
- notify();
83
- };
84
- export const getConsentSnapshot = () => snapshot;
85
- export const subscribeToConsent = listener => {
86
- listeners.add(listener);
87
- return () => {
88
- listeners.delete(listener);
89
- };
90
- };
91
- const applySnapshot = next => {
92
- if (next.analytics === snapshot.analytics && next.marketing === snapshot.marketing && next.performance === snapshot.performance) {
93
- return;
94
- }
95
- log(`ANSWER — ${describe(snapshot)} → ${describe(next)}`);
96
- snapshot = Object.freeze({
97
- ...next
98
- });
99
- notify();
100
- };
101
-
102
- /**
103
- * Install the host's provider. Reads its current state immediately, *then*
104
- * subscribes — DL #217: a listener registered into an already-rendered page is
105
- * bound to an event that may have fired long before the bundle executed, so
106
- * subscribing alone would leave the gate permanently at `unknown`.
107
- */
108
- export const installConsentProvider = next => {
109
- unsubscribeProvider == null || unsubscribeProvider();
110
- const initial = next.read();
111
- log(`provider installed: ${next.name} — reads ${describe(initial)}`);
112
- applySnapshot(initial);
113
- unsubscribeProvider = next.subscribe(applySnapshot);
114
- };
115
- export const resetConsentGateForTests = () => {
116
- unsubscribeProvider == null || unsubscribeProvider();
117
- unsubscribeProvider = null;
118
- snapshot = UNKNOWN_CONSENT;
119
- engaged = false;
120
- listeners.clear();
121
- };
122
- //# sourceMappingURL=consentGate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["UNKNOWN_CONSENT","LOG_PREFIX","log","_len","arguments","length","args","Array","_key","console","debug","describe","s","analytics","performance","marketing","snapshot","engaged","unsubscribeProvider","listeners","Set","notify","listener","mayPersistToken","state","unlockOnUserAction","getConsentSnapshot","subscribeToConsent","add","delete","applySnapshot","next","Object","freeze","installConsentProvider","initial","read","name","subscribe","resetConsentGateForTests","clear"],"sources":["../../../src/privacy/consentGate.ts"],"sourcesContent":["/**\n * The consent gate — DL #218 (revised).\n *\n * Legal cleared analytics/BI transmission to need no consent gate, so the old\n * `transmit()` + buffer machinery is gone. What analytics emitters used to route\n * through the gate now goes straight to the wire.\n *\n * Analytics AND performance telemetry both go straight to the wire now, so there\n * is nothing left to \"transmit\"-gate. What remains is the consent SIGNAL —\n * snapshot, engagement, provider — behind a single predicate:\n *\n * - `mayPersistToken()` — whether the visitor's OAuth token may be written to\n * localStorage. Persisting the token persists a cross-visit visitor identity\n * (its refresh token), which is the one thing consent still governs. Truth\n * table: granted → yes; denied → never; unknown → only once the visitor\n * engaged.\n *\n * Engagement (`unlockOnUserAction`, fired when the visitor submits a prompt)\n * resolves the `unknown` case — typing a question is a reason to remember this\n * visitor for the session — but it never overrides a `denied`.\n */\n\nimport {\n UNKNOWN_CONSENT,\n type ConsentProvider,\n type ConsentSnapshot,\n} from './types';\n\nconst LOG_PREFIX = '[w5-consent]';\n\nconst log = (...args: unknown[]): void => {\n try {\n console.debug(LOG_PREFIX, ...args);\n } catch {\n // A console that throws must never break anything.\n }\n};\n\nconst describe = (s: ConsentSnapshot): string =>\n `analytics=${s.analytics} performance=${s.performance} marketing=${s.marketing}`;\n\ntype Listener = () => void;\n\nlet snapshot: ConsentSnapshot = UNKNOWN_CONSENT;\nlet engaged = false;\nlet unsubscribeProvider: (() => void) | null = null;\n\nconst listeners = new Set<Listener>();\n\nconst notify = (): void => {\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // A subscriber that throws must not stop the others.\n }\n }\n};\n\n/**\n * Whether the visitor's OAuth token may be persisted to `localStorage`.\n *\n * Persisting the token persists a cross-visit visitor identity (the refresh\n * token re-identifies the visitor on their next visit), so it answers to\n * consent — the one thing that still does. Explicit consent wins in both\n * directions; engagement resolves only the `unknown` case. The token still\n * lives in memory for the session regardless — only the write to storage is\n * gated.\n */\nexport const mayPersistToken = (): boolean => {\n const state = snapshot.analytics;\n if (state === 'denied') {\n return false;\n }\n if (state === 'granted') {\n return true;\n }\n return engaged;\n};\n\n/**\n * The visitor did something deliberate — submitted a prompt — so we may remember\n * them for the session even though no CMP has answered. Unlocks the `unknown`\n * case only; it cannot override a `denied`.\n */\nexport const unlockOnUserAction = (): void => {\n if (engaged) {\n return;\n }\n engaged = true;\n log('UNLOCK — visitor submitted a prompt');\n notify();\n};\n\nexport const getConsentSnapshot = (): ConsentSnapshot => snapshot;\n\nexport const subscribeToConsent = (listener: Listener): (() => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nconst applySnapshot = (next: ConsentSnapshot): void => {\n if (\n next.analytics === snapshot.analytics &&\n next.marketing === snapshot.marketing &&\n next.performance === snapshot.performance\n ) {\n return;\n }\n log(`ANSWER — ${describe(snapshot)} → ${describe(next)}`);\n snapshot = Object.freeze({ ...next });\n notify();\n};\n\n/**\n * Install the host's provider. Reads its current state immediately, *then*\n * subscribes — DL #217: a listener registered into an already-rendered page is\n * bound to an event that may have fired long before the bundle executed, so\n * subscribing alone would leave the gate permanently at `unknown`.\n */\nexport const installConsentProvider = (next: ConsentProvider): void => {\n unsubscribeProvider?.();\n const initial = next.read();\n log(`provider installed: ${next.name} — reads ${describe(initial)}`);\n applySnapshot(initial);\n unsubscribeProvider = next.subscribe(applySnapshot);\n};\n\nexport const resetConsentGateForTests = (): void => {\n unsubscribeProvider?.();\n unsubscribeProvider = null;\n snapshot = UNKNOWN_CONSENT;\n engaged = false;\n listeners.clear();\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SACEA,eAAe,QAGV,SAAS;AAEhB,MAAMC,UAAU,GAAG,cAAc;AAEjC,MAAMC,GAAG,GAAG,SAAAA,CAAA,EAA8B;EACxC,IAAI;IAAA,SAAAC,IAAA,GAAAC,SAAA,CAAAC,MAAA,EADUC,IAAI,OAAAC,KAAA,CAAAJ,IAAA,GAAAK,IAAA,MAAAA,IAAA,GAAAL,IAAA,EAAAK,IAAA;MAAJF,IAAI,CAAAE,IAAA,IAAAJ,SAAA,CAAAI,IAAA;IAAA;IAEhBC,OAAO,CAACC,KAAK,CAACT,UAAU,EAAE,GAAGK,IAAI,CAAC;EACpC,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ,CAAC;AAED,MAAMK,QAAQ,GAAIC,CAAkB,IAClC,aAAaA,CAAC,CAACC,SAAS,gBAAgBD,CAAC,CAACE,WAAW,cAAcF,CAAC,CAACG,SAAS,EAAE;AAIlF,IAAIC,QAAyB,GAAGhB,eAAe;AAC/C,IAAIiB,OAAO,GAAG,KAAK;AACnB,IAAIC,mBAAwC,GAAG,IAAI;AAEnD,MAAMC,SAAS,GAAG,IAAIC,GAAG,CAAW,CAAC;AAErC,MAAMC,MAAM,GAAGA,CAAA,KAAY;EACzB,KAAK,MAAMC,QAAQ,IAAIH,SAAS,EAAE;IAChC,IAAI;MACFG,QAAQ,CAAC,CAAC;IACZ,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;AACF,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,eAAe,GAAGA,CAAA,KAAe;EAC5C,MAAMC,KAAK,GAAGR,QAAQ,CAACH,SAAS;EAChC,IAAIW,KAAK,KAAK,QAAQ,EAAE;IACtB,OAAO,KAAK;EACd;EACA,IAAIA,KAAK,KAAK,SAAS,EAAE;IACvB,OAAO,IAAI;EACb;EACA,OAAOP,OAAO;AAChB,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMQ,kBAAkB,GAAGA,CAAA,KAAY;EAC5C,IAAIR,OAAO,EAAE;IACX;EACF;EACAA,OAAO,GAAG,IAAI;EACdf,GAAG,CAAC,qCAAqC,CAAC;EAC1CmB,MAAM,CAAC,CAAC;AACV,CAAC;AAED,OAAO,MAAMK,kBAAkB,GAAGA,CAAA,KAAuBV,QAAQ;AAEjE,OAAO,MAAMW,kBAAkB,GAAIL,QAAkB,IAAmB;EACtEH,SAAS,CAACS,GAAG,CAACN,QAAQ,CAAC;EACvB,OAAO,MAAM;IACXH,SAAS,CAACU,MAAM,CAACP,QAAQ,CAAC;EAC5B,CAAC;AACH,CAAC;AAED,MAAMQ,aAAa,GAAIC,IAAqB,IAAW;EACrD,IACEA,IAAI,CAAClB,SAAS,KAAKG,QAAQ,CAACH,SAAS,IACrCkB,IAAI,CAAChB,SAAS,KAAKC,QAAQ,CAACD,SAAS,IACrCgB,IAAI,CAACjB,WAAW,KAAKE,QAAQ,CAACF,WAAW,EACzC;IACA;EACF;EACAZ,GAAG,CAAC,YAAYS,QAAQ,CAACK,QAAQ,CAAC,QAAQL,QAAQ,CAACoB,IAAI,CAAC,EAAE,CAAC;EAC3Df,QAAQ,GAAGgB,MAAM,CAACC,MAAM,CAAC;IAAE,GAAGF;EAAK,CAAC,CAAC;EACrCV,MAAM,CAAC,CAAC;AACV,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMa,sBAAsB,GAAIH,IAAqB,IAAW;EACrEb,mBAAmB,YAAnBA,mBAAmB,CAAG,CAAC;EACvB,MAAMiB,OAAO,GAAGJ,IAAI,CAACK,IAAI,CAAC,CAAC;EAC3BlC,GAAG,CAAC,uBAAuB6B,IAAI,CAACM,IAAI,YAAY1B,QAAQ,CAACwB,OAAO,CAAC,EAAE,CAAC;EACpEL,aAAa,CAACK,OAAO,CAAC;EACtBjB,mBAAmB,GAAGa,IAAI,CAACO,SAAS,CAACR,aAAa,CAAC;AACrD,CAAC;AAED,OAAO,MAAMS,wBAAwB,GAAGA,CAAA,KAAY;EAClDrB,mBAAmB,YAAnBA,mBAAmB,CAAG,CAAC;EACvBA,mBAAmB,GAAG,IAAI;EAC1BF,QAAQ,GAAGhB,eAAe;EAC1BiB,OAAO,GAAG,KAAK;EACfE,SAAS,CAACqB,KAAK,CAAC,CAAC;AACnB,CAAC","ignoreList":[]}
@@ -1,87 +0,0 @@
1
- /**
2
- * Consent override (debug-only) — DL #218.
3
- *
4
- * Shopify only serves its cookie banner where consent is legally required, so
5
- * from most of the world `shouldShowBanner()` is `false` and there is no way to
6
- * reach the `unknown` or `denied` branches by hand. Development stores often
7
- * have no privacy configuration at all, which pins the gate at `unknown`
8
- * forever. Neither is a bug, and both make the interesting states unreachable
9
- * from a browser.
10
- *
11
- * This forces the gate's answer so all four rows of the truth table can be
12
- * walked on any store, in any region, published or not.
13
- *
14
- * ?w5consent=granted | denied | unknown (one-shot)
15
- * localStorage["w5_consent_override"] (sticky)
16
- *
17
- * **The two are not equally trusted, on purpose.** A link is something one
18
- * person can send another, so a query param may only ever *reduce* what is
19
- * collected: `denied` is honoured anywhere, while `granted` and `unknown` are
20
- * honoured only on a local origin. Turning tracking *on* for someone else by
21
- * sending them a URL is exactly the thing this whole feature exists to
22
- * prevent. On a real storefront, set the localStorage key in devtools — which
23
- * is a deliberate act by the person whose browser it is.
24
- */
25
-
26
- export const CONSENT_OVERRIDE_KEY = 'w5_consent_override';
27
- export const CONSENT_OVERRIDE_QUERY_PARAM = 'w5consent';
28
- const VALID = ['granted', 'denied', 'unknown'];
29
- const isConsentState = value => typeof value === 'string' && VALID.includes(value);
30
-
31
- /** Only a local origin may force a *more* permissive state from a URL. */
32
- const isLocalOrigin = () => {
33
- try {
34
- const {
35
- hostname
36
- } = window.location;
37
- return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local');
38
- } catch {
39
- return false;
40
- }
41
- };
42
- const readQueryParam = () => {
43
- try {
44
- const raw = new URLSearchParams(window.location.search).get(CONSENT_OVERRIDE_QUERY_PARAM);
45
- if (!isConsentState(raw)) {
46
- return null;
47
- }
48
- // `denied` can only ever collect less, so a link may set it anywhere.
49
- if (raw === 'denied' || isLocalOrigin()) {
50
- return raw;
51
- }
52
- console.warn(`[w5-consent] ignoring ?${CONSENT_OVERRIDE_QUERY_PARAM}=${raw} — only "denied" is accepted from a URL off a local origin. ` + `Set localStorage["${CONSENT_OVERRIDE_KEY}"] = "${raw}" instead.`);
53
- return null;
54
- } catch {
55
- return null;
56
- }
57
- };
58
- const readStorage = () => {
59
- try {
60
- const raw = localStorage.getItem(CONSENT_OVERRIDE_KEY);
61
- return isConsentState(raw) ? raw : null;
62
- } catch {
63
- return null;
64
- }
65
- };
66
-
67
- /** The forced consent state, or `null` when no override is active. */
68
- export const getConsentOverride = () => readQueryParam() ?? readStorage();
69
- export const overrideSnapshot = state => ({
70
- analytics: state,
71
- marketing: state,
72
- performance: state
73
- });
74
-
75
- /** Persist the override and apply it on the next load. `null` clears it. */
76
- export const setConsentOverride = state => {
77
- try {
78
- if (state) {
79
- localStorage.setItem(CONSENT_OVERRIDE_KEY, state);
80
- } else {
81
- localStorage.removeItem(CONSENT_OVERRIDE_KEY);
82
- }
83
- } catch {
84
- // Ignore — the override simply won't persist.
85
- }
86
- };
87
- //# sourceMappingURL=consentOverride.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["CONSENT_OVERRIDE_KEY","CONSENT_OVERRIDE_QUERY_PARAM","VALID","isConsentState","value","includes","isLocalOrigin","hostname","window","location","endsWith","readQueryParam","raw","URLSearchParams","search","get","console","warn","readStorage","localStorage","getItem","getConsentOverride","overrideSnapshot","state","analytics","marketing","performance","setConsentOverride","setItem","removeItem"],"sources":["../../../src/privacy/consentOverride.ts"],"sourcesContent":["/**\n * Consent override (debug-only) — DL #218.\n *\n * Shopify only serves its cookie banner where consent is legally required, so\n * from most of the world `shouldShowBanner()` is `false` and there is no way to\n * reach the `unknown` or `denied` branches by hand. Development stores often\n * have no privacy configuration at all, which pins the gate at `unknown`\n * forever. Neither is a bug, and both make the interesting states unreachable\n * from a browser.\n *\n * This forces the gate's answer so all four rows of the truth table can be\n * walked on any store, in any region, published or not.\n *\n * ?w5consent=granted | denied | unknown (one-shot)\n * localStorage[\"w5_consent_override\"] (sticky)\n *\n * **The two are not equally trusted, on purpose.** A link is something one\n * person can send another, so a query param may only ever *reduce* what is\n * collected: `denied` is honoured anywhere, while `granted` and `unknown` are\n * honoured only on a local origin. Turning tracking *on* for someone else by\n * sending them a URL is exactly the thing this whole feature exists to\n * prevent. On a real storefront, set the localStorage key in devtools — which\n * is a deliberate act by the person whose browser it is.\n */\n\nimport type { ConsentSnapshot, ConsentState } from './types';\n\nexport const CONSENT_OVERRIDE_KEY = 'w5_consent_override';\nexport const CONSENT_OVERRIDE_QUERY_PARAM = 'w5consent';\n\nconst VALID: readonly ConsentState[] = ['granted', 'denied', 'unknown'];\n\nconst isConsentState = (value: unknown): value is ConsentState =>\n typeof value === 'string' && (VALID as readonly string[]).includes(value);\n\n/** Only a local origin may force a *more* permissive state from a URL. */\nconst isLocalOrigin = (): boolean => {\n try {\n const { hostname } = window.location;\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname.endsWith('.local')\n );\n } catch {\n return false;\n }\n};\n\nconst readQueryParam = (): ConsentState | null => {\n try {\n const raw = new URLSearchParams(window.location.search).get(\n CONSENT_OVERRIDE_QUERY_PARAM,\n );\n if (!isConsentState(raw)) {\n return null;\n }\n // `denied` can only ever collect less, so a link may set it anywhere.\n if (raw === 'denied' || isLocalOrigin()) {\n return raw;\n }\n console.warn(\n `[w5-consent] ignoring ?${CONSENT_OVERRIDE_QUERY_PARAM}=${raw} — only \"denied\" is accepted from a URL off a local origin. ` +\n `Set localStorage[\"${CONSENT_OVERRIDE_KEY}\"] = \"${raw}\" instead.`,\n );\n return null;\n } catch {\n return null;\n }\n};\n\nconst readStorage = (): ConsentState | null => {\n try {\n const raw = localStorage.getItem(CONSENT_OVERRIDE_KEY);\n return isConsentState(raw) ? raw : null;\n } catch {\n return null;\n }\n};\n\n/** The forced consent state, or `null` when no override is active. */\nexport const getConsentOverride = (): ConsentState | null =>\n readQueryParam() ?? readStorage();\n\nexport const overrideSnapshot = (state: ConsentState): ConsentSnapshot => ({\n analytics: state,\n marketing: state,\n performance: state,\n});\n\n/** Persist the override and apply it on the next load. `null` clears it. */\nexport const setConsentOverride = (state: ConsentState | null): void => {\n try {\n if (state) {\n localStorage.setItem(CONSENT_OVERRIDE_KEY, state);\n } else {\n localStorage.removeItem(CONSENT_OVERRIDE_KEY);\n }\n } catch {\n // Ignore — the override simply won't persist.\n }\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,OAAO,MAAMA,oBAAoB,GAAG,qBAAqB;AACzD,OAAO,MAAMC,4BAA4B,GAAG,WAAW;AAEvD,MAAMC,KAA8B,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;AAEvE,MAAMC,cAAc,GAAIC,KAAc,IACpC,OAAOA,KAAK,KAAK,QAAQ,IAAKF,KAAK,CAAuBG,QAAQ,CAACD,KAAK,CAAC;;AAE3E;AACA,MAAME,aAAa,GAAGA,CAAA,KAAe;EACnC,IAAI;IACF,MAAM;MAAEC;IAAS,CAAC,GAAGC,MAAM,CAACC,QAAQ;IACpC,OACEF,QAAQ,KAAK,WAAW,IACxBA,QAAQ,KAAK,WAAW,IACxBA,QAAQ,CAACG,QAAQ,CAAC,QAAQ,CAAC;EAE/B,CAAC,CAAC,MAAM;IACN,OAAO,KAAK;EACd;AACF,CAAC;AAED,MAAMC,cAAc,GAAGA,CAAA,KAA2B;EAChD,IAAI;IACF,MAAMC,GAAG,GAAG,IAAIC,eAAe,CAACL,MAAM,CAACC,QAAQ,CAACK,MAAM,CAAC,CAACC,GAAG,CACzDd,4BACF,CAAC;IACD,IAAI,CAACE,cAAc,CAACS,GAAG,CAAC,EAAE;MACxB,OAAO,IAAI;IACb;IACA;IACA,IAAIA,GAAG,KAAK,QAAQ,IAAIN,aAAa,CAAC,CAAC,EAAE;MACvC,OAAOM,GAAG;IACZ;IACAI,OAAO,CAACC,IAAI,CACV,0BAA0BhB,4BAA4B,IAAIW,GAAG,8DAA8D,GACzH,qBAAqBZ,oBAAoB,SAASY,GAAG,YACzD,CAAC;IACD,OAAO,IAAI;EACb,CAAC,CAAC,MAAM;IACN,OAAO,IAAI;EACb;AACF,CAAC;AAED,MAAMM,WAAW,GAAGA,CAAA,KAA2B;EAC7C,IAAI;IACF,MAAMN,GAAG,GAAGO,YAAY,CAACC,OAAO,CAACpB,oBAAoB,CAAC;IACtD,OAAOG,cAAc,CAACS,GAAG,CAAC,GAAGA,GAAG,GAAG,IAAI;EACzC,CAAC,CAAC,MAAM;IACN,OAAO,IAAI;EACb;AACF,CAAC;;AAED;AACA,OAAO,MAAMS,kBAAkB,GAAGA,CAAA,KAChCV,cAAc,CAAC,CAAC,IAAIO,WAAW,CAAC,CAAC;AAEnC,OAAO,MAAMI,gBAAgB,GAAIC,KAAmB,KAAuB;EACzEC,SAAS,EAAED,KAAK;EAChBE,SAAS,EAAEF,KAAK;EAChBG,WAAW,EAAEH;AACf,CAAC,CAAC;;AAEF;AACA,OAAO,MAAMI,kBAAkB,GAAIJ,KAA0B,IAAW;EACtE,IAAI;IACF,IAAIA,KAAK,EAAE;MACTJ,YAAY,CAACS,OAAO,CAAC5B,oBAAoB,EAAEuB,KAAK,CAAC;IACnD,CAAC,MAAM;MACLJ,YAAY,CAACU,UAAU,CAAC7B,oBAAoB,CAAC;IAC/C;EACF,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ,CAAC","ignoreList":[]}
@@ -1,65 +0,0 @@
1
- /**
2
- * Provider selection — DL #218.
3
- *
4
- * Detected at boot, not configured per client, because a merchant can install
5
- * a CMP long after we deploy and the bundle is not the source of truth for a
6
- * store's behaviour (DL #214).
7
- *
8
- * Order is host-supplied → Shopify → OneTrust → none. A host that publishes an
9
- * answer outranks anything we could sniff, because it knows things we cannot:
10
- * a server-side consent record, a CMP behind its own abstraction, or a legal
11
- * position we have no business guessing at.
12
- *
13
- * When nothing is detected the gate keeps its default — everything `unknown`,
14
- * held until the visitor acts. That is the case on every Shopify store with no
15
- * privacy configuration, which today is most of them.
16
- */
17
-
18
- import { installConsentProvider } from './consentGate.js';
19
- import { getConsentOverride, overrideSnapshot } from './consentOverride.js';
20
- import { createHostSuppliedConsentProvider, hasHostSuppliedConsent } from './providers/hostSuppliedProvider.js';
21
- import { createShopifyConsentProvider, isShopifyHost } from './providers/shopifyProvider.js';
22
- import { createOneTrustConsentProvider, isOneTrustHost } from './providers/oneTrustProvider.js';
23
- export const detectConsentProvider = () => {
24
- // Debug override outranks every real signal, so the states a region or an
25
- // unconfigured store makes unreachable can still be walked by hand.
26
- const forced = getConsentOverride();
27
- if (forced) {
28
- console.warn(`[w5-consent] OVERRIDE ACTIVE — consent forced to "${forced}". This is a debug switch, not a real answer.`);
29
- return {
30
- name: `override:${forced}`,
31
- read: () => overrideSnapshot(forced),
32
- subscribe: () => () => {}
33
- };
34
- }
35
- if (hasHostSuppliedConsent()) {
36
- return createHostSuppliedConsentProvider();
37
- }
38
- if (isShopifyHost()) {
39
- return createShopifyConsentProvider();
40
- }
41
- if (isOneTrustHost()) {
42
- return createOneTrustConsentProvider();
43
- }
44
- return null;
45
- };
46
-
47
- /**
48
- * Install the detected provider, if any. Call once at boot, before the first
49
- * emitter runs — anything raised earlier is held rather than lost, but the
50
- * sooner this runs the less the buffer has to carry.
51
- */
52
- export const initConsentGate = () => {
53
- const provider = detectConsentProvider();
54
- if (provider) {
55
- installConsentProvider(provider);
56
- } else {
57
- try {
58
- console.debug('[w5-consent] no CMP detected (no host-supplied consent, no Shopify customerPrivacy, no OneTrust) — everything held until the visitor submits a prompt');
59
- } catch {
60
- /* never break on a console */
61
- }
62
- }
63
- return provider;
64
- };
65
- //# sourceMappingURL=detectProvider.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["installConsentProvider","getConsentOverride","overrideSnapshot","createHostSuppliedConsentProvider","hasHostSuppliedConsent","createShopifyConsentProvider","isShopifyHost","createOneTrustConsentProvider","isOneTrustHost","detectConsentProvider","forced","console","warn","name","read","subscribe","initConsentGate","provider","debug"],"sources":["../../../src/privacy/detectProvider.ts"],"sourcesContent":["/**\n * Provider selection — DL #218.\n *\n * Detected at boot, not configured per client, because a merchant can install\n * a CMP long after we deploy and the bundle is not the source of truth for a\n * store's behaviour (DL #214).\n *\n * Order is host-supplied → Shopify → OneTrust → none. A host that publishes an\n * answer outranks anything we could sniff, because it knows things we cannot:\n * a server-side consent record, a CMP behind its own abstraction, or a legal\n * position we have no business guessing at.\n *\n * When nothing is detected the gate keeps its default — everything `unknown`,\n * held until the visitor acts. That is the case on every Shopify store with no\n * privacy configuration, which today is most of them.\n */\n\nimport { installConsentProvider } from './consentGate';\nimport { getConsentOverride, overrideSnapshot } from './consentOverride';\nimport type { ConsentProvider } from './types';\nimport {\n createHostSuppliedConsentProvider,\n hasHostSuppliedConsent,\n} from './providers/hostSuppliedProvider';\nimport {\n createShopifyConsentProvider,\n isShopifyHost,\n} from './providers/shopifyProvider';\nimport {\n createOneTrustConsentProvider,\n isOneTrustHost,\n} from './providers/oneTrustProvider';\n\nexport const detectConsentProvider = (): ConsentProvider | null => {\n // Debug override outranks every real signal, so the states a region or an\n // unconfigured store makes unreachable can still be walked by hand.\n const forced = getConsentOverride();\n if (forced) {\n console.warn(\n `[w5-consent] OVERRIDE ACTIVE — consent forced to \"${forced}\". This is a debug switch, not a real answer.`,\n );\n return {\n name: `override:${forced}`,\n read: () => overrideSnapshot(forced),\n subscribe: () => () => {},\n };\n }\n if (hasHostSuppliedConsent()) {\n return createHostSuppliedConsentProvider();\n }\n if (isShopifyHost()) {\n return createShopifyConsentProvider();\n }\n if (isOneTrustHost()) {\n return createOneTrustConsentProvider();\n }\n return null;\n};\n\n/**\n * Install the detected provider, if any. Call once at boot, before the first\n * emitter runs — anything raised earlier is held rather than lost, but the\n * sooner this runs the less the buffer has to carry.\n */\nexport const initConsentGate = (): ConsentProvider | null => {\n const provider = detectConsentProvider();\n if (provider) {\n installConsentProvider(provider);\n } else {\n try {\n console.debug(\n '[w5-consent] no CMP detected (no host-supplied consent, no Shopify customerPrivacy, no OneTrust) — everything held until the visitor submits a prompt',\n );\n } catch {\n /* never break on a console */\n }\n }\n return provider;\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,sBAAsB,QAAQ,eAAe;AACtD,SAASC,kBAAkB,EAAEC,gBAAgB,QAAQ,mBAAmB;AAExE,SACEC,iCAAiC,EACjCC,sBAAsB,QACjB,kCAAkC;AACzC,SACEC,4BAA4B,EAC5BC,aAAa,QACR,6BAA6B;AACpC,SACEC,6BAA6B,EAC7BC,cAAc,QACT,8BAA8B;AAErC,OAAO,MAAMC,qBAAqB,GAAGA,CAAA,KAA8B;EACjE;EACA;EACA,MAAMC,MAAM,GAAGT,kBAAkB,CAAC,CAAC;EACnC,IAAIS,MAAM,EAAE;IACVC,OAAO,CAACC,IAAI,CACV,qDAAqDF,MAAM,+CAC7D,CAAC;IACD,OAAO;MACLG,IAAI,EAAE,YAAYH,MAAM,EAAE;MAC1BI,IAAI,EAAEA,CAAA,KAAMZ,gBAAgB,CAACQ,MAAM,CAAC;MACpCK,SAAS,EAAEA,CAAA,KAAM,MAAM,CAAC;IAC1B,CAAC;EACH;EACA,IAAIX,sBAAsB,CAAC,CAAC,EAAE;IAC5B,OAAOD,iCAAiC,CAAC,CAAC;EAC5C;EACA,IAAIG,aAAa,CAAC,CAAC,EAAE;IACnB,OAAOD,4BAA4B,CAAC,CAAC;EACvC;EACA,IAAIG,cAAc,CAAC,CAAC,EAAE;IACpB,OAAOD,6BAA6B,CAAC,CAAC;EACxC;EACA,OAAO,IAAI;AACb,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMS,eAAe,GAAGA,CAAA,KAA8B;EAC3D,MAAMC,QAAQ,GAAGR,qBAAqB,CAAC,CAAC;EACxC,IAAIQ,QAAQ,EAAE;IACZjB,sBAAsB,CAACiB,QAAQ,CAAC;EAClC,CAAC,MAAM;IACL,IAAI;MACFN,OAAO,CAACO,KAAK,CACX,uJACF,CAAC;IACH,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;EACA,OAAOD,QAAQ;AACjB,CAAC","ignoreList":[]}
@@ -1,8 +0,0 @@
1
- export { UNKNOWN_CONSENT } from './types.js';
2
- export { unlockOnUserAction, mayPersistToken, getConsentSnapshot, subscribeToConsent, installConsentProvider, resetConsentGateForTests } from './consentGate.js';
3
- export { detectConsentProvider, initConsentGate } from './detectProvider.js';
4
- export { CONSENT_OVERRIDE_KEY, CONSENT_OVERRIDE_QUERY_PARAM, getConsentOverride, setConsentOverride } from './consentOverride.js';
5
- export { createShopifyConsentProvider, isShopifyHost } from './providers/shopifyProvider.js';
6
- export { createOneTrustConsentProvider, isOneTrustHost } from './providers/oneTrustProvider.js';
7
- export { HOST_CONSENT_GLOBAL, createHostSuppliedConsentProvider, hasHostSuppliedConsent, publishHostConsent } from './providers/hostSuppliedProvider.js';
8
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["UNKNOWN_CONSENT","unlockOnUserAction","mayPersistToken","getConsentSnapshot","subscribeToConsent","installConsentProvider","resetConsentGateForTests","detectConsentProvider","initConsentGate","CONSENT_OVERRIDE_KEY","CONSENT_OVERRIDE_QUERY_PARAM","getConsentOverride","setConsentOverride","createShopifyConsentProvider","isShopifyHost","createOneTrustConsentProvider","isOneTrustHost","HOST_CONSENT_GLOBAL","createHostSuppliedConsentProvider","hasHostSuppliedConsent","publishHostConsent"],"sources":["../../../src/privacy/index.ts"],"sourcesContent":["export {\n type ConsentState,\n type ConsentPurpose,\n type GatedPurpose,\n type ConsentSnapshot,\n type ConsentProvider,\n UNKNOWN_CONSENT,\n} from './types';\n\nexport {\n unlockOnUserAction,\n mayPersistToken,\n getConsentSnapshot,\n subscribeToConsent,\n installConsentProvider,\n resetConsentGateForTests,\n} from './consentGate';\n\nexport { detectConsentProvider, initConsentGate } from './detectProvider';\n\nexport {\n CONSENT_OVERRIDE_KEY,\n CONSENT_OVERRIDE_QUERY_PARAM,\n getConsentOverride,\n setConsentOverride,\n} from './consentOverride';\n\nexport {\n createShopifyConsentProvider,\n isShopifyHost,\n} from './providers/shopifyProvider';\nexport {\n createOneTrustConsentProvider,\n isOneTrustHost,\n} from './providers/oneTrustProvider';\nexport {\n type HostConsentInput,\n HOST_CONSENT_GLOBAL,\n createHostSuppliedConsentProvider,\n hasHostSuppliedConsent,\n publishHostConsent,\n} from './providers/hostSuppliedProvider';\n"],"mappings":"AAAA,SAMEA,eAAe,QACV,SAAS;AAEhB,SACEC,kBAAkB,EAClBC,eAAe,EACfC,kBAAkB,EAClBC,kBAAkB,EAClBC,sBAAsB,EACtBC,wBAAwB,QACnB,eAAe;AAEtB,SAASC,qBAAqB,EAAEC,eAAe,QAAQ,kBAAkB;AAEzE,SACEC,oBAAoB,EACpBC,4BAA4B,EAC5BC,kBAAkB,EAClBC,kBAAkB,QACb,mBAAmB;AAE1B,SACEC,4BAA4B,EAC5BC,aAAa,QACR,6BAA6B;AACpC,SACEC,6BAA6B,EAC7BC,cAAc,QACT,8BAA8B;AACrC,SAEEC,mBAAmB,EACnBC,iCAAiC,EACjCC,sBAAsB,EACtBC,kBAAkB,QACb,kCAAkC","ignoreList":[]}
@@ -1,85 +0,0 @@
1
- /**
2
- * Host-supplied provider — DL #218.
3
- *
4
- * The escape hatch for every CMP we cannot detect: a host page that already
5
- * knows its visitor's answer publishes it, and the gate believes it. This is
6
- * how Circana, feature.com and any merchant on Cookiebot/Osano/Klaviyo reach
7
- * the gate without web5 learning each vendor's API.
8
- *
9
- * Two ways in, because hosts differ in when they know:
10
- * - `window.__web5_consent__` set before the bundle loads, read at install
11
- * - `publishHostConsent()` called at any time afterwards
12
- */
13
-
14
- import { UNKNOWN_CONSENT } from '../types.js';
15
- export const HOST_CONSENT_GLOBAL = '__web5_consent__';
16
-
17
- /** What a host may publish. Anything missing stays `unknown`. */
18
-
19
- const coerce = value => {
20
- if (value === true) {
21
- return 'granted';
22
- }
23
- if (value === false) {
24
- return 'denied';
25
- }
26
- if (value === 'granted' || value === 'denied' || value === 'unknown') {
27
- return value;
28
- }
29
- return 'unknown';
30
- };
31
- const normalize = input => {
32
- if (!input || typeof input !== 'object') {
33
- return UNKNOWN_CONSENT;
34
- }
35
- const analytics = coerce(input.analytics);
36
- return {
37
- analytics,
38
- marketing: coerce(input.marketing),
39
- // A host that speaks only about analytics is taken to mean the same for
40
- // load telemetry, which is the same wire and the same recipient.
41
- performance: input.performance === undefined ? analytics : coerce(input.performance)
42
- };
43
- };
44
- const readGlobal = () => {
45
- if (typeof window === 'undefined') {
46
- return UNKNOWN_CONSENT;
47
- }
48
- const raw = window[HOST_CONSENT_GLOBAL];
49
- return normalize(raw);
50
- };
51
- export const hasHostSuppliedConsent = () => {
52
- if (typeof window === 'undefined') {
53
- return false;
54
- }
55
- const raw = window[HOST_CONSENT_GLOBAL];
56
- return !!raw && typeof raw === 'object';
57
- };
58
- const subscribers = new Set();
59
- let published = null;
60
-
61
- /**
62
- * Called by the host — directly, or by the loader when it is handed consent in
63
- * its boot options — whenever the visitor's answer is known or changes.
64
- */
65
- export const publishHostConsent = input => {
66
- published = normalize(input);
67
- for (const subscriber of subscribers) {
68
- subscriber(published);
69
- }
70
- };
71
- export const createHostSuppliedConsentProvider = () => ({
72
- name: 'host-supplied',
73
- read: () => published ?? readGlobal(),
74
- subscribe(onChange) {
75
- subscribers.add(onChange);
76
- return () => {
77
- subscribers.delete(onChange);
78
- };
79
- }
80
- });
81
- export const __resetHostConsentForTests = () => {
82
- published = null;
83
- subscribers.clear();
84
- };
85
- //# sourceMappingURL=hostSuppliedProvider.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["UNKNOWN_CONSENT","HOST_CONSENT_GLOBAL","coerce","value","normalize","input","analytics","marketing","performance","undefined","readGlobal","window","raw","hasHostSuppliedConsent","subscribers","Set","published","publishHostConsent","subscriber","createHostSuppliedConsentProvider","name","read","subscribe","onChange","add","delete","__resetHostConsentForTests","clear"],"sources":["../../../../src/privacy/providers/hostSuppliedProvider.ts"],"sourcesContent":["/**\n * Host-supplied provider — DL #218.\n *\n * The escape hatch for every CMP we cannot detect: a host page that already\n * knows its visitor's answer publishes it, and the gate believes it. This is\n * how Circana, feature.com and any merchant on Cookiebot/Osano/Klaviyo reach\n * the gate without web5 learning each vendor's API.\n *\n * Two ways in, because hosts differ in when they know:\n * - `window.__web5_consent__` set before the bundle loads, read at install\n * - `publishHostConsent()` called at any time afterwards\n */\n\nimport {\n UNKNOWN_CONSENT,\n type ConsentProvider,\n type ConsentSnapshot,\n type ConsentState,\n} from '../types';\n\nexport const HOST_CONSENT_GLOBAL = '__web5_consent__';\n\n/** What a host may publish. Anything missing stays `unknown`. */\nexport interface HostConsentInput {\n analytics?: ConsentState | boolean;\n marketing?: ConsentState | boolean;\n performance?: ConsentState | boolean;\n}\n\nconst coerce = (value: ConsentState | boolean | undefined): ConsentState => {\n if (value === true) {\n return 'granted';\n }\n if (value === false) {\n return 'denied';\n }\n if (value === 'granted' || value === 'denied' || value === 'unknown') {\n return value;\n }\n return 'unknown';\n};\n\nconst normalize = (\n input: HostConsentInput | null | undefined,\n): ConsentSnapshot => {\n if (!input || typeof input !== 'object') {\n return UNKNOWN_CONSENT;\n }\n const analytics = coerce(input.analytics);\n return {\n analytics,\n marketing: coerce(input.marketing),\n // A host that speaks only about analytics is taken to mean the same for\n // load telemetry, which is the same wire and the same recipient.\n performance:\n input.performance === undefined ? analytics : coerce(input.performance),\n };\n};\n\nconst readGlobal = (): ConsentSnapshot => {\n if (typeof window === 'undefined') {\n return UNKNOWN_CONSENT;\n }\n const raw = (window as unknown as Record<string, unknown>)[\n HOST_CONSENT_GLOBAL\n ];\n return normalize(raw as HostConsentInput | undefined);\n};\n\nexport const hasHostSuppliedConsent = (): boolean => {\n if (typeof window === 'undefined') {\n return false;\n }\n const raw = (window as unknown as Record<string, unknown>)[\n HOST_CONSENT_GLOBAL\n ];\n return !!raw && typeof raw === 'object';\n};\n\nconst subscribers = new Set<(snapshot: ConsentSnapshot) => void>();\nlet published: ConsentSnapshot | null = null;\n\n/**\n * Called by the host — directly, or by the loader when it is handed consent in\n * its boot options — whenever the visitor's answer is known or changes.\n */\nexport const publishHostConsent = (input: HostConsentInput): void => {\n published = normalize(input);\n for (const subscriber of subscribers) {\n subscriber(published);\n }\n};\n\nexport const createHostSuppliedConsentProvider = (): ConsentProvider => ({\n name: 'host-supplied',\n\n read: () => published ?? readGlobal(),\n\n subscribe(onChange) {\n subscribers.add(onChange);\n return () => {\n subscribers.delete(onChange);\n };\n },\n});\n\nexport const __resetHostConsentForTests = (): void => {\n published = null;\n subscribers.clear();\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SACEA,eAAe,QAIV,UAAU;AAEjB,OAAO,MAAMC,mBAAmB,GAAG,kBAAkB;;AAErD;;AAOA,MAAMC,MAAM,GAAIC,KAAyC,IAAmB;EAC1E,IAAIA,KAAK,KAAK,IAAI,EAAE;IAClB,OAAO,SAAS;EAClB;EACA,IAAIA,KAAK,KAAK,KAAK,EAAE;IACnB,OAAO,QAAQ;EACjB;EACA,IAAIA,KAAK,KAAK,SAAS,IAAIA,KAAK,KAAK,QAAQ,IAAIA,KAAK,KAAK,SAAS,EAAE;IACpE,OAAOA,KAAK;EACd;EACA,OAAO,SAAS;AAClB,CAAC;AAED,MAAMC,SAAS,GACbC,KAA0C,IACtB;EACpB,IAAI,CAACA,KAAK,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE;IACvC,OAAOL,eAAe;EACxB;EACA,MAAMM,SAAS,GAAGJ,MAAM,CAACG,KAAK,CAACC,SAAS,CAAC;EACzC,OAAO;IACLA,SAAS;IACTC,SAAS,EAAEL,MAAM,CAACG,KAAK,CAACE,SAAS,CAAC;IAClC;IACA;IACAC,WAAW,EACTH,KAAK,CAACG,WAAW,KAAKC,SAAS,GAAGH,SAAS,GAAGJ,MAAM,CAACG,KAAK,CAACG,WAAW;EAC1E,CAAC;AACH,CAAC;AAED,MAAME,UAAU,GAAGA,CAAA,KAAuB;EACxC,IAAI,OAAOC,MAAM,KAAK,WAAW,EAAE;IACjC,OAAOX,eAAe;EACxB;EACA,MAAMY,GAAG,GAAID,MAAM,CACjBV,mBAAmB,CACpB;EACD,OAAOG,SAAS,CAACQ,GAAmC,CAAC;AACvD,CAAC;AAED,OAAO,MAAMC,sBAAsB,GAAGA,CAAA,KAAe;EACnD,IAAI,OAAOF,MAAM,KAAK,WAAW,EAAE;IACjC,OAAO,KAAK;EACd;EACA,MAAMC,GAAG,GAAID,MAAM,CACjBV,mBAAmB,CACpB;EACD,OAAO,CAAC,CAACW,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ;AACzC,CAAC;AAED,MAAME,WAAW,GAAG,IAAIC,GAAG,CAAsC,CAAC;AAClE,IAAIC,SAAiC,GAAG,IAAI;;AAE5C;AACA;AACA;AACA;AACA,OAAO,MAAMC,kBAAkB,GAAIZ,KAAuB,IAAW;EACnEW,SAAS,GAAGZ,SAAS,CAACC,KAAK,CAAC;EAC5B,KAAK,MAAMa,UAAU,IAAIJ,WAAW,EAAE;IACpCI,UAAU,CAACF,SAAS,CAAC;EACvB;AACF,CAAC;AAED,OAAO,MAAMG,iCAAiC,GAAGA,CAAA,MAAwB;EACvEC,IAAI,EAAE,eAAe;EAErBC,IAAI,EAAEA,CAAA,KAAML,SAAS,IAAIN,UAAU,CAAC,CAAC;EAErCY,SAASA,CAACC,QAAQ,EAAE;IAClBT,WAAW,CAACU,GAAG,CAACD,QAAQ,CAAC;IACzB,OAAO,MAAM;MACXT,WAAW,CAACW,MAAM,CAACF,QAAQ,CAAC;IAC9B,CAAC;EACH;AACF,CAAC,CAAC;AAEF,OAAO,MAAMG,0BAA0B,GAAGA,CAAA,KAAY;EACpDV,SAAS,GAAG,IAAI;EAChBF,WAAW,CAACa,KAAK,CAAC,CAAC;AACrB,CAAC","ignoreList":[]}
@@ -1,66 +0,0 @@
1
- /**
2
- * OneTrust provider — DL #218.
3
- *
4
- * Lifted from the consent check that used to live inside
5
- * `utils/analyticsEvents.ts`, with one deliberate behaviour change: that
6
- * function returned `true` when OneTrust was absent ("host is responsible for
7
- * loading OneTrust"), which on a Shopify storefront meant unconditional
8
- * default-allow. Absence is no longer this provider's problem — it is only
9
- * selected when OneTrust is actually on the page, and absence is handled by
10
- * the gate's own default.
11
- */
12
-
13
- /** OneTrust's default taxonomy: C0002 is the performance/analytics category. */
14
- const ANALYTICS_CATEGORY = 'C0002';
15
- /** C0004 is targeting/advertising. */
16
- const MARKETING_CATEGORY = 'C0004';
17
- const readGroups = () => {
18
- if (typeof window === 'undefined') {
19
- return null;
20
- }
21
- const groups = window.OnetrustActiveGroups;
22
- return typeof groups === 'string' ? groups : null;
23
- };
24
- export const isOneTrustHost = () => readGroups() !== null;
25
- const readSnapshot = () => {
26
- const groups = readGroups();
27
- if (groups !== null) {
28
- try {
29
- console.debug(`[w5-consent] onetrust: active groups "${groups}"`);
30
- } catch {
31
- /* never break on a console */
32
- }
33
- }
34
- if (groups === null) {
35
- return {
36
- analytics: 'unknown',
37
- marketing: 'unknown',
38
- performance: 'unknown'
39
- };
40
- }
41
- // OneTrust publishes the *active* groups, so a category that is absent from
42
- // a string OneTrust has written is a refusal, not silence.
43
- const analytics = groups.includes(ANALYTICS_CATEGORY) ? 'granted' : 'denied';
44
- const marketing = groups.includes(MARKETING_CATEGORY) ? 'granted' : 'denied';
45
- return {
46
- analytics,
47
- marketing,
48
- performance: analytics
49
- };
50
- };
51
- export const createOneTrustConsentProvider = () => ({
52
- name: 'onetrust',
53
- read: readSnapshot,
54
- subscribe(onChange) {
55
- const publish = () => onChange(readSnapshot());
56
- if (typeof window === 'undefined') {
57
- return () => {};
58
- }
59
- // OneTrust fires this on every banner interaction.
60
- window.addEventListener('OneTrustGroupsUpdated', publish);
61
- return () => {
62
- window.removeEventListener('OneTrustGroupsUpdated', publish);
63
- };
64
- }
65
- });
66
- //# sourceMappingURL=oneTrustProvider.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["ANALYTICS_CATEGORY","MARKETING_CATEGORY","readGroups","window","groups","OnetrustActiveGroups","isOneTrustHost","readSnapshot","console","debug","analytics","marketing","performance","includes","createOneTrustConsentProvider","name","read","subscribe","onChange","publish","addEventListener","removeEventListener"],"sources":["../../../../src/privacy/providers/oneTrustProvider.ts"],"sourcesContent":["/**\n * OneTrust provider — DL #218.\n *\n * Lifted from the consent check that used to live inside\n * `utils/analyticsEvents.ts`, with one deliberate behaviour change: that\n * function returned `true` when OneTrust was absent (\"host is responsible for\n * loading OneTrust\"), which on a Shopify storefront meant unconditional\n * default-allow. Absence is no longer this provider's problem — it is only\n * selected when OneTrust is actually on the page, and absence is handled by\n * the gate's own default.\n */\n\nimport type { ConsentProvider, ConsentSnapshot } from '../types';\n\n/** OneTrust's default taxonomy: C0002 is the performance/analytics category. */\nconst ANALYTICS_CATEGORY = 'C0002';\n/** C0004 is targeting/advertising. */\nconst MARKETING_CATEGORY = 'C0004';\n\nconst readGroups = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n const groups = (window as unknown as { OnetrustActiveGroups?: unknown })\n .OnetrustActiveGroups;\n return typeof groups === 'string' ? groups : null;\n};\n\nexport const isOneTrustHost = (): boolean => readGroups() !== null;\n\nconst readSnapshot = (): ConsentSnapshot => {\n const groups = readGroups();\n if (groups !== null) {\n try {\n console.debug(`[w5-consent] onetrust: active groups \"${groups}\"`);\n } catch {\n /* never break on a console */\n }\n }\n if (groups === null) {\n return {\n analytics: 'unknown',\n marketing: 'unknown',\n performance: 'unknown',\n };\n }\n // OneTrust publishes the *active* groups, so a category that is absent from\n // a string OneTrust has written is a refusal, not silence.\n const analytics = groups.includes(ANALYTICS_CATEGORY) ? 'granted' : 'denied';\n const marketing = groups.includes(MARKETING_CATEGORY) ? 'granted' : 'denied';\n return { analytics, marketing, performance: analytics };\n};\n\nexport const createOneTrustConsentProvider = (): ConsentProvider => ({\n name: 'onetrust',\n\n read: readSnapshot,\n\n subscribe(onChange) {\n const publish = () => onChange(readSnapshot());\n if (typeof window === 'undefined') {\n return () => {};\n }\n // OneTrust fires this on every banner interaction.\n window.addEventListener('OneTrustGroupsUpdated', publish);\n return () => {\n window.removeEventListener('OneTrustGroupsUpdated', publish);\n };\n },\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA,MAAMA,kBAAkB,GAAG,OAAO;AAClC;AACA,MAAMC,kBAAkB,GAAG,OAAO;AAElC,MAAMC,UAAU,GAAGA,CAAA,KAAqB;EACtC,IAAI,OAAOC,MAAM,KAAK,WAAW,EAAE;IACjC,OAAO,IAAI;EACb;EACA,MAAMC,MAAM,GAAID,MAAM,CACnBE,oBAAoB;EACvB,OAAO,OAAOD,MAAM,KAAK,QAAQ,GAAGA,MAAM,GAAG,IAAI;AACnD,CAAC;AAED,OAAO,MAAME,cAAc,GAAGA,CAAA,KAAeJ,UAAU,CAAC,CAAC,KAAK,IAAI;AAElE,MAAMK,YAAY,GAAGA,CAAA,KAAuB;EAC1C,MAAMH,MAAM,GAAGF,UAAU,CAAC,CAAC;EAC3B,IAAIE,MAAM,KAAK,IAAI,EAAE;IACnB,IAAI;MACFI,OAAO,CAACC,KAAK,CAAC,yCAAyCL,MAAM,GAAG,CAAC;IACnE,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;EACA,IAAIA,MAAM,KAAK,IAAI,EAAE;IACnB,OAAO;MACLM,SAAS,EAAE,SAAS;MACpBC,SAAS,EAAE,SAAS;MACpBC,WAAW,EAAE;IACf,CAAC;EACH;EACA;EACA;EACA,MAAMF,SAAS,GAAGN,MAAM,CAACS,QAAQ,CAACb,kBAAkB,CAAC,GAAG,SAAS,GAAG,QAAQ;EAC5E,MAAMW,SAAS,GAAGP,MAAM,CAACS,QAAQ,CAACZ,kBAAkB,CAAC,GAAG,SAAS,GAAG,QAAQ;EAC5E,OAAO;IAAES,SAAS;IAAEC,SAAS;IAAEC,WAAW,EAAEF;EAAU,CAAC;AACzD,CAAC;AAED,OAAO,MAAMI,6BAA6B,GAAGA,CAAA,MAAwB;EACnEC,IAAI,EAAE,UAAU;EAEhBC,IAAI,EAAET,YAAY;EAElBU,SAASA,CAACC,QAAQ,EAAE;IAClB,MAAMC,OAAO,GAAGA,CAAA,KAAMD,QAAQ,CAACX,YAAY,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAOJ,MAAM,KAAK,WAAW,EAAE;MACjC,OAAO,MAAM,CAAC,CAAC;IACjB;IACA;IACAA,MAAM,CAACiB,gBAAgB,CAAC,uBAAuB,EAAED,OAAO,CAAC;IACzD,OAAO,MAAM;MACXhB,MAAM,CAACkB,mBAAmB,CAAC,uBAAuB,EAAEF,OAAO,CAAC;IAC9D,CAAC;EACH;AACF,CAAC,CAAC","ignoreList":[]}