@dreamdata_io/verify-tracking 0.1.0

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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +756 -0
  3. package/dist/ai/checker.js +222 -0
  4. package/dist/ai/checker.js.map +1 -0
  5. package/dist/ai/checklist.js +190 -0
  6. package/dist/ai/checklist.js.map +1 -0
  7. package/dist/ai/cli-presets.js +27 -0
  8. package/dist/ai/cli-presets.js.map +1 -0
  9. package/dist/ai/evidence-view.js +143 -0
  10. package/dist/ai/evidence-view.js.map +1 -0
  11. package/dist/ai/local-cli.js +58 -0
  12. package/dist/ai/local-cli.js.map +1 -0
  13. package/dist/ai/openai-compatible.js +78 -0
  14. package/dist/ai/openai-compatible.js.map +1 -0
  15. package/dist/ai/response-parser.js +105 -0
  16. package/dist/ai/response-parser.js.map +1 -0
  17. package/dist/ai/types.js +2 -0
  18. package/dist/ai/types.js.map +1 -0
  19. package/dist/analysis/consent-mode.js +225 -0
  20. package/dist/analysis/consent-mode.js.map +1 -0
  21. package/dist/analysis/deployment.js +2658 -0
  22. package/dist/analysis/deployment.js.map +1 -0
  23. package/dist/analysis/diagnosis/copy.en.js +303 -0
  24. package/dist/analysis/diagnosis/copy.en.js.map +1 -0
  25. package/dist/analysis/diagnosis/diagnose.js +172 -0
  26. package/dist/analysis/diagnosis/diagnose.js.map +1 -0
  27. package/dist/analysis/diagnosis/docs.js +118 -0
  28. package/dist/analysis/diagnosis/docs.js.map +1 -0
  29. package/dist/analysis/diagnosis/index.js +5 -0
  30. package/dist/analysis/diagnosis/index.js.map +1 -0
  31. package/dist/analysis/diagnosis/rules.js +656 -0
  32. package/dist/analysis/diagnosis/rules.js.map +1 -0
  33. package/dist/analysis/diagnosis/types.js +2 -0
  34. package/dist/analysis/diagnosis/types.js.map +1 -0
  35. package/dist/cli.js +344 -0
  36. package/dist/cli.js.map +1 -0
  37. package/dist/config/env.js +100 -0
  38. package/dist/config/env.js.map +1 -0
  39. package/dist/config/preflight.js +167 -0
  40. package/dist/config/preflight.js.map +1 -0
  41. package/dist/config/user-config.js +79 -0
  42. package/dist/config/user-config.js.map +1 -0
  43. package/dist/evidence/collect.js +527 -0
  44. package/dist/evidence/collect.js.map +1 -0
  45. package/dist/evidence/consent-accept.js +377 -0
  46. package/dist/evidence/consent-accept.js.map +1 -0
  47. package/dist/evidence/consent.js +340 -0
  48. package/dist/evidence/consent.js.map +1 -0
  49. package/dist/evidence/constants.js +615 -0
  50. package/dist/evidence/constants.js.map +1 -0
  51. package/dist/evidence/globals.js +132 -0
  52. package/dist/evidence/globals.js.map +1 -0
  53. package/dist/evidence/gtm-container.js +432 -0
  54. package/dist/evidence/gtm-container.js.map +1 -0
  55. package/dist/evidence/heuristic-banner.js +107 -0
  56. package/dist/evidence/heuristic-banner.js.map +1 -0
  57. package/dist/evidence/http-probe.js +139 -0
  58. package/dist/evidence/http-probe.js.map +1 -0
  59. package/dist/evidence/init-script.js +112 -0
  60. package/dist/evidence/init-script.js.map +1 -0
  61. package/dist/evidence/network.js +365 -0
  62. package/dist/evidence/network.js.map +1 -0
  63. package/dist/evidence/preference-center.js +368 -0
  64. package/dist/evidence/preference-center.js.map +1 -0
  65. package/dist/evidence/script.js +425 -0
  66. package/dist/evidence/script.js.map +1 -0
  67. package/dist/evidence/second-page.js +332 -0
  68. package/dist/evidence/second-page.js.map +1 -0
  69. package/dist/evidence/selector-probe.js +53 -0
  70. package/dist/evidence/selector-probe.js.map +1 -0
  71. package/dist/evidence/stealth.js +21 -0
  72. package/dist/evidence/stealth.js.map +1 -0
  73. package/dist/evidence/types.js +14 -0
  74. package/dist/evidence/types.js.map +1 -0
  75. package/dist/interactive/prompt.js +31 -0
  76. package/dist/interactive/prompt.js.map +1 -0
  77. package/dist/mcp/server.js +308 -0
  78. package/dist/mcp/server.js.map +1 -0
  79. package/dist/mcp/summary.js +41 -0
  80. package/dist/mcp/summary.js.map +1 -0
  81. package/dist/report/context.js +184 -0
  82. package/dist/report/context.js.map +1 -0
  83. package/dist/report/projection.js +125 -0
  84. package/dist/report/projection.js.map +1 -0
  85. package/dist/report/quote-check.js +90 -0
  86. package/dist/report/quote-check.js.map +1 -0
  87. package/dist/report/render.js +178 -0
  88. package/dist/report/render.js.map +1 -0
  89. package/dist/report/serializers.js +124 -0
  90. package/dist/report/serializers.js.map +1 -0
  91. package/dist/report/store.js +139 -0
  92. package/dist/report/store.js.map +1 -0
  93. package/dist/run/verify.js +77 -0
  94. package/dist/run/verify.js.map +1 -0
  95. package/dist/utils/duration.js +10 -0
  96. package/dist/utils/duration.js.map +1 -0
  97. package/dist/utils/errors.js +17 -0
  98. package/dist/utils/errors.js.map +1 -0
  99. package/dist/utils/logger.js +59 -0
  100. package/dist/utils/logger.js.map +1 -0
  101. package/dist/utils/runTimingsLog.js +27 -0
  102. package/dist/utils/runTimingsLog.js.map +1 -0
  103. package/dist/utils/slug.js +27 -0
  104. package/dist/utils/slug.js.map +1 -0
  105. package/dist/version.js +10 -0
  106. package/dist/version.js.map +1 -0
  107. package/package.json +57 -0
  108. package/scripts/install.sh +314 -0
