@wix/web5-core 1.63.47 → 1.63.49

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 (67) hide show
  1. package/dist/cjs/client/loaderAuthFetch.js +25 -0
  2. package/dist/cjs/client/loaderAuthFetch.js.map +1 -0
  3. package/dist/cjs/index.js +6 -28
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/esm/client/loaderAuthFetch.js +21 -0
  6. package/dist/esm/client/loaderAuthFetch.js.map +1 -0
  7. package/dist/esm/index.js +1 -4
  8. package/dist/esm/index.js.map +1 -1
  9. package/dist/types/client/loaderAuthFetch.d.ts +26 -0
  10. package/dist/types/client/loaderAuthFetch.d.ts.map +1 -0
  11. package/dist/types/index.d.ts +1 -2
  12. package/dist/types/index.d.ts.map +1 -1
  13. package/package.json +2 -2
  14. package/dist/cjs/client/wixAuthFetch.js +0 -73
  15. package/dist/cjs/client/wixAuthFetch.js.map +0 -1
  16. package/dist/cjs/privacy/consentGate.js +0 -129
  17. package/dist/cjs/privacy/consentGate.js.map +0 -1
  18. package/dist/cjs/privacy/consentOverride.js +0 -94
  19. package/dist/cjs/privacy/consentOverride.js.map +0 -1
  20. package/dist/cjs/privacy/detectProvider.js +0 -71
  21. package/dist/cjs/privacy/detectProvider.js.map +0 -1
  22. package/dist/cjs/privacy/index.js +0 -33
  23. package/dist/cjs/privacy/index.js.map +0 -1
  24. package/dist/cjs/privacy/providers/hostSuppliedProvider.js +0 -93
  25. package/dist/cjs/privacy/providers/hostSuppliedProvider.js.map +0 -1
  26. package/dist/cjs/privacy/providers/oneTrustProvider.js +0 -72
  27. package/dist/cjs/privacy/providers/oneTrustProvider.js.map +0 -1
  28. package/dist/cjs/privacy/providers/shopifyProvider.js +0 -181
  29. package/dist/cjs/privacy/providers/shopifyProvider.js.map +0 -1
  30. package/dist/cjs/privacy/types.js +0 -34
  31. package/dist/cjs/privacy/types.js.map +0 -1
  32. package/dist/esm/client/wixAuthFetch.js +0 -68
  33. package/dist/esm/client/wixAuthFetch.js.map +0 -1
  34. package/dist/esm/privacy/consentGate.js +0 -122
  35. package/dist/esm/privacy/consentGate.js.map +0 -1
  36. package/dist/esm/privacy/consentOverride.js +0 -87
  37. package/dist/esm/privacy/consentOverride.js.map +0 -1
  38. package/dist/esm/privacy/detectProvider.js +0 -65
  39. package/dist/esm/privacy/detectProvider.js.map +0 -1
  40. package/dist/esm/privacy/index.js +0 -8
  41. package/dist/esm/privacy/index.js.map +0 -1
  42. package/dist/esm/privacy/providers/hostSuppliedProvider.js +0 -85
  43. package/dist/esm/privacy/providers/hostSuppliedProvider.js.map +0 -1
  44. package/dist/esm/privacy/providers/oneTrustProvider.js +0 -66
  45. package/dist/esm/privacy/providers/oneTrustProvider.js.map +0 -1
  46. package/dist/esm/privacy/providers/shopifyProvider.js +0 -178
  47. package/dist/esm/privacy/providers/shopifyProvider.js.map +0 -1
  48. package/dist/esm/privacy/types.js +0 -30
  49. package/dist/esm/privacy/types.js.map +0 -1
  50. package/dist/types/client/wixAuthFetch.d.ts +0 -12
  51. package/dist/types/client/wixAuthFetch.d.ts.map +0 -1
  52. package/dist/types/privacy/consentGate.d.ts +0 -52
  53. package/dist/types/privacy/consentGate.d.ts.map +0 -1
  54. package/dist/types/privacy/consentOverride.d.ts +0 -33
  55. package/dist/types/privacy/consentOverride.d.ts.map +0 -1
  56. package/dist/types/privacy/detectProvider.d.ts +0 -25
  57. package/dist/types/privacy/detectProvider.d.ts.map +0 -1
  58. package/dist/types/privacy/index.d.ts +0 -8
  59. package/dist/types/privacy/index.d.ts.map +0 -1
  60. package/dist/types/privacy/providers/hostSuppliedProvider.d.ts +0 -29
  61. package/dist/types/privacy/providers/hostSuppliedProvider.d.ts.map +0 -1
  62. package/dist/types/privacy/providers/oneTrustProvider.d.ts +0 -15
  63. package/dist/types/privacy/providers/oneTrustProvider.d.ts.map +0 -1
  64. package/dist/types/privacy/providers/shopifyProvider.d.ts +0 -26
  65. package/dist/types/privacy/providers/shopifyProvider.d.ts.map +0 -1
  66. package/dist/types/privacy/types.d.ts +0 -32
  67. package/dist/types/privacy/types.d.ts.map +0 -1
