@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,425 @@
1
+ import { logger } from "../utils/logger.js";
2
+ import { CMP_SCRIPT_BLOCK_MARKERS, COOKIELESS_BUNDLE_ELEMENT_ID, DREAMDATA_COOKIELESS_SCRIPT_FILE, DREAMDATA_COOKIELESS_SCRIPT_HOST, DREAMDATA_NORMAL_SCRIPT_FILE, DREAMDATA_ORIGIN_IN_SNIPPET, GTM_CONTAINER_ID_IN_TEXT, GTM_CONTAINER_ID_PATTERN, gtmContainerUrl, HELPER_SCRIPT_PATH_MARKERS, isDreamdataUrl, NORMAL_BUNDLE_ELEMENT_ID, NORMAL_COMPANION_REFERENCE, THIRD_PARTY_SEGMENT_SCRIPT_HOST, WRITE_KEY_FROM_LOAD_CALL, WRITE_KEY_FROM_SCRIPT_SRC, WRITE_KEY_FROM_SNIPPET, } from "./constants.js";
3
+ import { parseGtmResource, readDreamdataTags } from "./gtm-container.js";
4
+ /** A page can name more containers than are worth fetching; keep it bounded. */
5
+ const MAX_CONTAINERS_SCANNED = 5;
6
+ function attr(tag, name) {
7
+ const match = new RegExp(`\\s${name}=["']([^"']*)["']`, "i").exec(tag);
8
+ return match ? match[1] : null;
9
+ }
10
+ /** Which CMP, if any, has neutralised this tag instead of removing it. */
11
+ function blockedBy(tag) {
12
+ return CMP_SCRIPT_BLOCK_MARKERS.find((marker) => marker.pattern.test(tag))?.vendor ?? null;
13
+ }
14
+ function isHelperScript(src) {
15
+ return HELPER_SCRIPT_PATH_MARKERS.some((marker) => src.includes(marker));
16
+ }
17
+ /**
18
+ * Classifies one tag/snippet by which bundle it actually loads.
19
+ *
20
+ * The two kinds are decided independently — the previous
21
+ * `normal = present && !cookieless` shortcut could only ever report one
22
+ * bundle per page, so a dual install read as cookieless-only and then as
23
+ * "cookieless by design".
24
+ */
25
+ export function classifyBundleText(text) {
26
+ if (isHelperScript(text))
27
+ return "helper";
28
+ if (text.includes(DREAMDATA_COOKIELESS_SCRIPT_FILE) ||
29
+ text.includes(DREAMDATA_COOKIELESS_SCRIPT_HOST) ||
30
+ text.includes(COOKIELESS_BUNDLE_ELEMENT_ID)) {
31
+ return "cookieless";
32
+ }
33
+ if (text.includes(DREAMDATA_NORMAL_SCRIPT_FILE) || NORMAL_COMPANION_REFERENCE.test(text))
34
+ return "normal";
35
+ // A Dreamdata origin with no recognisable bundle filename: the tag belongs
36
+ // to the install (the caller already filtered by host) but which bundle it
37
+ // serves cannot be read off the text, so say so rather than guessing.
38
+ return "unknown";
39
+ }
40
+ function extractWriteKey(candidates) {
41
+ for (const candidate of candidates) {
42
+ const key = WRITE_KEY_FROM_SCRIPT_SRC.exec(candidate)?.[1] ??
43
+ WRITE_KEY_FROM_SNIPPET.exec(candidate)?.[1] ??
44
+ WRITE_KEY_FROM_LOAD_CALL.exec(candidate)?.[1] ??
45
+ null;
46
+ if (key !== null)
47
+ return key;
48
+ }
49
+ return null;
50
+ }
51
+ /**
52
+ * Finds every installed tracking bundle. Matches script tags whose *src*
53
+ * points at a configured Dreamdata host, and inline snippets that reference
54
+ * one — never a tag that merely contains the word "dreamdata" (e.g. another
55
+ * vendor's `referer=dreamdata.io` query string).
56
+ *
57
+ * Tags a CMP has neutralised (type="text/plain" and friends) are still
58
+ * matched and reported with `blockedBy` set: "installed but gated" and "not
59
+ * installed" are the two states this whole tool has to tell apart.
60
+ */
61
+ export async function readScriptPresence(page, extraHosts) {
62
+ return readScriptPresenceFromHtml(await page.content(), page.url(), extraHosts);
63
+ }
64
+ /**
65
+ * The same detection over markup we hold as a string rather than a live DOM.
66
+ *
67
+ * Split out because a rendered DOM is not the only place this question gets
68
+ * asked. When a bot wall serves the browser an interstitial instead of the
69
+ * site, the DOM has nothing in it and every field below reads "not
70
+ * installed" — while a plain HTTP GET of the same URL is served the real
71
+ * markup (see http-probe.ts). One detector, two sources.
72
+ */
73
+ export function readScriptPresenceFromHtml(html, pageUrl, extraHosts) {
74
+ const resolve = (src) => {
75
+ try {
76
+ return new URL(src, pageUrl).toString();
77
+ }
78
+ catch {
79
+ return src;
80
+ }
81
+ };
82
+ // Match every script tag with a src, then decide by its resolved *hostname*.
83
+ // Testing the whole URL would let a third party's `?referer=dreamdata.io`
84
+ // query string masquerade as an install.
85
+ const srcTags = [...html.matchAll(/<script[^>]*\ssrc=["']([^"']+)["'][^>]*>/gi)]
86
+ .map((m) => ({ tag: m[0], src: resolve(m[1]) }))
87
+ .filter((entry) => isDreamdataUrl(entry.src, extraHosts));
88
+ // An inline snippet counts only when it writes out a Dreamdata origin, so a
89
+ // real Segment install (cdn.segment.com) is not mistaken for a Dreamdata one.
90
+ const inlineTags = [...html.matchAll(/<script(?![^>]*\ssrc=)[^>]*>([\s\S]*?)<\/script>/gi)]
91
+ .map((m) => ({ tag: m[0], code: m[1] }))
92
+ .filter((entry) => DREAMDATA_ORIGIN_IN_SNIPPET.test(entry.tag) || extraHosts.some((host) => entry.tag.includes(host)));
93
+ const matches = [
94
+ ...srcTags.map((entry) => ({
95
+ kind: classifyBundleText(entry.src + entry.tag),
96
+ src: entry.src,
97
+ elementId: attr(entry.tag, "id"),
98
+ type: attr(entry.tag, "type"),
99
+ blockedBy: blockedBy(entry.tag),
100
+ inline: false,
101
+ })),
102
+ ...inlineTags.map((entry) => ({
103
+ kind: classifyBundleText(entry.tag),
104
+ src: null,
105
+ elementId: attr(entry.tag, "id"),
106
+ type: attr(entry.tag, "type"),
107
+ blockedBy: blockedBy(entry.tag),
108
+ inline: true,
109
+ })),
110
+ ];
111
+ const bundles = matches.filter((m) => m.kind !== "helper");
112
+ const present = bundles.length > 0;
113
+ // Several inline scripts may name a Dreamdata origin (a CSP string, an image
114
+ // URL). Prefer one that actually configures a writeKey — that is the loader
115
+ // snippet; anything else is an incidental mention.
116
+ const loaderSnippet = inlineTags.find((entry) => WRITE_KEY_FROM_SNIPPET.test(entry.tag) || WRITE_KEY_FROM_LOAD_CALL.test(entry.tag))
117
+ ?.tag ?? null;
118
+ const primary = srcTags.find((entry) => !isHelperScript(entry.src))?.tag ?? loaderSnippet ?? inlineTags[0]?.tag ?? null;
119
+ // Look for the key in the bundle src, then in the loader snippet. Once the
120
+ // bundle has loaded, the src is the primary match but usually carries no
121
+ // key — falling back to the loader keeps the report stable across the pre-
122
+ // and post-consent loads of the same page.
123
+ const writeKey = extractWriteKey([
124
+ srcTags.find((entry) => !isHelperScript(entry.src))?.src,
125
+ loaderSnippet,
126
+ primary,
127
+ ...matches.map((m) => m.src),
128
+ ].filter((v) => typeof v === "string"));
129
+ /** A tag whose companion reference proves the *other* bundle is expected. */
130
+ const cookielessLoaderNamingCompanion = inlineTags.find((entry) => classifyBundleText(entry.tag) === "cookieless" && NORMAL_COMPANION_REFERENCE.test(entry.code));
131
+ return {
132
+ present,
133
+ writeKey,
134
+ htmlSnippet: primary?.slice(0, 2000) ?? null,
135
+ // Reported independently of which tag matched, so it stays true whether or
136
+ // not the bundle has loaded yet.
137
+ segmentCompatSnippet: loaderSnippet !== null,
138
+ helperScriptOnly: !present && matches.length > 0,
139
+ cookielessSnippet: matches.some((m) => m.kind === "cookieless"),
140
+ normalSnippet: matches.some((m) => m.kind === "normal"),
141
+ matches,
142
+ cookielessLoaderNamesNormalCompanion: cookielessLoaderNamingCompanion !== undefined,
143
+ };
144
+ }
145
+ /**
146
+ * A real (non-Dreamdata) Segment install: a `<script src>` resolving to
147
+ * Segment's own CDN.
148
+ *
149
+ * `readScriptPresenceFromHtml` above filters everything to Dreamdata-owned
150
+ * hosts by design, so a genuine Segment tag never reaches it — this is the
151
+ * separate, narrower check for exactly that tag. It exists because a
152
+ * customer whose Dreamdata connection is a server-side Segment destination
153
+ * (see ThirdPartySegmentEvidence) has NO Dreamdata-hosted script on the page
154
+ * at all; this is the one piece of client-side evidence such a deployment
155
+ * leaves.
156
+ */
157
+ export function detectThirdPartySegmentScript(html, pageUrl) {
158
+ const resolve = (src) => {
159
+ try {
160
+ return new URL(src, pageUrl).toString();
161
+ }
162
+ catch {
163
+ return src;
164
+ }
165
+ };
166
+ const match = [...html.matchAll(/<script[^>]*\ssrc=["']([^"']+)["'][^>]*>/gi)]
167
+ .map((m) => resolve(m[1]))
168
+ .find((src) => {
169
+ try {
170
+ return new URL(src).hostname.toLowerCase() === THIRD_PARTY_SEGMENT_SCRIPT_HOST;
171
+ }
172
+ catch {
173
+ return false;
174
+ }
175
+ });
176
+ return { scriptDetected: match !== undefined, scriptSrc: match ?? null };
177
+ }
178
+ /**
179
+ * Every GTM container id a blob of text names.
180
+ *
181
+ * Frameworks often keep the id in page config and load GTM later (or not at
182
+ * all in a headless run), so the runtime object can be empty on a site whose
183
+ * container is exactly what we need to read. The id format is specific enough
184
+ * to lift straight out of the markup.
185
+ */
186
+ export function gtmContainerIdsFromText(text) {
187
+ return [...new Set((text.match(GTM_CONTAINER_ID_IN_TEXT) ?? []).map((id) => id.toUpperCase()))].filter((id) => GTM_CONTAINER_ID_PATTERN.test(id));
188
+ }
189
+ /**
190
+ * Reads the published GTM container(s) the page loads.
191
+ *
192
+ * A tag-manager-injected bundle never enters the DOM until its trigger and
193
+ * consent conditions are met, so the DOM cannot answer "is a normal bundle
194
+ * part of this deployment?" for a GTM install — the container can. This is
195
+ * the difference between reporting workvivo.com as cookieless-by-design and
196
+ * reporting it as a normal bundle gated on analytics_storage.
197
+ */
198
+ export async function scanTagManagerContainers(page, timeoutMs,
199
+ /** Shared across a run's phases so one container is fetched once, not per phase. */
200
+ cache = new Map(),
201
+ /**
202
+ * Container ids found somewhere other than this page — in practice the
203
+ * run's plain-HTTP fetch of the same URL. A bot wall leaves the DOM with no
204
+ * container id in it at all, and without these the scan reports
205
+ * `scanned: false` on a site whose container is the only place the bundle
206
+ * appears. See http-probe.ts.
207
+ */
208
+ extraContainerIds = []) {
209
+ const errors = [];
210
+ const containerIds = await page
211
+ .evaluate(() => {
212
+ const w = window;
213
+ const fromRuntime = Object.keys(w.google_tag_manager ?? {});
214
+ // Server-side/first-party GTM loads gtm.js from the site's own domain,
215
+ // so the runtime object is often the only place its id shows up.
216
+ const fromTags = Array.from(document.querySelectorAll("script[src]"))
217
+ .map((s) => {
218
+ try {
219
+ return new URL(s.src).searchParams.get("id") ?? "";
220
+ }
221
+ catch {
222
+ return "";
223
+ }
224
+ })
225
+ .filter(Boolean);
226
+ return [...new Set([...fromRuntime, ...fromTags])];
227
+ })
228
+ .catch((err) => {
229
+ errors.push(`Could not read tag manager container ids: ${err.message}`);
230
+ return [];
231
+ });
232
+ const fromMarkup = await page
233
+ .content()
234
+ .then(gtmContainerIdsFromText)
235
+ .catch(() => []);
236
+ const gtmIds = [...new Set([...containerIds, ...fromMarkup, ...extraContainerIds])]
237
+ .filter((id) => GTM_CONTAINER_ID_PATTERN.test(id))
238
+ .slice(0, MAX_CONTAINERS_SCANNED);
239
+ if (gtmIds.length === 0) {
240
+ return {
241
+ scanned: false,
242
+ containerIds: [],
243
+ containers: [],
244
+ normalBundleTagFound: false,
245
+ cookielessBundleTagFound: false,
246
+ thirdPartySegmentTagFound: false,
247
+ normalBundleConsentGate: null,
248
+ normalBundleBlockingTriggers: null,
249
+ errors,
250
+ };
251
+ }
252
+ const containers = [];
253
+ // Deliberately NOT derived from `containers`: these stay the substring read
254
+ // on the raw body, which is the broader signal. A container whose JSON this
255
+ // module cannot parse still plainly contains the filename, and narrowing
256
+ // these to the parsed view would make such an install read as having no
257
+ // Dreamdata tag at all — which is what cookielessAbsentByDesign turns into
258
+ // a not_applicable, so beckman.com's deliberate standard-only install would
259
+ // start reading as a failure.
260
+ let normalBundleTagFound = false;
261
+ let cookielessBundleTagFound = false;
262
+ let thirdPartySegmentTagFound = false;
263
+ let normalBundleConsentGate = null;
264
+ let normalBundleBlockingTriggers = null;
265
+ // gtmIds is already de-duplicated (see the Set above), so fetching every
266
+ // container concurrently is safe — nothing here writes to `cache` twice
267
+ // for the same id within one call.
268
+ const bodies = await Promise.all(gtmIds.map(async (id) => {
269
+ if (cache.has(id))
270
+ return [id, cache.get(id)];
271
+ const body = await page.request
272
+ .get(gtmContainerUrl(id), { timeout: Math.min(10_000, timeoutMs) })
273
+ .then((res) => (res.ok() ? res.text() : Promise.reject(new Error(`HTTP ${res.status()}`))))
274
+ .catch((err) => {
275
+ logger.debug(`GTM container ${id} unreadable`, err);
276
+ errors.push(`Container ${id} could not be read: ${err.message}`);
277
+ return null;
278
+ });
279
+ cache.set(id, body);
280
+ return [id, body];
281
+ }));
282
+ for (const [id, body] of bodies) {
283
+ if (body === null) {
284
+ // The fetch failure is already in `errors`; repeating it here is what
285
+ // lets a reader tell "this container has no Dreamdata tag" apart from
286
+ // "nobody could look" — credo.ai's second container 404s, and without
287
+ // this the report describes it the same way as a clean container.
288
+ containers.push({
289
+ containerId: id,
290
+ read: false,
291
+ error: errors.find((message) => message.startsWith(`Container ${id} `)) ?? "could not be read",
292
+ version: null,
293
+ bundles: [],
294
+ tags: [],
295
+ });
296
+ continue;
297
+ }
298
+ const resource = parseGtmResource(body);
299
+ const tags = resource === null
300
+ ? []
301
+ : readDreamdataTags(resource, {
302
+ normal: DREAMDATA_NORMAL_SCRIPT_FILE,
303
+ cookieless: DREAMDATA_COOKIELESS_SCRIPT_FILE,
304
+ });
305
+ containers.push({
306
+ containerId: id,
307
+ // A body that arrived but would not parse is still a failed read for
308
+ // the purposes below: nothing can be said about its tags.
309
+ read: resource !== null,
310
+ error: resource === null ? "the container body did not parse" : null,
311
+ version: resource?.version ?? null,
312
+ bundles: [...new Set(tags.flatMap((tag) => tag.bundles))],
313
+ tags,
314
+ });
315
+ if (body.includes(DREAMDATA_COOKIELESS_SCRIPT_FILE))
316
+ cookielessBundleTagFound = true;
317
+ // clio.com is the case this exists for: the container ships a tag
318
+ // loading Segment's own library, triggered on its own schedule, so
319
+ // neither the live DOM nor a request capture ever saw it within this
320
+ // crawl's window — this is the only place it was findable at all.
321
+ if (body.includes(THIRD_PARTY_SEGMENT_SCRIPT_HOST))
322
+ thirdPartySegmentTagFound = true;
323
+ const normalAt = body.indexOf(DREAMDATA_NORMAL_SCRIPT_FILE);
324
+ if (normalAt === -1)
325
+ continue;
326
+ normalBundleTagFound = true;
327
+ // Parsing the container properly is what surfaces the tag's BLOCKING
328
+ // triggers, which the consent list alone cannot show — see
329
+ // gtm-container.ts. Read off the same parse as `containers` above rather
330
+ // than parsing twice: brace-matching a published container is the
331
+ // expensive part of this scan. The regex below stays as the fallback for
332
+ // a body whose shape the parser does not recognise.
333
+ const parsed = tags.find((tag) => tag.bundles.includes("normal"));
334
+ if (parsed !== undefined) {
335
+ normalBundleConsentGate = parsed.consent;
336
+ if (parsed.blockedWhen.length > 0) {
337
+ normalBundleBlockingTriggers = [...(normalBundleBlockingTriggers ?? []), ...parsed.blockedWhen];
338
+ }
339
+ continue;
340
+ }
341
+ // GTM serialises each tag as one object with its consent requirements
342
+ // ahead of the HTML it injects, so the nearest preceding "consent" list
343
+ // is this tag's gate. A heuristic on a minified blob — reported as a
344
+ // corroborating signal, never as the sole basis for a verdict.
345
+ const window_ = body.slice(Math.max(0, normalAt - 4000), normalAt);
346
+ const consentMatch = /"consent":\["list"((?:,"[a-z_]+")+)\]/g;
347
+ let last = null;
348
+ for (let m = consentMatch.exec(window_); m !== null; m = consentMatch.exec(window_))
349
+ last = m;
350
+ if (last) {
351
+ normalBundleConsentGate = last[1]
352
+ .split(",")
353
+ .map((s) => s.replace(/"/g, "").trim())
354
+ .filter(Boolean);
355
+ }
356
+ }
357
+ return {
358
+ scanned: true,
359
+ containerIds: gtmIds,
360
+ containers,
361
+ normalBundleTagFound,
362
+ cookielessBundleTagFound,
363
+ thirdPartySegmentTagFound,
364
+ normalBundleConsentGate,
365
+ normalBundleBlockingTriggers: normalBundleBlockingTriggers === null ? null : [...new Set(normalBundleBlockingTriggers)],
366
+ errors,
367
+ };
368
+ }
369
+ /**
370
+ * Every reason to believe a normal (cookie-based) bundle is part of this
371
+ * deployment, in descending order of strength.
372
+ *
373
+ * A DOM snapshot alone cannot answer this: a consent-gated bundle is absent
374
+ * from the DOM by design, which is exactly when the question matters. Only an
375
+ * empty result justifies calling an install cookieless-by-design.
376
+ */
377
+ export function deriveNormalBundleSignals(inputs) {
378
+ const { presence, tagManager, dreamdataIsStubQueue, normalRequestCount, httpMarkup = null } = inputs;
379
+ const signals = [];
380
+ if (normalRequestCount > 0) {
381
+ signals.push({ source: "request", detail: `${normalRequestCount} request(s) sent by the normal bundle` });
382
+ }
383
+ for (const match of presence.matches.filter((m) => m.kind === "normal")) {
384
+ signals.push(match.blockedBy === null
385
+ ? { source: "dom-script", detail: `<script${match.src ? ` src=${match.src}` : " (inline)"}> is in the DOM` }
386
+ : {
387
+ source: "cmp-blocked-script",
388
+ detail: `<script${match.src ? ` src=${match.src}` : " (inline)"}> is present but neutralised by ${match.blockedBy}`,
389
+ });
390
+ }
391
+ if (tagManager.normalBundleTagFound) {
392
+ const gate = tagManager.normalBundleConsentGate;
393
+ // The exception is named alongside the consent list: a tag can satisfy
394
+ // its consent gate and still be held back by one, and then it is not
395
+ // Consent Mode keeping tracking off before the banner is answered.
396
+ const blocking = tagManager.normalBundleBlockingTriggers;
397
+ signals.push({
398
+ source: "tag-manager-container",
399
+ detail: `GTM container ${tagManager.containerIds.join(", ")} ships a tag loading ${DREAMDATA_NORMAL_SCRIPT_FILE}${gate ? `, gated on ${gate.join(", ")}` : ""}${blocking?.length ? `, blocked whenever ${blocking.join("; or ")}` : ""}`,
400
+ });
401
+ }
402
+ // Only worth stating when the DOM did not already show it: on a page the
403
+ // browser rendered normally these two agree, and one signal per reason is
404
+ // the point of this list.
405
+ if (httpMarkup?.normalSnippet && !presence.normalSnippet) {
406
+ signals.push({
407
+ source: "http-markup",
408
+ detail: `the markup served to a plain HTTP GET of this URL contains a tag loading ${DREAMDATA_NORMAL_SCRIPT_FILE}, though the rendered page did not`,
409
+ });
410
+ }
411
+ if (presence.cookielessLoaderNamesNormalCompanion) {
412
+ signals.push({
413
+ source: "cookieless-loader-companion",
414
+ detail: `the cookieless loader checks for #${NORMAL_BUNDLE_ELEMENT_ID}, the normal bundle's tag`,
415
+ });
416
+ }
417
+ if (dreamdataIsStubQueue) {
418
+ signals.push({
419
+ source: "stub-queue",
420
+ detail: "window.dreamdata is the loader's stub array — calls are queued for a normal bundle that never arrived",
421
+ });
422
+ }
423
+ return signals;
424
+ }
425
+ //# sourceMappingURL=script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script.js","sourceRoot":"","sources":["../../src/evidence/script.ts"],"names":[],"mappings":"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"}