@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,172 @@
1
+ import { EN_CAUSE_COPY } from "./copy.en.js";
2
+ import { CMP_VENDORS_IN_DOCS, docTitle, docUrl } from "./docs.js";
3
+ import { CAUSE_RULES } from "./rules.js";
4
+ /**
5
+ * Ranking order. "not-a-verdict" is first because it pre-empts rather than
6
+ * outranks — nothing else is reported at all once one of those fires.
7
+ */
8
+ const KIND_RANK = {
9
+ "not-a-verdict": 0,
10
+ blocking: 1,
11
+ leaking: 2,
12
+ "duplicate-counting": 3,
13
+ hardening: 4,
14
+ };
15
+ /**
16
+ * Three is a reader's limit, not an evidence limit. Across the 25-report
17
+ * corpus the cap never actually binds (the worst sites resolve to three), so
18
+ * it is a safety net rather than the mechanism — and anything it does trim is
19
+ * still named in `alsoFired`, so a count stays complete.
20
+ */
21
+ const MAX_CAUSES = 3;
22
+ /**
23
+ * Checks the diagnosis layer does not speak for at all.
24
+ *
25
+ * console-clean is the only one. Its warn says the install printed a
26
+ * Dreamdata message to the console — which turned out to be a false positive
27
+ * as a *finding*: blackline.com's "Plugin loaded - YOU SHOULD SEE THIS IN
28
+ * PRODUCTION" and qa.com's third-party destination note both ran clean
29
+ * otherwise, and neither is something to tell a customer about. It had a
30
+ * "hardening" cause of its own, and that cause was the whole of the headline
31
+ * on runs where nothing else was wrong — "Nothing is broken, one thing to
32
+ * harden" over a line of text no visitor sees.
33
+ *
34
+ * So the check stays exactly as it is in the per-check list, warn and all,
35
+ * for whoever is actually debugging an install. It just never enters the
36
+ * ledger: no cause may trigger on it, and it can never land in
37
+ * `unexplainedChecks` either, which is the other way a check reaches the
38
+ * summary. A run whose only unhealthy check is this one diagnoses as clean.
39
+ */
40
+ const NOT_DIAGNOSED = new Set(["console-clean"]);
41
+ /** A check that is "fail" or "warn" is claimable; nothing else is. */
42
+ function isUnhealthy(status) {
43
+ return status === "fail" || status === "warn";
44
+ }
45
+ function buildFacts(analysis, extras) {
46
+ const byId = new Map(analysis.checks.map((check) => [check.id, check]));
47
+ const byPhase = new Map(analysis.matrix.map((phase) => [phase.phase, phase]));
48
+ const vendor = analysis.cmpVendor;
49
+ return {
50
+ analysis,
51
+ check: (id) => byId.get(id),
52
+ statusOf: (id) => byId.get(id)?.status ?? "absent",
53
+ // A check's own `phases` field already means "the phases this status is
54
+ // about" — for a failure, only the ones that actually failed. Re-deriving
55
+ // that here is how a cause ends up attributing a one-phase failure to
56
+ // three phases.
57
+ failingPhases: (id) => byId.get(id)?.phases ?? [],
58
+ phase: (name) => byPhase.get(name),
59
+ observed: analysis.matrix,
60
+ unhealthy: analysis.checks
61
+ .filter((check) => isUnhealthy(check.status) && !NOT_DIAGNOSED.has(check.id))
62
+ .map((check) => check.id),
63
+ duplicates: extras.duplicates,
64
+ vars: {
65
+ cmpVendor: vendor,
66
+ cmpInDocs: vendor !== null && CMP_VENDORS_IN_DOCS.has(vendor),
67
+ installMethod: analysis.install.method,
68
+ phases: analysis.phasesObserved,
69
+ },
70
+ };
71
+ }
72
+ /**
73
+ * Only a "hardening" cause may be triggered by a "warn".
74
+ *
75
+ * A warn means the configuration is wrong AND this run proved something else
76
+ * held the line — nothing actually leaked (see CrossPhaseCheck). So a warn
77
+ * must never fire a blocking or leaking cause: sciex_com's Consent Mode
78
+ * defaults to granted and only a GTM blocking trigger keeps identified
79
+ * tracking off an unanswered page, which is worth reporting as a single point
80
+ * of failure and is emphatically NOT "this site tracks people who refused".
81
+ * Without this split, `consent-default-not-denied` matched that shape and
82
+ * reported a healthy site as leaking.
83
+ *
84
+ * A warn is still claimable: a cause that legitimately accounts for one may
85
+ * name it in `claims`. This governs triggering only.
86
+ */
87
+ function triggerStatusHolds(rule, facts) {
88
+ // An empty trigger belongs to a not-a-verdict rule, which gates on
89
+ // pageReadability/shape in `match` instead of on any check.
90
+ if (rule.trigger.length === 0)
91
+ return true;
92
+ const wantsUnhealthy = rule.triggerOn === "unhealthy" || rule.kind === "hardening";
93
+ const triggers = (id) => {
94
+ const status = facts.statusOf(id);
95
+ return wantsUnhealthy ? isUnhealthy(status) : status === "fail";
96
+ };
97
+ return rule.triggerMode === "all" ? rule.trigger.every(triggers) : rule.trigger.some(triggers);
98
+ }
99
+ function toDiagnosedCause(fired, copy) {
100
+ const entry = copy[fired.rule.id];
101
+ const doc = entry.doc(fired.vars);
102
+ // The phases a cause is about are the union of the phases its claimed
103
+ // checks were about, deduped in PhaseName order rather than discovery order
104
+ // so the field is stable across runs.
105
+ const claimed = new Set(fired.vars.phases);
106
+ return {
107
+ id: fired.rule.id,
108
+ kind: fired.rule.kind,
109
+ problem: entry.problem(fired.vars),
110
+ fix: entry.fix(fired.vars),
111
+ docUrl: docUrl(doc),
112
+ docTitle: docTitle(doc),
113
+ explains: fired.claims,
114
+ phases: ["clean", "grant", "reject"].filter((phase) => claimed.has(phase)),
115
+ };
116
+ }
117
+ /**
118
+ * Diagnose one run.
119
+ *
120
+ * `copy` is the i18n seam: another table over the same CauseIds and CauseVars
121
+ * swaps the language without touching a single predicate.
122
+ */
123
+ export function diagnose(analysis, extras, copy = EN_CAUSE_COPY) {
124
+ const facts = buildFacts(analysis, extras);
125
+ const hadFailures = analysis.checks.some((check) => check.status === "fail");
126
+ const open = new Set(facts.unhealthy);
127
+ const fired = [];
128
+ for (const rule of CAUSE_RULES) {
129
+ if (!triggerStatusHolds(rule, facts))
130
+ continue;
131
+ // Subsumption. If a check this rule exists for is unhealthy but no longer
132
+ // open, an earlier cause already accounted for it and this rule would be
133
+ // re-reporting the same problem one level down.
134
+ if (rule.trigger.some((id) => facts.unhealthy.includes(id) && !open.has(id)))
135
+ continue;
136
+ const match = rule.match(facts);
137
+ if (match === null)
138
+ continue;
139
+ // Filtering against `open` before recording is what makes double-claiming
140
+ // structurally impossible rather than merely unlikely.
141
+ const claims = [...new Set([...rule.trigger, ...match.claims])].filter((id) => open.has(id));
142
+ const phases = [...new Set(claims.flatMap((id) => facts.failingPhases(id)))];
143
+ fired.push({
144
+ rule,
145
+ claims,
146
+ vars: { ...facts.vars, ...match.vars, phases: phases.length > 0 ? phases : facts.vars.phases },
147
+ });
148
+ for (const id of claims)
149
+ open.delete(id);
150
+ if (match.preempt) {
151
+ // This run measured something other than the site. Anything else we
152
+ // could say about it would be a statement about a bot wall's challenge
153
+ // document, or about someone else's analytics.
154
+ return {
155
+ causes: fired.map((f) => toDiagnosedCause(f, copy)),
156
+ alsoFired: [],
157
+ unexplainedChecks: [],
158
+ hadFailures,
159
+ };
160
+ }
161
+ }
162
+ // Stable sort on a total-ordered key, so equal kinds keep rule order and
163
+ // the same evidence always produces the same ranking.
164
+ const ranked = [...fired].sort((a, b) => KIND_RANK[a.rule.kind] - KIND_RANK[b.rule.kind]);
165
+ return {
166
+ causes: ranked.slice(0, MAX_CAUSES).map((f) => toDiagnosedCause(f, copy)),
167
+ alsoFired: ranked.slice(MAX_CAUSES).map((f) => f.rule.id),
168
+ unexplainedChecks: [...open],
169
+ hadFailures,
170
+ };
171
+ }
172
+ //# sourceMappingURL=diagnose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../../../src/analysis/diagnosis/diagnose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ export const DOCS_BASE_URL = "https://developer.dreamdata.io";
2
+ export const DOC_LINKS = {
3
+ /** Carries the per-vendor outbound list, which is why a CMP fix links here. */
4
+ "consent-mode-turn-on": {
5
+ path: "client-side/google-consent-mode",
6
+ anchor: "how-to-turn-on-google-consent-mode-v2",
7
+ title: "How to turn on Google Consent Mode v2",
8
+ },
9
+ "consent-mode-verify": {
10
+ path: "client-side/google-consent-mode",
11
+ anchor: "how-to-check-that-google-consent-mode-v2-is-enabled",
12
+ title: "How to check Google Consent Mode v2 is enabled",
13
+ },
14
+ /** The recipe for firing a tag off a CMP's own dataLayer event. */
15
+ "gtm-custom-trigger": {
16
+ path: "client-side/troubleshooting",
17
+ anchor: "adding-a-custom-trigger-in-google-tag-manager",
18
+ title: "Adding a custom trigger in Google Tag Manager",
19
+ },
20
+ spa: {
21
+ path: "client-side/how-to-track-spas",
22
+ title: "Single-Page App SDK",
23
+ },
24
+ "script-installation": {
25
+ path: "client-side/script-installation",
26
+ title: "Website Script",
27
+ },
28
+ "script-installation-gtm": {
29
+ path: "client-side/script-installation",
30
+ anchor: "option-b-install-with-google-tag-manager",
31
+ title: "Install with Google Tag Manager",
32
+ },
33
+ "script-installation-troubleshooting": {
34
+ path: "client-side/script-installation",
35
+ anchor: "troubleshooting-common-issues",
36
+ title: "Troubleshooting common issues",
37
+ },
38
+ cookieless: {
39
+ path: "client-side/cookieless",
40
+ title: "Cookieless Account Analytics",
41
+ },
42
+ debugging: {
43
+ path: "client-side/debugging-dreamdata-analytics",
44
+ title: "Test your tracking",
45
+ },
46
+ csp: {
47
+ path: "client-side/csp",
48
+ title: "Content Security Policy",
49
+ },
50
+ cookies: {
51
+ path: "faq/cookies",
52
+ title: "Cookies",
53
+ },
54
+ };
55
+ /** Composed in one place, so there is one thing for a test to assert about. */
56
+ export function docUrl(id) {
57
+ const link = DOC_LINKS[id];
58
+ return `${DOCS_BASE_URL}/${link.path}${link.anchor ? `#${link.anchor}` : ""}`;
59
+ }
60
+ export function docTitle(id) {
61
+ return DOC_LINKS[id].title;
62
+ }
63
+ /**
64
+ * The CMP vendors the Consent Mode page actually has setup instructions for.
65
+ *
66
+ * The intersection of two lists: the vendors named in
67
+ * `docs/client-side/google-consent-mode.md` (CookieBot, CookieYes, OneTrust,
68
+ * Osano, HubSpot, Cookie Information, Framer, Klaro) and the vendor ids this
69
+ * harness can detect (CMP_VENDORS and DOM_ONLY_CMP_VENDORS in
70
+ * src/evidence/constants.ts). Cookie Information, Framer and Klaro are in the
71
+ * docs but undetectable, so they cannot appear here; usercentrics, pardot,
72
+ * termly, complianz, trustarc, ketch, cookiescript and the two IAB shims are
73
+ * detectable but absent from the docs, so a fix sentence for them has to
74
+ * degrade — see CMP_DISPLAY_NAMES and the copy table.
75
+ *
76
+ * Real reports currently detect usercentrics (2 sites) and pardot (1), so
77
+ * that degrade path is live, not theoretical. docs.test.ts asserts this set
78
+ * in BOTH directions, so adding Usercentrics to the docs without adding it
79
+ * here fails the build rather than leaving the sentence needlessly vague.
80
+ */
81
+ export const CMP_VENDORS_IN_DOCS = new Set([
82
+ "cookiebot",
83
+ "cookieyes",
84
+ "onetrust",
85
+ "osano",
86
+ "hubspot",
87
+ ]);
88
+ /**
89
+ * How a vendor id is spelled in a sentence a human reads.
90
+ *
91
+ * Only ids that can actually reach a sentence need an entry; anything missing
92
+ * falls back to the raw id, which is still readable ("ketch"). "unknown-custom"
93
+ * is deliberately absent: a bespoke banner has no name to print, and the copy
94
+ * table branches on that rather than printing a placeholder.
95
+ */
96
+ export const CMP_DISPLAY_NAMES = {
97
+ cookiebot: "Cookiebot",
98
+ cookieyes: "CookieYes",
99
+ onetrust: "OneTrust",
100
+ osano: "Osano",
101
+ hubspot: "HubSpot",
102
+ usercentrics: "Usercentrics",
103
+ pardot: "Pardot",
104
+ termly: "Termly",
105
+ complianz: "Complianz",
106
+ trustarc: "TrustArc",
107
+ ketch: "Ketch",
108
+ cookiescript: "CookieScript",
109
+ "iab-tcf": "the IAB TCF CMP",
110
+ "iab-cmp": "the IAB CMP",
111
+ "generic-cookieconsent": "the CookieConsent banner",
112
+ };
113
+ export function cmpDisplayName(vendor) {
114
+ if (vendor === null || vendor === "unknown-custom")
115
+ return "the cookie banner";
116
+ return CMP_DISPLAY_NAMES[vendor] ?? vendor;
117
+ }
118
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/analysis/diagnosis/docs.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,aAAa,GAAG,gCAAgC,CAAC;AAE9D,MAAM,CAAC,MAAM,SAAS,GAA+B;IACnD,+EAA+E;IAC/E,sBAAsB,EAAE;QACtB,IAAI,EAAE,iCAAiC;QACvC,MAAM,EAAE,uCAAuC;QAC/C,KAAK,EAAE,uCAAuC;KAC/C;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,iCAAiC;QACvC,MAAM,EAAE,qDAAqD;QAC7D,KAAK,EAAE,gDAAgD;KACxD;IACD,mEAAmE;IACnE,oBAAoB,EAAE;QACpB,IAAI,EAAE,6BAA6B;QACnC,MAAM,EAAE,+CAA+C;QACvD,KAAK,EAAE,+CAA+C;KACvD;IACD,GAAG,EAAE;QACH,IAAI,EAAE,+BAA+B;QACrC,KAAK,EAAE,qBAAqB;KAC7B;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,iCAAiC;QACvC,KAAK,EAAE,gBAAgB;KACxB;IACD,yBAAyB,EAAE;QACzB,IAAI,EAAE,iCAAiC;QACvC,MAAM,EAAE,0CAA0C;QAClD,KAAK,EAAE,iCAAiC;KACzC;IACD,qCAAqC,EAAE;QACrC,IAAI,EAAE,iCAAiC;QACvC,MAAM,EAAE,+BAA+B;QACvC,KAAK,EAAE,+BAA+B;KACvC;IACD,UAAU,EAAE;QACV,IAAI,EAAE,wBAAwB;QAC9B,KAAK,EAAE,8BAA8B;KACtC;IACD,SAAS,EAAE;QACT,IAAI,EAAE,2CAA2C;QACjD,KAAK,EAAE,oBAAoB;KAC5B;IACD,GAAG,EAAE;QACH,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,yBAAyB;KACjC;IACD,OAAO,EAAE;QACP,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,SAAS;KACjB;CACF,CAAC;AAEF,+EAA+E;AAC/E,MAAM,UAAU,MAAM,CAAC,EAAa;IAClC,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO,GAAG,aAAa,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,EAAa;IACpC,OAAO,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IAC9D,WAAW;IACX,WAAW;IACX,UAAU;IACV,OAAO;IACP,SAAS;CACV,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,cAAc;IAC5B,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,iBAAiB;IAC5B,SAAS,EAAE,aAAa;IACxB,uBAAuB,EAAE,0BAA0B;CACpD,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,MAAqB;IAClD,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,gBAAgB;QAAE,OAAO,mBAAmB,CAAC;IAC/E,OAAO,iBAAiB,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { diagnose } from "./diagnose.js";
2
+ export { CMP_VENDORS_IN_DOCS, DOCS_BASE_URL, DOC_LINKS, cmpDisplayName, docTitle, docUrl } from "./docs.js";
3
+ export { EN_CAUSE_COPY, EN_CAUSE_LABELS, causeLabel } from "./copy.en.js";
4
+ export { CAUSE_RULES } from "./rules.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analysis/diagnosis/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5G,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}