@@ -0,0 +1,377 @@
1
+ import { BANNER_CONTEXT_KEYWORDS, GENERIC_ACCEPT_PHRASES, GENERIC_REJECT_PHRASES, PREFERENCE_CENTER_COUNTERPART_PHRASES, } from "./constants.js";
2
+ import { runHeuristicBanner } from "./heuristic-banner.js";
3
+ import { drivePreferenceCenter } from "./preference-center.js";
4
+ import { clickFirstMatching } from "./selector-probe.js";
5
+ import { logger } from "../utils/logger.js";
6
+ /**
7
+ * Selectors used only when no vendor API is reachable. Kept narrow and
8
+ * vendor-specific rather than matching any button whose text says "accept"
9
+ * or "reject", so we never click an unrelated control.
10
+ */
11
+ const ACCEPT_SELECTORS = [
12
+ "#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll",
13
+ "#CybotCookiebotDialogBodyButtonAccept",
14
+ "#onetrust-accept-btn-handler",
15
+ ".onetrust-close-btn-handler.accept-cookies",
16
+ "#usercentrics-root >>> button[data-testid='uc-accept-all-button']",
17
+ ".cky-btn-accept",
18
+ "#cookiescript_accept",
19
+ "button#iubenda-cs-accept-btn",
20
+ "[aria-label='Accept all cookies']",
21
+ ".osano-cm-accept-all",
22
+ ".cmplz-accept",
23
+ "#hs-eu-confirmation-button",
24
+ "#pi_tracking_opt_in_yes",
25
+ ];
26
+ /** Reject-side equivalents of ACCEPT_SELECTORS, for the same vendors. */
27
+ const REJECT_SELECTORS = [
28
+ "#CybotCookiebotDialogBodyLevelButtonLevelOptinDeclineAll",
29
+ "#CybotCookiebotDialogBodyButtonDecline",
30
+ "#onetrust-reject-all-handler",
31
+ ".onetrust-close-btn-handler.reject-cookies",
32
+ "#usercentrics-root >>> button[data-testid='uc-deny-all-button']",
33
+ ".cky-btn-reject",
34
+ "#cookiescript_reject",
35
+ "button#iubenda-cs-reject-btn",
36
+ "[aria-label='Reject all cookies']",
37
+ "[aria-label='Decline all cookies']",
38
+ ".osano-cm-denyAll",
39
+ ".cmplz-deny",
40
+ "#hs-eu-decline-button",
41
+ "#pi_tracking_opt_in_no",
42
+ ];
43
+ const HEURISTIC_ACCEPT_ARGS = {
44
+ mode: "click",
45
+ direction: "accept",
46
+ acceptPhrases: GENERIC_ACCEPT_PHRASES,
47
+ rejectPhrases: GENERIC_REJECT_PHRASES,
48
+ preferenceCounterpartPhrases: PREFERENCE_CENTER_COUNTERPART_PHRASES,
49
+ contextKeywords: BANNER_CONTEXT_KEYWORDS,
50
+ };
51
+ const HEURISTIC_REJECT_ARGS = {
52
+ mode: "click",
53
+ direction: "reject",
54
+ acceptPhrases: GENERIC_ACCEPT_PHRASES,
55
+ rejectPhrases: GENERIC_REJECT_PHRASES,
56
+ preferenceCounterpartPhrases: PREFERENCE_CENTER_COUNTERPART_PHRASES,
57
+ contextKeywords: BANNER_CONTEXT_KEYWORDS,
58
+ };
59
+ /**
60
+ * Attempts to grant analytics consent through the CMP's own API, falling back
61
+ * to its accept button. Returns what was tried so the report can state plainly
62
+ * whether the page was observed pre- or post-consent.
63
+ *
64
+ * Only ever called by the grant phase.
65
+ */
66
+ export async function acceptConsent(page, timeoutMs) {
67
+ const viaApi = await page
68
+ .evaluate(() => {
69
+ const w = window;
70
+ if (typeof w.Cookiebot?.submitCustomConsent === "function") {
71
+ w.Cookiebot.submitCustomConsent(true, true, true);
72
+ return "Cookiebot.submitCustomConsent(true, true, true)";
73
+ }
74
+ if (typeof w.Cookiebot?.dialog?.submitAll === "function") {
75
+ w.Cookiebot.dialog.submitAll();
76
+ return "Cookiebot.dialog.submitAll()";
77
+ }
78
+ if (typeof w.OneTrust?.AllowAll === "function") {
79
+ w.OneTrust.AllowAll();
80
+ return "OneTrust.AllowAll()";
81
+ }
82
+ if (typeof w.UC_UI?.acceptAllConsents === "function") {
83
+ void w.UC_UI.acceptAllConsents().then(() => w.UC_UI?.closeCMP?.());
84
+ return "UC_UI.acceptAllConsents()";
85
+ }
86
+ return null;
87
+ })
88
+ .catch((err) => {
89
+ logger.debug("Consent API call failed", err);
90
+ return null;
91
+ });
92
+ if (viaApi)
93
+ return { attempted: true, succeeded: true, detail: `granted via ${viaApi}` };
94
+ const matched = await clickFirstMatching(page, ACCEPT_SELECTORS, timeoutMs);
95
+ if (matched) {
96
+ return { attempted: true, succeeded: true, detail: `clicked ${matched}` };
97
+ }
98
+ // Last resort: no known vendor API or selector reached anything, which is
99
+ // expected for a bespoke banner. Try the generic text-matched fallback
100
+ // before giving up — see heuristic-banner.ts for what counts as a match.
101
+ const viaHeuristic = await page.evaluate(runHeuristicBanner, HEURISTIC_ACCEPT_ARGS).catch(() => null);
102
+ if (viaHeuristic?.clicked) {
103
+ return { attempted: true, succeeded: true, detail: `clicked heuristically-matched button "${viaHeuristic.clicked}"` };
104
+ }
105
+ // Nothing on the banner itself answers it. Some banners only ever answer
106
+ // through their preference centre — see preference-center.ts.
107
+ const viaPreferences = await drivePreferenceCenter(page, "accept", timeoutMs);
108
+ if (viaPreferences.succeeded)
109
+ return { attempted: true, succeeded: true, detail: `granted via the preference centre: ${viaPreferences.detail}` };
110
+ return {
111
+ attempted: true,
112
+ succeeded: false,
113
+ detail: `no CMP API, accept button, or heuristically-matched banner control could be reached, and the preference centre did not answer it either (${viaPreferences.detail})`,
114
+ };
115
+ }
116
+ /**
117
+ * Symmetric counterpart to acceptConsent: attempts to DENY analytics consent
118
+ * through the CMP's own API, falling back to its reject/decline button.
119
+ *
120
+ * This lets a run observe the explicit-denial state (as opposed to the
121
+ * pre-consent default), which is required to tell "consent was never asked"
122
+ * apart from "consent was asked and refused, but the site tracked anyway" —
123
+ * a distinct defect (see checklist consent-mode / event-behavior items).
124
+ *
125
+ * Only ever called by the reject phase.
126
+ */
127
+ export async function rejectConsent(page, timeoutMs) {
128
+ const viaApi = await page
129
+ .evaluate(() => {
130
+ const w = window;
131
+ if (typeof w.Cookiebot?.submitCustomConsent === "function") {
132
+ w.Cookiebot.submitCustomConsent(false, false, false);
133
+ return "Cookiebot.submitCustomConsent(false, false, false)";
134
+ }
135
+ if (typeof w.Cookiebot?.dialog?.submitDecline === "function") {
136
+ w.Cookiebot.dialog.submitDecline();
137
+ return "Cookiebot.dialog.submitDecline()";
138
+ }
139
+ if (typeof w.OneTrust?.RejectAll === "function") {
140
+ w.OneTrust.RejectAll();
141
+ return "OneTrust.RejectAll()";
142
+ }
143
+ if (typeof w.UC_UI?.denyAllConsents === "function") {
144
+ void w.UC_UI.denyAllConsents().then(() => w.UC_UI?.closeCMP?.());
145
+ return "UC_UI.denyAllConsents()";
146
+ }
147
+ return null;
148
+ })
149
+ .catch((err) => {
150
+ logger.debug("Consent API call failed", err);
151
+ return null;
152
+ });
153
+ if (viaApi)
154
+ return { attempted: true, succeeded: true, detail: `denied via ${viaApi}` };
155
+ const matched = await clickFirstMatching(page, REJECT_SELECTORS, timeoutMs);
156
+ if (matched) {
157
+ return { attempted: true, succeeded: true, detail: `clicked ${matched}` };
158
+ }
159
+ const viaHeuristic = await page.evaluate(runHeuristicBanner, HEURISTIC_REJECT_ARGS).catch(() => null);
160
+ if (viaHeuristic?.clicked) {
161
+ return { attempted: true, succeeded: true, detail: `clicked heuristically-matched button "${viaHeuristic.clicked}"` };
162
+ }
163
+ // The common shape this exists for: a banner offering only "Accept all"
164
+ // and a "Settings" link, where the visitor's only way to refuse is to open
165
+ // the panel, leave analytics off and save. Without this the phase reports
166
+ // the refusal as undrivable and every consent verdict goes inconclusive on
167
+ // a site a visitor can plainly say no to.
168
+ const viaPreferences = await drivePreferenceCenter(page, "reject", timeoutMs);
169
+ if (viaPreferences.succeeded)
170
+ return { attempted: true, succeeded: true, detail: `denied via the preference centre: ${viaPreferences.detail}` };
171
+ return {
172
+ attempted: true,
173
+ succeeded: false,
174
+ detail: `no CMP API, reject button, or heuristically-matched banner control could be reached, and the preference centre did not answer it either (${viaPreferences.detail})`,
175
+ };
176
+ }
177
+ /**
178
+ * Cookies CMPs write to record the visitor's answer. Prefix-matched, since
179
+ * several append an id or a group suffix.
180
+ */
181
+ const CONSENT_COOKIE_PREFIXES = [
182
+ "CookieConsent",
183
+ "OptanonConsent",
184
+ "OptanonAlertBoxClosed",
185
+ "cookieyes-consent",
186
+ "usercentrics",
187
+ "cookiescript_key",
188
+ "euconsent-v2",
189
+ "osano_consentmanager",
190
+ "cmplz_",
191
+ "__hs_cookie_cat_pref",
192
+ "__hs_opt_out",
193
+ "pi_opt_in987432",
194
+ ];
195
+ /**
196
+ * Cookies third-party infrastructure writes on a schedule of its own —
197
+ * bot-management, CDN session, and load-balancer cookies being the most
198
+ * common — that have nothing to do with the banner. The generic "any cookie
199
+ * that wasn't there before" fallback below exists for bespoke banners with no
200
+ * recognised name; without this exclusion list it fires on the first
201
+ * Cloudflare `__cf_bm` rotation a click happens to race against, on any site
202
+ * sitting behind it, regardless of what the visitor actually answered.
203
+ */
204
+ const NOISE_COOKIE_PREFIXES = ["__cf_bm", "_cfuvid", "cf_clearance", "__cflb", "AWSALB", "AWSALBCORS", "incap_ses", "visid_incap"];
205
+ /**
206
+ * Entries a CMP writes on page load and then REWRITES IN PLACE when the
207
+ * visitor answers, rather than creating fresh. Usercentrics is the common
208
+ * case: ucData/ucString are there from the first page view, so neither the
209
+ * cookie-name rule nor the "something new appeared" fallback can ever see a
210
+ * decision land on one of those sites — the value changing is the only
211
+ * evidence that exists. Matched against cookie names and localStorage keys
212
+ * alike, and only ever consulted as a value-changed check, so an entry that
213
+ * merely exists still proves nothing.
214
+ */
215
+ const CONSENT_STORAGE_PREFIXES = [...CONSENT_COOKIE_PREFIXES, "ucData", "ucString", "uc_"];
216
+ /**
217
+ * The same idea for a banner nobody has heard of, matched on the name rather
218
+ * than a vendor prefix.
219
+ *
220
+ * meteomatics.com is the case: its hand-rolled banner keeps the decision in
221
+ * localStorage under `cookie-consent`, and writes that key on page load —
222
+ * `{"consents":[],"consentedAt":null,"rejectedAt":null}` — before the visitor
223
+ * has answered anything. So neither the vendor-prefix rule nor the "a key
224
+ * that wasn't there before appeared" fallback can ever see the answer land,
225
+ * and a click that worked perfectly was reported as `not-persisted`, which
226
+ * sank the whole phase. The value moving is the only evidence such a banner
227
+ * produces.
228
+ *
229
+ * Only ever consulted as a value-CHANGED check within the few seconds after
230
+ * a click we drove ourselves, so a key merely existing still proves nothing.
231
+ */
232
+ const CONSENT_ENTRY_NAME_PATTERN = /consent|cookie[-_. ]?(?:prefs|preferences|settings|choices|notice|banner)/i;
233
+ /**
234
+ * Snapshots what's already there BEFORE the banner is clicked, so
235
+ * waitForConsentPersisted's generic fallback can tell "the site just wrote
236
+ * something new" apart from "this cookie was already here on load".
237
+ */
238
+ export async function snapshotStorage(page) {
239
+ const cookies = await page.context().cookies().catch(() => []);
240
+ const localStorageEntries = await page
241
+ .evaluate(() => {
242
+ try {
243
+ return Object.keys(window.localStorage).map((k) => [k, window.localStorage.getItem(k)]);
244
+ }
245
+ catch {
246
+ return [];
247
+ }
248
+ })
249
+ .catch(() => []);
250
+ return {
251
+ cookies: Object.fromEntries(cookies.map((c) => [c.name, c.value])),
252
+ localStorage: Object.fromEntries(localStorageEntries),
253
+ };
254
+ }
255
+ /**
256
+ * Waits until the CMP has durably recorded the grant, then returns.
257
+ *
258
+ * submitCustomConsent() and friends persist asynchronously, so reloading the
259
+ * moment they return can beat the cookie being written — the reload then
260
+ * replays the pre-consent page and the run reports nothing tracked on a
261
+ * healthy install. Polling both the vendor API and the cookie jar closes that
262
+ * race; a false return means the grant may not have stuck.
263
+ *
264
+ * `before` — taken with snapshotStorage() ahead of the click — backs two
265
+ * fallbacks for banners with no recognised vendor API and no cookie name in
266
+ * CONSENT_COOKIE_PREFIXES: any cookie or localStorage key that did not exist
267
+ * before the click is evidence the site recorded *something*, even though
268
+ * this harness cannot name what; and any recognised consent entry whose
269
+ * value changed covers the CMPs that rewrite an entry they already had
270
+ * (see CONSENT_STORAGE_PREFIXES).
271
+ */
272
+ export async function waitForConsentPersisted(page, timeoutMs, before) {
273
+ const deadline = Date.now() + Math.min(8000, timeoutMs);
274
+ const isConsentEntry = (name) => CONSENT_STORAGE_PREFIXES.some((prefix) => name.startsWith(prefix)) || CONSENT_ENTRY_NAME_PATTERN.test(name);
275
+ while (Date.now() < deadline) {
276
+ const viaApi = await page
277
+ .evaluate(() => {
278
+ const w = window;
279
+ if (typeof w.Cookiebot?.hasResponse === "boolean")
280
+ return w.Cookiebot.hasResponse;
281
+ if (typeof w.OneTrust?.IsAlertBoxClosed === "function") {
282
+ try {
283
+ return Boolean(w.OneTrust.IsAlertBoxClosed());
284
+ }
285
+ catch {
286
+ return false;
287
+ }
288
+ }
289
+ return false;
290
+ })
291
+ .catch(() => false);
292
+ if (viaApi)
293
+ return true;
294
+ const cookies = await page.context().cookies().catch(() => []);
295
+ if (cookies.some((c) => CONSENT_COOKIE_PREFIXES.some((prefix) => c.name.startsWith(prefix))))
296
+ return true;
297
+ // Generic fallback for a custom banner with no recognised cookie name.
298
+ // Third-party infrastructure noise is excluded — see NOISE_COOKIE_PREFIXES.
299
+ if (cookies.some((c) => !(c.name in before.cookies) && !NOISE_COOKIE_PREFIXES.some((prefix) => c.name.startsWith(prefix))))
300
+ return true;
301
+ // A recognised consent entry whose value moved. Restricted to names we
302
+ // recognise, because plenty of unrelated entries (session ids, A/B test
303
+ // buckets, analytics timestamps) churn on their own and would otherwise
304
+ // certify any drive at all.
305
+ if (cookies.some((c) => isConsentEntry(c.name) && c.name in before.cookies && before.cookies[c.name] !== c.value))
306
+ return true;
307
+ const storage = await page
308
+ .evaluate(() => {
309
+ try {
310
+ return Object.keys(window.localStorage).map((k) => [k, window.localStorage.getItem(k)]);
311
+ }
312
+ catch {
313
+ return [];
314
+ }
315
+ })
316
+ .catch(() => []);
317
+ if (storage.some(([k]) => !(k in before.localStorage)))
318
+ return true;
319
+ if (storage.some(([k, v]) => isConsentEntry(k) && k in before.localStorage && before.localStorage[k] !== v))
320
+ return true;
321
+ await page.waitForTimeout(250);
322
+ }
323
+ return false;
324
+ }
325
+ /**
326
+ * Waits for Consent Mode to actually reflect the choice we just drove.
327
+ *
328
+ * waitForConsentPersisted only proves the CMP wrote *a* cookie. A CMP that
329
+ * records a decision but never emits gtag('consent','update',…) leaves every
330
+ * Consent-Mode-gated tag — including a GTM-managed Dreamdata bundle — exactly
331
+ * where it was, which is itself the defect worth reporting. Confirming the
332
+ * signal separately is what lets the report say which of the two happened.
333
+ *
334
+ * Only an explicit `update` counts as confirmation. A value the page was
335
+ * already sitting on — a declared default, or gtag.js's own fallback (see
336
+ * classifyAnalyticsStorage) — was never caused by anything we did, even when
337
+ * it happens to equal `expected`: a site that defaults analytics_storage to
338
+ * denied, or never wires up Consent Mode at all, reads "denied" that way by
339
+ * coincidence on every reject phase, which would otherwise be
340
+ * indistinguishable from a CMP that genuinely relayed the refusal.
341
+ *
342
+ * null means there was no readable Consent Mode state at all (no gtag on the
343
+ * page), which is not a failure — only sites using Consent Mode can be held
344
+ * to it.
345
+ */
346
+ export async function waitForConsentSignal(page, expected, timeoutMs) {
347
+ const deadline = Date.now() + Math.min(8000, timeoutMs);
348
+ let sawConsentMode = false;
349
+ while (Date.now() < deadline) {
350
+ const result = await page
351
+ .evaluate(() => {
352
+ const w = window;
353
+ const entry = w.google_tag_data?.ics?.entries?.["analytics_storage"];
354
+ if (!entry)
355
+ return null;
356
+ const hasUpdate = entry.update !== undefined && entry.update !== null;
357
+ const value = hasUpdate ? entry.update : entry.default;
358
+ let state;
359
+ if (value === true || value === "granted")
360
+ state = "granted";
361
+ else if (value === false || value === "denied")
362
+ state = "denied";
363
+ else
364
+ state = "unset";
365
+ return { state, hasUpdate };
366
+ })
367
+ .catch(() => null);
368
+ if (result !== null) {
369
+ sawConsentMode = true;
370
+ if (result.state === expected && result.hasUpdate)
371
+ return true;
372
+ }
373
+ await page.waitForTimeout(250);
374
+ }
375
+ return sawConsentMode ? false : null;
376
+ }
377
+ //# sourceMappingURL=consent-accept.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-accept.js","sourceRoot":"","sources":["../../src/evidence/consent-accept.ts"],"names":[],"mappings":"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"}