feat: ga consent mode v2

This commit is contained in:
2026-10-12 17:32:16 +05:00
parent 1981afbc64
commit dad71f0bda
4 changed files with 188 additions and 151 deletions
+55
View File
@@ -0,0 +1,55 @@
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
// The analytics tag ships as a static file (`web/static/ga.js`) so it can be
// gated by PUBLIC_GA_ENABLED at build time; these guards cover the promises of
// docs/plan-seo.md section 0 - cookieless, no ads, and nothing about the image.
const WEB_DIR = fileURLToPath(new URL("../../", import.meta.url));
const GA_PATH = join(WEB_DIR, "static", "ga.js");
function sourceFiles(dir: string): string[] {
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const full = join(dir, entry.name);
if (entry.isDirectory()) return sourceFiles(full);
return entry.name.endsWith(".ts") || entry.name.endsWith(".svelte")
? [full]
: [];
});
}
describe("GA tag configuration", () => {
const ga = readFileSync(GA_PATH, "utf8");
it("denies analytics and ad storage before the config call", () => {
for (const key of [
"analytics_storage",
"ad_storage",
"ad_user_data",
"ad_personalization",
]) {
expect(ga, key).toMatch(new RegExp(`${key}:\\s*"denied"`));
}
expect(ga.indexOf('gtag("consent"')).toBeLessThan(
ga.indexOf('gtag("config"'),
);
});
it("keeps remarketing and Google signals off", () => {
expect(ga).toMatch(/allow_google_signals:\s*false/);
expect(ga).toMatch(/allow_ad_personalization_signals:\s*false/);
});
it("sends nothing but the page view", () => {
const callers = sourceFiles(join(WEB_DIR, "src"))
.filter((file) => !file.endsWith(".test.ts"))
.filter((file) =>
/gtag\s*\(|dataLayer\.push/.test(readFileSync(file, "utf8")),
);
expect(
callers.map((file) => file.replace(WEB_DIR, "")),
"gtag is configured in one place only",
).toEqual([]);
});
});
+22 -1
View File
@@ -2,5 +2,26 @@ window.dataLayer = window.dataLayer || [];
function gtag() {
window.dataLayer.push(arguments);
}
// Consent Mode v2, denied by default, before any config call: analytics then
// run in cookieless mode (pings without cookies, no cross-site identity), which
// is the configuration promised in `docs/plan-seo.md` section 0. No consent UI
// is planned - nothing here is granted by default.
gtag("consent", "default", {
analytics_storage: "denied",
ad_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
});
gtag("js", new Date());
gtag("config", "G-MEQJ7JHRQQ");
gtag("config", "G-MEQJ7JHRQQ", {
// No remarketing and no ad audiences: this site takes no ads (section 0).
allow_google_signals: false,
allow_ad_personalization_signals: false,
// GA4 drops the full IP either way; this removes the parameter outright.
ip_collection: false,
});