@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,615 @@
1
+ export const DREAMDATA_CDN_HOST = "cdn.dreamdata.cloud";
2
+ // The cookieless bundle is served from a distinct CDN host; event delivery
3
+ // (the POST requests) from either bundle still targets DREAMDATA_CDN_HOST by
4
+ // default — these hosts only matter for detecting the <script> tag itself.
5
+ export const DREAMDATA_COOKIELESS_SCRIPT_HOST = "cdn.drda.io";
6
+ export const DEFAULT_SCRIPT_HOSTS = [DREAMDATA_CDN_HOST, DREAMDATA_COOKIELESS_SCRIPT_HOST];
7
+ /**
8
+ * Hostnames Dreamdata serves from: the shared CDNs plus per-tenant subdomains
9
+ * of the first-party zones (api.s.dreamdata.io, evs.s.dreamdata.io, …). Fully
10
+ * anchored and requiring at least one subdomain label, so the bare
11
+ * dreamdata.io apex does not match — it shows up in unrelated referer strings.
12
+ *
13
+ * Tested against a URL's *hostname*, never the whole URL, so a third party's
14
+ * `?referer=dreamdata.io` query string cannot trigger it.
15
+ */
16
+ export const DREAMDATA_HOST_PATTERN = /^(?:[a-z0-9-]+\.)+(?:dreamdata\.(?:io|cloud)|drda\.io)$/i;
17
+ /** Matches a Dreamdata-owned origin written out inside an inline snippet. */
18
+ export const DREAMDATA_ORIGIN_IN_SNIPPET = /https?:\/\/(?:[a-z0-9-]+\.)+(?:dreamdata\.(?:io|cloud)|drda\.io)/i;
19
+ /**
20
+ * Hosts that receive event requests. Installs that front Dreamdata with a
21
+ * first-party CNAME (e.g. evs.s.dreamdata.io on dreamdata.io itself) deliver
22
+ * to their own host instead, so this is configurable via --tracking-host.
23
+ */
24
+ export const DEFAULT_TRACKING_HOSTS = [DREAMDATA_CDN_HOST];
25
+ /**
26
+ * Real Segment's own CDN — a `<script src>` pointing here is Segment's
27
+ * actual analytics.js/analytics-next library, NOT a Dreamdata bundle, even
28
+ * though the site may still be forwarding events to Dreamdata behind the
29
+ * scenes. See ThirdPartySegmentEvidence for why this harness can only ever
30
+ * report that this script exists, never whether Dreamdata receives anything.
31
+ */
32
+ export const THIRD_PARTY_SEGMENT_SCRIPT_HOST = "cdn.segment.com";
33
+ /**
34
+ * Segment's own event-collection endpoint. A request here proves the site is
35
+ * instrumented with real Segment and actively sending it events — nothing
36
+ * more. Whether Segment then forwards a copy to Dreamdata is a server-side
37
+ * destination/connection this harness cannot observe.
38
+ */
39
+ export const THIRD_PARTY_SEGMENT_TRACKING_HOST = "api.segment.io";
40
+ /**
41
+ * Event endpoints appear as /api/v1/<letter> on the shared CDN and as plain
42
+ * /v1/<letter> on first-party delivery hosts (e.g. api.s.dreamdata.io/v1/p),
43
+ * so the /api/ segment is optional.
44
+ */
45
+ export const ENDPOINT_REGEX = /\/(?:api\/)?v1\/([a-z])(?:[/?]|$)/;
46
+ // The single most reliable per-request signal for which bundle sent an
47
+ // event: the delivery plugin stamps this into every payload's context.
48
+ export const COOKIELESS_LIBRARY_NAME_MARKER = "cookieless";
49
+ export const COOKIELESS_GLOBAL_KEY = "dreamdata-cl";
50
+ /**
51
+ * Segment-compatible installs expose the queue/SDK as window.analytics
52
+ * rather than window.dreamdata. Both are valid Dreamdata deployments.
53
+ */
54
+ export const SEGMENT_COMPAT_GLOBAL_KEY = "analytics";
55
+ // Matches the writeKey segment out of the installed script's src, e.g.
56
+ // https://cdn.dreamdata.cloud/scripts/analytics.js/v1/{writeKey}/analytics.min.js
57
+ // or .../analytics/js/v1/{writeKey}/analytics...
58
+ export const WRITE_KEY_FROM_SCRIPT_SRC = /\/analytics(?:\.js|\/js)\/v1\/([^/]+)/i;
59
+ /**
60
+ * Segment-style snippets assign the key instead of putting it in the src:
61
+ * `analytics._writeKey="…"` or `writeKey: "…"`. The `=` is why the older
62
+ * colon/quote-only pattern missed real installs.
63
+ */
64
+ export const WRITE_KEY_FROM_SNIPPET = /\b_?writeKey["']?\s*[:=]\s*["']([\w-]+)["']/i;
65
+ /** `analytics.load("{writeKey}")` — the last-resort snippet fallback. */
66
+ export const WRITE_KEY_FROM_LOAD_CALL = /\.load\(\s*["']([\w-]+)["']/i;
67
+ /**
68
+ * Dreamdata ships auxiliary scripts (e.g. the identify-form helper) from the
69
+ * same CDN. They are not tracking bundles, so matching one must not count as
70
+ * "the script is installed".
71
+ */
72
+ export const HELPER_SCRIPT_PATH_MARKERS = ["/scripts/identify-form/"];
73
+ export const ENDPOINT_NAMES = {
74
+ p: "page",
75
+ i: "identify",
76
+ t: "track",
77
+ a: "alias",
78
+ g: "group",
79
+ s: "screen",
80
+ b: "batch",
81
+ };
82
+ export const DD_COOKIE_NAMES = [
83
+ "dd_anonymous_id",
84
+ "dd_user_id",
85
+ "dd_user_traits",
86
+ "dd_group_id",
87
+ "dd_group_properties",
88
+ ];
89
+ export const AJS_COOKIE_PREFIX = "ajs_";
90
+ /** Segment-compatibility equivalents of DD_COOKIE_NAMES, same order. */
91
+ export const AJS_COOKIE_NAMES = [
92
+ "ajs_anonymous_id",
93
+ "ajs_user_id",
94
+ "ajs_user_traits",
95
+ "ajs_group_id",
96
+ "ajs_group_properties",
97
+ ];
98
+ export const MESSAGE_ID_PATTERN = /^ajs-next-/;
99
+ export const DREAMDATA_METHODS = [
100
+ "page",
101
+ "track",
102
+ "identify",
103
+ "group",
104
+ "alias",
105
+ "screen",
106
+ "ready",
107
+ "setAnonymousId",
108
+ "trackForm",
109
+ "trackLink",
110
+ ];
111
+ /**
112
+ * How long a parsed document is given to go quiet before a run carries on.
113
+ *
114
+ * A bounded settle, not a completion condition. Waiting on `networkidle` for
115
+ * a whole navigation timeout reads as "wait until the page has finished", but
116
+ * a page that keeps a socket open — a chat widget, an analytics heartbeat, a
117
+ * bot wall polling its own challenge endpoint — never reaches idle at all, so
118
+ * such a wait always runs to the full timeout. A page that does go quiet is
119
+ * waited for exactly as before; one that never does costs this much and no
120
+ * more, and that is not an error.
121
+ */
122
+ export const NETWORK_SETTLE_MS = 5_000;
123
+ /**
124
+ * How long an interaction-gated banner is given to mount after the nudge that
125
+ * should reveal it. It is a synchronous DOM build in every case seen so far —
126
+ * this is slack for a framework's render tick, not a wait for a timer.
127
+ */
128
+ export const BANNER_REVEAL_SETTLE_MS = 750;
129
+ /**
130
+ * How far the nudge scrolls. Enough to cross a scroll-depth trigger's first
131
+ * threshold — catonetworks.com's GTM tags need one — and small enough that the
132
+ * page is still showing what it shows on arrival. Restored to 0 straight after.
133
+ */
134
+ export const BANNER_REVEAL_SCROLL_PX = 300;
135
+ /**
136
+ * CMP vendors we can detect and, where possible, drive programmatically.
137
+ * `globalKey` is probed on window; order is most-specific first.
138
+ */
139
+ export const CMP_VENDORS = [
140
+ { id: "cookiebot", globalKey: "Cookiebot" },
141
+ { id: "onetrust", globalKey: "OneTrust" },
142
+ { id: "onetrust", globalKey: "OptanonActiveGroups" },
143
+ { id: "onetrust", globalKey: "OnetrustActiveGroups" },
144
+ { id: "usercentrics", globalKey: "UC_UI" },
145
+ { id: "cookieyes", globalKey: "getCkyConsent" },
146
+ { id: "generic-cookieconsent", globalKey: "CookieConsent" },
147
+ { id: "iab-tcf", globalKey: "__tcfapi" },
148
+ { id: "iab-cmp", globalKey: "__cmp" },
149
+ { id: "osano", globalKey: "Osano" },
150
+ { id: "complianz", globalKey: "complianz" },
151
+ ];
152
+ export const KNOWN_CMP_GLOBALS = CMP_VENDORS.map((v) => v.globalKey);
153
+ /**
154
+ * Fallback used only when no named vendor/DOM banner matched: a bespoke,
155
+ * hand-rolled banner exposes no global and no recognisable id/class, so the
156
+ * only thing left to go on is a clickable control's own text. Kept to exact
157
+ * phrases actually seen on cookie banners, not "contains accept", so an
158
+ * unrelated page control (a signup form's "Accept" button) is never matched
159
+ * on text alone — see heuristic-banner.ts for the container check that is
160
+ * the real safeguard against a false match.
161
+ */
162
+ export const GENERIC_ACCEPT_PHRASES = [
163
+ "accept all",
164
+ "accept all cookies",
165
+ "allow all",
166
+ "allow all cookies",
167
+ "i accept",
168
+ "agree",
169
+ "agree to all",
170
+ "accept cookies",
171
+ "allow cookies",
172
+ // A banner with no reject button often has no "all" to accept either, so
173
+ // its one control just says what happens next. meteomatics.com's
174
+ // "Accept and continue" is the case this list was missing: nothing matched
175
+ // it, so the banner was reported as absent and all three phases measured
176
+ // the same unanswered page.
177
+ "accept and continue",
178
+ "accept & continue",
179
+ "agree and continue",
180
+ "agree & continue",
181
+ "accept and close",
182
+ "agree and close",
183
+ "accept and proceed",
184
+ "agree and proceed",
185
+ ];
186
+ /** Reject-side equivalents of GENERIC_ACCEPT_PHRASES. */
187
+ export const GENERIC_REJECT_PHRASES = [
188
+ "necessary only",
189
+ "only necessary",
190
+ "essential only",
191
+ "only essential",
192
+ "reject all",
193
+ "decline all",
194
+ "reject",
195
+ "decline",
196
+ "deny",
197
+ "refuse all",
198
+ // Osano's own reject button says what it rejects rather than "all":
199
+ // achievers.com offers "Accept All" and "Reject Non-Essential", so nothing
200
+ // here matched, the banner failed the counterpart test in
201
+ // heuristic-banner.ts, and a banner sitting in plain markup went unseen.
202
+ "reject non-essential",
203
+ "reject non-essential cookies",
204
+ "decline non-essential",
205
+ "decline non-essential cookies",
206
+ ];
207
+ /**
208
+ * A matched accept/reject control only counts as a real banner when some
209
+ * ancestor's text also mentions one of these — otherwise text matching alone
210
+ * would happily click an unrelated "Accept"/"Decline" button elsewhere on
211
+ * the page.
212
+ */
213
+ export const BANNER_CONTEXT_KEYWORDS = ["cookie", "consent", "privacy", "gdpr"];
214
+ /**
215
+ * Banners recognised by a DOM element rather than a window global.
216
+ *
217
+ * HubSpot's banner is the reason this list exists: window._hsp is present on
218
+ * every HubSpot-tracked page whether or not a banner is shown, so probing it
219
+ * would invent a consent gate on sites that deliberately have none. The
220
+ * banner element only exists when the banner is actually rendered.
221
+ */
222
+ export const DOM_ONLY_CMP_VENDORS = [
223
+ { id: "hubspot", bannerElementId: "hs-eu-cookie-confirmation", responseCookiePrefix: "__hs_cookie_cat_pref" },
224
+ // Salesforce Pardot's standard tracking-code opt-in snippet. piEnableTracking
225
+ // and piDisableTracking are defined on window as part of every Pardot
226
+ // tracking snippet whether or not a banner is ever shown to this visitor
227
+ // (much like HubSpot's window._hsp), so probing for those globals the way
228
+ // CMP_VENDORS does would invent a consent gate on any Pardot-tracked page.
229
+ // The banner div only exists when the banner is actually rendered.
230
+ { id: "pardot", bannerElementId: "pi_tracking_opt_in_div", responseCookiePrefix: "pi_opt_in987432" },
231
+ ];
232
+ /**
233
+ * True when a URL's host belongs to Dreamdata, or is one the operator named
234
+ * explicitly via --tracking-host (needed for a fully custom CNAME).
235
+ */
236
+ export function isDreamdataUrl(url, extraHosts = []) {
237
+ if (extraHosts.some((host) => url.includes(host)))
238
+ return true;
239
+ try {
240
+ return DREAMDATA_HOST_PATTERN.test(new URL(url).hostname);
241
+ }
242
+ catch {
243
+ return false;
244
+ }
245
+ }
246
+ /**
247
+ * Path fragments a second-page probe refuses to follow.
248
+ *
249
+ * These pages are the ones most likely to sit outside the site's own tracking
250
+ * setup — a separate template, a third-party host, an auth redirect — so a
251
+ * missing request there would say nothing about the install, and a consent
252
+ * banner's own "privacy policy" link is exactly the kind of link that would
253
+ * otherwise get picked.
254
+ */
255
+ export const SECOND_PAGE_PATH_DENYLIST = [
256
+ "privacy",
257
+ "cookie",
258
+ "legal",
259
+ "terms",
260
+ "gdpr",
261
+ "imprint",
262
+ "login",
263
+ "signin",
264
+ "sign-in",
265
+ "signup",
266
+ "sign-up",
267
+ "register",
268
+ "logout",
269
+ "account",
270
+ "checkout",
271
+ "cart",
272
+ "search",
273
+ ];
274
+ /** Extensions that are a download or an asset rather than a tracked page view. */
275
+ export const SECOND_PAGE_ASSET_EXTENSIONS = [
276
+ ".pdf",
277
+ ".zip",
278
+ ".dmg",
279
+ ".exe",
280
+ ".csv",
281
+ ".xlsx",
282
+ ".doc",
283
+ ".docx",
284
+ ".ppt",
285
+ ".pptx",
286
+ ".jpg",
287
+ ".jpeg",
288
+ ".png",
289
+ ".gif",
290
+ ".svg",
291
+ ".webp",
292
+ ".mp4",
293
+ ".mp3",
294
+ ".xml",
295
+ ".json",
296
+ ".rss",
297
+ ];
298
+ /** Attribute stamped on the link the second-page probe decided to follow. */
299
+ export const SECOND_PAGE_LINK_ATTRIBUTE = "data-dd-second-page-link";
300
+ /**
301
+ * How many ranked links the probe keeps in reserve, and how many of them it is
302
+ * willing to walk past when they land on a page with no install on it.
303
+ *
304
+ * Each retry costs a navigation back to the first page plus a navigation out
305
+ * again, three times a run. Three is enough to clear a site with two or three
306
+ * separately-built sections near the top of the nav (whereby.com's Gatsby blog
307
+ * being the case this exists for) without letting a site with nothing
308
+ * instrumented anywhere spend the whole budget proving it.
309
+ */
310
+ export const SECOND_PAGE_MAX_RETRIES = 3;
311
+ /** Ranked links kept per page — enough to survive the retries above. */
312
+ export const SECOND_PAGE_MAX_CANDIDATES = 12;
313
+ /** Filenames the two bundles are served under, used to classify a tag by src. */
314
+ export const DREAMDATA_NORMAL_SCRIPT_FILE = "dreamdata.min.js";
315
+ export const DREAMDATA_COOKIELESS_SCRIPT_FILE = "dreamdata.cl.min.js";
316
+ /** Element ids the loader snippets stamp onto the <script> tags they inject. */
317
+ export const NORMAL_BUNDLE_ELEMENT_ID = "dreamdata-analytics";
318
+ export const COOKIELESS_BUNDLE_ELEMENT_ID = "dreamdata-analytics-cl";
319
+ /**
320
+ * The cookieless loader names its normal-bundle companion by element id
321
+ * (`getElementById("dreamdata-analytics")`) so it can stand down when the
322
+ * normal bundle is unblocked. That reference is the strongest in-page proof
323
+ * that a normal bundle belongs to the deployment even when consent has kept
324
+ * it out of the DOM entirely.
325
+ *
326
+ * Quoted deliberately: the cookieless tag's own id is "dreamdata-analytics-cl",
327
+ * which an unanchored substring match would happily mistake for the companion.
328
+ */
329
+ export const NORMAL_COMPANION_REFERENCE = /["']dreamdata-analytics["']|data-global-dreamdata-analytics-key/;
330
+ /**
331
+ * How CMPs neutralise a tag they are gating. A tag matching any of these is
332
+ * installed but deliberately not executing — the opposite of "not installed",
333
+ * and the distinction the cookieless-by-design verdict hinges on.
334
+ */
335
+ export const CMP_SCRIPT_BLOCK_MARKERS = [
336
+ { pattern: /type=["']text\/plain["']/i, vendor: "generic (type=text/plain)" },
337
+ { pattern: /type=["']text\/gtmscript["']/i, vendor: "google-tag-manager" },
338
+ { pattern: /optanon-category/i, vendor: "onetrust" },
339
+ { pattern: /data-ot-ignore/i, vendor: "onetrust" },
340
+ { pattern: /data-cookieconsent=/i, vendor: "cookiebot" },
341
+ { pattern: /cmplz-script|data-cmplz/i, vendor: "complianz" },
342
+ { pattern: /data-cookieyes|class=["'][^"']*cky-/i, vendor: "cookieyes" },
343
+ { pattern: /data-usercentrics/i, vendor: "usercentrics" },
344
+ ];
345
+ /**
346
+ * Which consent platform a document is wired to, read off the raw markup
347
+ * alone — the script the CMP loads, not a window global or a rendered banner.
348
+ *
349
+ * Used only by the plain-HTTP probe, which executes nothing and so can never
350
+ * see a global or a banner element. Its job there is corroboration: a served
351
+ * document that loads OneTrust, next to a rendered page where
352
+ * consent.cmpDetected came back false in every phase, is the browser having
353
+ * been shown a different document — not a site without a banner.
354
+ */
355
+ export const CMP_MARKUP_MARKERS = [
356
+ { pattern: /cdn\.cookielaw\.org|otSDKStub|optanon/i, vendor: "onetrust" },
357
+ { pattern: /consent\.cookiebot\.com|Cookiebot/i, vendor: "cookiebot" },
358
+ { pattern: /app\.usercentrics\.eu|usercentrics/i, vendor: "usercentrics" },
359
+ { pattern: /cdn-cookieyes\.com|cookieyes/i, vendor: "cookieyes" },
360
+ { pattern: /cmp\.osano\.com|osano/i, vendor: "osano" },
361
+ { pattern: /consent\.trustarc\.com|trustarc/i, vendor: "trustarc" },
362
+ { pattern: /app\.termly\.io|termly/i, vendor: "termly" },
363
+ { pattern: /global\.ketchcdn\.com|ketch/i, vendor: "ketch" },
364
+ { pattern: /cookie-?script\.com/i, vendor: "cookiescript" },
365
+ { pattern: /cmplz-|complianz/i, vendor: "complianz" },
366
+ { pattern: /hs-eu-cookie-confirmation/i, vendor: "hubspot" },
367
+ { pattern: /pi_tracking_opt_in_div/i, vendor: "pardot" },
368
+ ];
369
+ /**
370
+ * Markup that names a framework shipping a client-side router.
371
+ *
372
+ * A marker is NOT evidence of client-side routing, and nothing may read this
373
+ * list as if it were. yougov.com serves ng-version, ships the Angular router,
374
+ * and loaded a new document for every link the run followed; Astro and Gatsby
375
+ * ship routers and are routinely deployed as plain multi-page sites. What a
376
+ * marker buys is the one case where behaviour is unavailable: zoominfo.com's
377
+ * bot wall left secondPage.kind null in all three phases, and its served
378
+ * markup names Next.js 115 times over — the only routing hint that survived
379
+ * the wall. See RoutingMode, where a marker can reach "suspected" and never
380
+ * an observed mode.
381
+ *
382
+ * Deliberately excludes the root-container markers (id="root", id="app"):
383
+ * jumpcloud.com is WordPress and has id="app", which makes that pattern a
384
+ * false-positive generator rather than a signal. Excludes data-reactroot for
385
+ * the same reason at one remove — it says the document was server-rendered by
386
+ * React, which is an argument about how the page arrived, not about how it
387
+ * navigates. Excludes astro-island, which argues against client routing.
388
+ *
389
+ * Case-sensitive on purpose: __NEXT_DATA__ and __NUXT__ are exact
390
+ * identifiers, and a case-insensitive /next/ matches ordinary prose.
391
+ */
392
+ export const CSR_FRAMEWORK_MARKERS = [
393
+ { pattern: /__NEXT_DATA__|\/_next\/static\//, framework: "next" },
394
+ { pattern: /id="__nuxt"|__NUXT__|\/_nuxt\//, framework: "nuxt" },
395
+ { pattern: /__remixContext/, framework: "remix" },
396
+ { pattern: /__sveltekit_|data-sveltekit-/, framework: "sveltekit" },
397
+ { pattern: /page-data\/app-data\.json|\/page-data\//, framework: "gatsby" },
398
+ { pattern: /\sng-version="/, framework: "angular" },
399
+ ];
400
+ /**
401
+ * Tag managers inject the bundle at runtime, so a consent-gated normal bundle
402
+ * never reaches the DOM to be found. Reading the published container is the
403
+ * only way to see that it exists at all.
404
+ */
405
+ export const GTM_CONTAINER_ID_PATTERN = /^GTM-[A-Z0-9]+$/;
406
+ /**
407
+ * The same id lifted out of page markup, for sites that keep it in config and
408
+ * load GTM later (or not at all under automation) — window.google_tag_manager
409
+ * is empty exactly when the container is what we most need to read.
410
+ */
411
+ export const GTM_CONTAINER_ID_IN_TEXT = /GTM-[A-Z0-9]{4,10}/g;
412
+ export function gtmContainerUrl(containerId) {
413
+ return `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(containerId)}`;
414
+ }
415
+ /**
416
+ * Banners that offer no "Reject all" at all — only "Accept all" and a
417
+ * settings/preferences link — are common enough that refusing them is a
418
+ * distinct flow rather than an edge case: open the preference centre, leave
419
+ * every non-essential category off, and save. These are the vendor controls
420
+ * that open that panel. Kept vendor-namespaced so a wrong guess simply
421
+ * matches nothing rather than clicking an unrelated page control.
422
+ */
423
+ export const PREFERENCE_CENTER_OPEN_SELECTORS = [
424
+ ".cky-btn-customize",
425
+ "#onetrust-pc-btn-handler",
426
+ ".ot-sdk-show-settings",
427
+ ".optanon-toggle-display",
428
+ "#usercentrics-root >>> button[data-testid='uc-more-button']",
429
+ "#usercentrics-root >>> button[data-testid='uc-customize-anchor']",
430
+ "#CybotCookiebotDialogBodyLevelButtonLevelOptinShowDetails",
431
+ "#CybotCookiebotDialogBodyButtonDetails",
432
+ ".osano-cm-manage",
433
+ ".cmplz-manage-options",
434
+ "#cookiescript_manage",
435
+ ".iubenda-cs-customize-btn",
436
+ ".t-preference-button",
437
+ "#hs-eu-cookie-settings-button",
438
+ ];
439
+ /**
440
+ * The "commit what the toggles now say" control inside that panel — NOT an
441
+ * accept-all/reject-all shortcut, which would discard the toggle states we
442
+ * just set.
443
+ */
444
+ export const PREFERENCE_CENTER_SAVE_SELECTORS = [
445
+ ".cky-btn-preferences",
446
+ ".save-preference-btn-handler",
447
+ "#usercentrics-root >>> button[data-testid='uc-save-button']",
448
+ "#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",
449
+ "#CybotCookiebotDialogBodyButtonAcceptSelected",
450
+ ".osano-cm-save",
451
+ ".cmplz-save-preferences",
452
+ "#cookiescript_save",
453
+ ".iubenda-cs-save-btn",
454
+ ".t-saveButton",
455
+ ];
456
+ /**
457
+ * Text fallback for the same two controls on a banner with no recognised
458
+ * vendor. Ordered most-specific-first: the least ambiguous phrase that
459
+ * matches wins, so a panel containing both "Save my preferences" and a bare
460
+ * "Save" never picks the vague one. The bare entries at the tail are only
461
+ * safe because both searches are scoped to a container that mentions cookies
462
+ * and holds the consent toggles — see preference-center.ts.
463
+ */
464
+ export const GENERIC_PREFERENCE_OPEN_PHRASES = [
465
+ "cookie settings",
466
+ "cookies settings",
467
+ "cookie preferences",
468
+ "cookies preferences",
469
+ // Osano's drawer link. Names the storage it governs rather than cookies
470
+ // outright, which is specific enough to prove a banner is one.
471
+ "storage preferences",
472
+ "manage cookies",
473
+ "manage preferences",
474
+ "manage options",
475
+ "manage choices",
476
+ "manage settings",
477
+ "customize settings",
478
+ "customise settings",
479
+ "customize cookies",
480
+ "customise cookies",
481
+ "cookie options",
482
+ "let me choose",
483
+ "more options",
484
+ "customize",
485
+ "customise",
486
+ "preferences",
487
+ "settings",
488
+ ];
489
+ /**
490
+ * The cookie-explicit half of GENERIC_PREFERENCE_OPEN_PHRASES, used where a
491
+ * settings link has to stand in for the missing reject button rather than
492
+ * merely be clicked once a banner is already established.
493
+ *
494
+ * heuristic-banner.ts proves a banner is a banner by finding the
495
+ * OPPOSITE-direction control next to the one it matched. A banner offering
496
+ * only "Accept and continue" and "Cookie Settings" has no opposite control at
497
+ * all, so that test failed and the whole banner went unseen — including by
498
+ * the preference-centre flow written for exactly this shape. A settings link
499
+ * is allowed to play the counterpart's part, but only when it names cookies
500
+ * outright: the vaguer tail of the open list ("settings", "preferences",
501
+ * "customize") sits in far too many page footers to prove anything.
502
+ */
503
+ export const PREFERENCE_CENTER_COUNTERPART_PHRASES = [
504
+ "cookie settings",
505
+ "cookies settings",
506
+ "cookie preferences",
507
+ "cookies preferences",
508
+ "storage preferences",
509
+ "cookie options",
510
+ "manage cookies",
511
+ "customize cookies",
512
+ "customise cookies",
513
+ "manage preferences",
514
+ "manage choices",
515
+ "manage options",
516
+ "let me choose",
517
+ ];
518
+ /**
519
+ * Context keywords for the CATEGORY TOGGLES inside a preference centre —
520
+ * deliberately narrower than BANNER_CONTEXT_KEYWORDS, which keeps "privacy".
521
+ *
522
+ * Nearly every marketing form on the web has a "I agree to the Terms and
523
+ * Privacy Policy" checkbox, so "privacy" alone pulls unrelated form controls
524
+ * into the toggle set; the panel is then computed as the smallest element
525
+ * containing all of them, which is the whole page, and the save-button search
526
+ * is scoped to the whole page with it. A consent category names cookies or
527
+ * consent.
528
+ */
529
+ export const PREFERENCE_PANEL_CONTEXT_KEYWORDS = ["cookie", "consent", "gdpr"];
530
+ /**
531
+ * How much text a container may hold and still count as the panel a toggle
532
+ * lives in. A preference centre's own copy is a few hundred to a few thousand
533
+ * characters; the <main> that happens to contain both a newsletter form and a
534
+ * cookie banner is one to two orders of magnitude larger (168k on
535
+ * meteomatics.com). Without this bound the keyword narrowing above is not
536
+ * enough on its own, since a page-level ancestor mentions cookies via the
537
+ * banner it contains.
538
+ */
539
+ export const PREFERENCE_PANEL_MAX_CONTEXT_CHARS = 6000;
540
+ /** Save-side equivalents of GENERIC_PREFERENCE_OPEN_PHRASES. */
541
+ export const GENERIC_SAVE_PREFERENCES_PHRASES = [
542
+ "save my preferences",
543
+ "save my choices",
544
+ "save my settings",
545
+ "save preferences",
546
+ "save settings",
547
+ "save choices",
548
+ "save selection",
549
+ "allow selection",
550
+ "confirm my choices",
551
+ "confirm my preferences",
552
+ "confirm choices",
553
+ "confirm selection",
554
+ "save and close",
555
+ "save & close",
556
+ "save and exit",
557
+ "save & exit",
558
+ "save and accept",
559
+ "save",
560
+ "confirm",
561
+ "apply",
562
+ "done",
563
+ ];
564
+ /**
565
+ * A category toggle matching any of these is the always-on essential bucket.
566
+ * Turning it off is not something a visitor can do, and attempting it is how
567
+ * a panel ends up in a state it refuses to save.
568
+ */
569
+ export const ESSENTIAL_CATEGORY_PATTERN = /necessar|essential|strictly|required|mandatory|always active|always on/i;
570
+ /**
571
+ * Everything after "Content Security Policy directive:" in a CSP violation
572
+ * message — the site's whole policy, reproduced verbatim by the browser.
573
+ *
574
+ * It is quoted evidence about the SITE, not about the resource that was
575
+ * blocked, and matching against it is how idtdna.com got a failing
576
+ * console-clean check for a blocked ad.doubleclick.net request: the policy it
577
+ * violated happens to allow cdn.dreamdata.cloud, so the word "dreamdata"
578
+ * appeared in a message that has nothing to do with Dreamdata. The blocked
579
+ * URL always precedes this clause, so a CSP error that really does block
580
+ * Dreamdata still matches on the part that names it.
581
+ */
582
+ const CSP_POLICY_CLAUSE = /Content Security Policy directive:[\s\S]*/i;
583
+ /**
584
+ * Is this console message ABOUT Dreamdata, rather than merely containing the
585
+ * word somewhere?
586
+ */
587
+ export function mentionsDreamdata(text) {
588
+ return /dreamdata/i.test(text.replace(CSP_POLICY_CLAUSE, ""));
589
+ }
590
+ /**
591
+ * The vocabulary a console message uses when something actually went wrong.
592
+ *
593
+ * Matched against the message the SITE wrote, not the browser's own wrapper,
594
+ * so it stays deliberately plain: the words a developer reaches for when
595
+ * reporting a failure, not a catalogue of Dreamdata error strings.
596
+ */
597
+ const FAILURE_VOCABULARY = /\b(fail(ed|s|ure)?|error|refus(ed|es)|block(ed|s)|denied|reject(ed|s)|unable|cannot|can't|couldn't|invalid|missing|not found|undefined is not|is not a function|null is not|exception|uncaught|unhandled|timed? ?out|timeout|retry|retrying|abort(ed)?|crash(ed)?|unsupported|deprecated|twice|duplicate snippet|conflict)\b/i;
598
+ /**
599
+ * Is this console.error a real failure report, or a debug line the site chose
600
+ * to print at error level?
601
+ *
602
+ * blackline.com's Nuxt plugin logs "🔌 [DREAMDATA] Plugin loaded - YOU SHOULD
603
+ * SEE THIS IN PRODUCTION" through console.error on every page load. Nothing
604
+ * went wrong — the plugin is announcing itself — but console-clean read the
605
+ * severity the site picked and sank an otherwise clean run. Severity alone is
606
+ * not the site's report of a failure; the words are.
607
+ *
608
+ * Judged from the message and nothing else. Whether the events were delivered
609
+ * is a different check's business, and letting it gate this one would make a
610
+ * console message mean different things on different runs.
611
+ */
612
+ export function describesFailure(text) {
613
+ return FAILURE_VOCABULARY.test(text.replace(CSP_POLICY_CLAUSE, ""));
614
+ }
615
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/evidence/constants.ts"],"names":[],"mappings":"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"}