@@ -1,34 +0,0 @@
1
- "use strict";
2
-
3
- exports.__esModule = true;
4
- exports.UNKNOWN_CONSENT = void 0;
5
- /**
6
- * Consent vocabulary shared by the gate and every provider.
7
- *
8
- * DL #218. Three states, because "we have not been told" is not the same
9
- * answer as "no" and must not be collapsed into one: a visitor who has not
10
- * been asked can still be unlocked by their own action, a visitor who
11
- * refused never can.
12
- */
13
-
14
- /**
15
- * `necessary` is never gated — auth tokens and the configuration fetch are
16
- * what make the widget work at all, and a visitor who loaded the page asked
17
- * for that much. Everything else answers to the gate.
18
- */
19
-
20
- const UNKNOWN_CONSENT = exports.UNKNOWN_CONSENT = Object.freeze({
21
- performance: 'unknown',
22
- analytics: 'unknown',
23
- marketing: 'unknown'
24
- });
25
-
26
- /**
27
- * A consent provider adapts one host's CMP to the gate.
28
- *
29
- * `read()` must answer synchronously with what is known *now*. Providers are
30
- * installed into an already-rendered storefront page (DL #217), so a provider
31
- * that only subscribes is inert — the event it waits for may have fired long
32
- * before the bundle executed. Always read first, then subscribe.
33
- */
34
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["UNKNOWN_CONSENT","exports","Object","freeze","performance","analytics","marketing"],"sources":["../../../src/privacy/types.ts"],"sourcesContent":["/**\n * Consent vocabulary shared by the gate and every provider.\n *\n * DL #218. Three states, because \"we have not been told\" is not the same\n * answer as \"no\" and must not be collapsed into one: a visitor who has not\n * been asked can still be unlocked by their own action, a visitor who\n * refused never can.\n */\n\nexport type ConsentState = 'granted' | 'denied' | 'unknown';\n\n/**\n * `necessary` is never gated — auth tokens and the configuration fetch are\n * what make the widget work at all, and a visitor who loaded the page asked\n * for that much. Everything else answers to the gate.\n */\nexport type ConsentPurpose =\n | 'necessary'\n | 'performance'\n | 'analytics'\n | 'marketing';\n\nexport type GatedPurpose = Exclude<ConsentPurpose, 'necessary'>;\n\nexport type ConsentSnapshot = Record<GatedPurpose, ConsentState>;\n\nexport const UNKNOWN_CONSENT: ConsentSnapshot = Object.freeze({\n performance: 'unknown',\n analytics: 'unknown',\n marketing: 'unknown',\n});\n\n/**\n * A consent provider adapts one host's CMP to the gate.\n *\n * `read()` must answer synchronously with what is known *now*. Providers are\n * installed into an already-rendered storefront page (DL #217), so a provider\n * that only subscribes is inert — the event it waits for may have fired long\n * before the bundle executed. Always read first, then subscribe.\n */\nexport interface ConsentProvider {\n readonly name: string;\n read(): ConsentSnapshot;\n subscribe(onChange: (snapshot: ConsentSnapshot) => void): () => void;\n}\n"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;;AAWO,MAAMA,eAAgC,GAAAC,OAAA,CAAAD,eAAA,GAAGE,MAAM,CAACC,MAAM,CAAC;EAC5DC,WAAW,EAAE,SAAS;EACtBC,SAAS,EAAE,SAAS;EACpBC,SAAS,EAAE;AACb,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA","ignoreList":[]}
@@ -1,68 +0,0 @@
1
- /**
2
- * Wix-authenticated `fetch` for Web5 BE calls from a host (third-party) origin.
3
- *
4
- * `wixapis.com`'s `/wix-assistant-web5/v1/turn` endpoint refuses
5
- * cross-origin anonymous calls (HTTP 500) — the only auth path that survives
6
- * CORS from a host (e.g. Shopify) origin is `@wix/sdk`'s `OAuthStrategy`, which
7
- * supplies the `Authorization` header the BE allowlists.
8
- *
9
- * Shared by every embed bundle that talks to the turn endpoint — the
10
- * placement bundle and the input-widget chip generator — so they all attach
11
- * the same token. Token caching uses the same `localStorage` key format
12
- * (`web5_tokens_<clientId>`) as `web50-server-ui`'s token cache, so the loader,
13
- * full app, placement, and input widget share one OAuth token instead of each
14
- * burning a `/oauth2/token` round-trip per page load.
15
- */
16
- import { createClient, OAuthStrategy } from '@wix/sdk';
17
- const TOKEN_CACHE_KEY_PREFIX = 'web5_tokens_';
18
- function tokenKey(clientId) {
19
- return `${TOKEN_CACHE_KEY_PREFIX}${clientId}`;
20
- }
21
- function loadTokens(client, clientId) {
22
- try {
23
- const raw = localStorage.getItem(tokenKey(clientId));
24
- if (!raw) return;
25
- client.auth.setTokens(JSON.parse(raw));
26
- } catch {
27
- try {
28
- localStorage.removeItem(tokenKey(clientId));
29
- } catch {
30
- /* ignore */
31
- }
32
- }
33
- }
34
- function saveTokens(client, clientId) {
35
- try {
36
- localStorage.setItem(tokenKey(clientId), JSON.stringify(client.auth.getTokens()));
37
- } catch {
38
- // privacy mode / quota — in-memory token still works for this session
39
- }
40
- }
41
-
42
- /**
43
- * Build a `fetch`-shaped function pre-bound to the given Wix client id. The
44
- * returned value is drop-in compatible with any `fetchImpl` option
45
- * (`placementFetch`, `fetchContextualChips`, …).
46
- *
47
- * Each call loads cached tokens before the request, then saves the (possibly
48
- * refreshed) tokens after the response headers arrive — we deliberately do NOT
49
- * wait for the streaming body; the SDK's refresh runs alongside the stream and
50
- * the saved tokens benefit subsequent calls.
51
- */
52
- export function createWixAuthFetch(clientId) {
53
- if (!clientId) {
54
- throw new Error('[web5-core] createWixAuthFetch: clientId is empty');
55
- }
56
- const client = createClient({
57
- auth: OAuthStrategy({
58
- clientId
59
- })
60
- });
61
- return async (input, init) => {
62
- loadTokens(client, clientId);
63
- const res = await client.fetchWithAuth(input, init);
64
- saveTokens(client, clientId);
65
- return res;
66
- };
67
- }
68
- //# sourceMappingURL=wixAuthFetch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["createClient","OAuthStrategy","TOKEN_CACHE_KEY_PREFIX","tokenKey","clientId","loadTokens","client","raw","localStorage","getItem","auth","setTokens","JSON","parse","removeItem","saveTokens","setItem","stringify","getTokens","createWixAuthFetch","Error","input","init","res","fetchWithAuth"],"sources":["../../../src/client/wixAuthFetch.ts"],"sourcesContent":["/**\n * Wix-authenticated `fetch` for Web5 BE calls from a host (third-party) origin.\n *\n * `wixapis.com`'s `/wix-assistant-web5/v1/turn` endpoint refuses\n * cross-origin anonymous calls (HTTP 500) — the only auth path that survives\n * CORS from a host (e.g. Shopify) origin is `@wix/sdk`'s `OAuthStrategy`, which\n * supplies the `Authorization` header the BE allowlists.\n *\n * Shared by every embed bundle that talks to the turn endpoint — the\n * placement bundle and the input-widget chip generator — so they all attach\n * the same token. Token caching uses the same `localStorage` key format\n * (`web5_tokens_<clientId>`) as `web50-server-ui`'s token cache, so the loader,\n * full app, placement, and input widget share one OAuth token instead of each\n * burning a `/oauth2/token` round-trip per page load.\n */\nimport { createClient, OAuthStrategy } from '@wix/sdk';\n\nconst TOKEN_CACHE_KEY_PREFIX = 'web5_tokens_';\n\ninterface SdkClient {\n fetchWithAuth: typeof fetch;\n auth: {\n setTokens: (tokens: unknown) => void;\n getTokens: () => unknown;\n };\n}\n\nfunction tokenKey(clientId: string): string {\n return `${TOKEN_CACHE_KEY_PREFIX}${clientId}`;\n}\n\nfunction loadTokens(client: SdkClient, clientId: string): void {\n try {\n const raw = localStorage.getItem(tokenKey(clientId));\n if (!raw) return;\n client.auth.setTokens(JSON.parse(raw));\n } catch {\n try {\n localStorage.removeItem(tokenKey(clientId));\n } catch {\n /* ignore */\n }\n }\n}\n\nfunction saveTokens(client: SdkClient, clientId: string): void {\n try {\n localStorage.setItem(\n tokenKey(clientId),\n JSON.stringify(client.auth.getTokens()),\n );\n } catch {\n // privacy mode / quota — in-memory token still works for this session\n }\n}\n\n/**\n * Build a `fetch`-shaped function pre-bound to the given Wix client id. The\n * returned value is drop-in compatible with any `fetchImpl` option\n * (`placementFetch`, `fetchContextualChips`, …).\n *\n * Each call loads cached tokens before the request, then saves the (possibly\n * refreshed) tokens after the response headers arrive — we deliberately do NOT\n * wait for the streaming body; the SDK's refresh runs alongside the stream and\n * the saved tokens benefit subsequent calls.\n */\nexport function createWixAuthFetch(clientId: string): typeof fetch {\n if (!clientId) {\n throw new Error('[web5-core] createWixAuthFetch: clientId is empty');\n }\n const client = createClient({\n auth: OAuthStrategy({ clientId }),\n }) as unknown as SdkClient;\n\n return (async (input: RequestInfo | URL, init?: RequestInit) => {\n loadTokens(client, clientId);\n const res = await client.fetchWithAuth(input, init);\n saveTokens(client, clientId);\n return res;\n }) as typeof fetch;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,YAAY,EAAEC,aAAa,QAAQ,UAAU;AAEtD,MAAMC,sBAAsB,GAAG,cAAc;AAU7C,SAASC,QAAQA,CAACC,QAAgB,EAAU;EAC1C,OAAO,GAAGF,sBAAsB,GAAGE,QAAQ,EAAE;AAC/C;AAEA,SAASC,UAAUA,CAACC,MAAiB,EAAEF,QAAgB,EAAQ;EAC7D,IAAI;IACF,MAAMG,GAAG,GAAGC,YAAY,CAACC,OAAO,CAACN,QAAQ,CAACC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAACG,GAAG,EAAE;IACVD,MAAM,CAACI,IAAI,CAACC,SAAS,CAACC,IAAI,CAACC,KAAK,CAACN,GAAG,CAAC,CAAC;EACxC,CAAC,CAAC,MAAM;IACN,IAAI;MACFC,YAAY,CAACM,UAAU,CAACX,QAAQ,CAACC,QAAQ,CAAC,CAAC;IAC7C,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ;AACF;AAEA,SAASW,UAAUA,CAACT,MAAiB,EAAEF,QAAgB,EAAQ;EAC7D,IAAI;IACFI,YAAY,CAACQ,OAAO,CAClBb,QAAQ,CAACC,QAAQ,CAAC,EAClBQ,IAAI,CAACK,SAAS,CAACX,MAAM,CAACI,IAAI,CAACQ,SAAS,CAAC,CAAC,CACxC,CAAC;EACH,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,kBAAkBA,CAACf,QAAgB,EAAgB;EACjE,IAAI,CAACA,QAAQ,EAAE;IACb,MAAM,IAAIgB,KAAK,CAAC,mDAAmD,CAAC;EACtE;EACA,MAAMd,MAAM,GAAGN,YAAY,CAAC;IAC1BU,IAAI,EAAET,aAAa,CAAC;MAAEG;IAAS,CAAC;EAClC,CAAC,CAAyB;EAE1B,OAAQ,OAAOiB,KAAwB,EAAEC,IAAkB,KAAK;IAC9DjB,UAAU,CAACC,MAAM,EAAEF,QAAQ,CAAC;IAC5B,MAAMmB,GAAG,GAAG,MAAMjB,MAAM,CAACkB,aAAa,CAACH,KAAK,EAAEC,IAAI,CAAC;IACnDP,UAAU,CAACT,MAAM,EAAEF,QAAQ,CAAC;IAC5B,OAAOmB,GAAG;EACZ,CAAC;AACH","ignoreList":[]}
@@ -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":[]}