[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Cookie Consent Manager

Build a Cookie Consent Manager

Build a GDPR/CCPA-compliant cookie consent system with granular category controls, consent storage, third-party script blocking, preference center, and consent analytics.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 6 skills

Avg quality 97/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

hono

v1.1.0

Hono is a small web framework built on Web Standards (Request/Response) that runs unchanged on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda and Vercel. Use it when the user asks to build a Hono API, add Zod validation, JWT auth, CORS or middleware, use the type-safe RPC client (hc), render JSX, stream responses, or deploy one codebase to several runtimes.

100/100 quality
6.20× impact
SAFE· stale score
View skill
>

nextjs

v1.1.0

Assists with building production-grade React applications using Next.js 16. Use when working with the App Router, Server Components, Server Actions, the proxy file (formerly middleware), Cache Components, or deploying to Vercel or self-hosted environments. Trigger words: nextjs, next.js, app router, server components, server actions, use cache, proxy.ts, react framework, ssr, isr.

100/100 quality
5.29× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Vera leads compliance at a 30-person company. They use 15 third-party scripts (Google Analytics, Hotjar, Intercom, Facebook Pixel, etc.) that all set cookies. GDPR requires explicit consent before non-essential cookies. CCPA requires an opt-out mechanism. Their current banner is a non-functional "We use cookies" notice that doesn't actually block anything. A €20K GDPR fine from a competitor scared the board. They need real consent management: block scripts until consent, track preferences, and let users change their mind.

Step 1: Build the Consent Manager

typescript
// src/consent/manager.ts — Cookie consent with script blocking and preference center
import { pool } from "../db";
import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

type ConsentCategory = "necessary" | "analytics" | "marketing" | "personalization";

interface ConsentPreferences {
  necessary: true;             // always true, can't be disabled
  analytics: boolean;
  marketing: boolean;
  personalization: boolean;
  timestamp: string;
  version: string;
  ip: string;
  userAgent: string;
}

interface CookieDefinition {
  name: string;
  category: ConsentCategory;
  provider: string;
  purpose: string;
  duration: string;
  type: "first-party" | "third-party";
}

// All cookies used on the site (transparency requirement)
const COOKIE_REGISTRY: CookieDefinition[] = [
  { name: "session_id", category: "necessary", provider: "Our Site", purpose: "User session", duration: "Session", type: "first-party" },
  { name: "csrf_token", category: "necessary", provider: "Our Site", purpose: "Security", duration: "Session", type: "first-party" },
  { name: "_ga", category: "analytics", provider: "Google Analytics", purpose: "Visitor statistics", duration: "2 years", type: "third-party" },
  { name: "_gid", category: "analytics", provider: "Google Analytics", purpose: "Session tracking", duration: "24 hours", type: "third-party" },
  { name: "_hjid", category: "analytics", provider: "Hotjar", purpose: "Heatmaps and recordings", duration: "1 year", type: "third-party" },
  { name: "_fbp", category: "marketing", provider: "Facebook", purpose: "Ad targeting", duration: "3 months", type: "third-party" },
  { name: "intercom-id", category: "personalization", provider: "Intercom", purpose: "Chat widget", duration: "9 months", type: "third-party" },
];

// Third-party scripts mapped to consent categories
const SCRIPT_REGISTRY: Record<ConsentCategory, Array<{ src: string; id: string }>> = {
  necessary: [],
  analytics: [
    { src: "https://www.googletagmanager.com/gtag/js?id=G-XXXXX", id: "gtag" },
    { src: "https://static.hotjar.com/c/hotjar-XXXXX.js", id: "hotjar" },
  ],
  marketing: [
    { src: "https://connect.facebook.net/en_US/fbevents.js", id: "fb-pixel" },
  ],
  personalization: [
    { src: "https://widget.intercom.io/widget/XXXXX", id: "intercom" },
  ],
};

// Client-side: consent banner logic
export function getConsentBannerConfig() {
  return {
    categories: [
      {
        id: "necessary",
        name: "Strictly Necessary",
        description: "Required for the website to function. Cannot be disabled.",
        required: true,
        cookies: COOKIE_REGISTRY.filter((c) => c.category === "necessary"),
      },
      {
        id: "analytics",
        name: "Analytics",
        description: "Help us understand how visitors use our website.",
        required: false,
        cookies: COOKIE_REGISTRY.filter((c) => c.category === "analytics"),
      },
      {
        id: "marketing",
        name: "Marketing",
        description: "Used to deliver relevant ads and track campaign performance.",
        required: false,
        cookies: COOKIE_REGISTRY.filter((c) => c.category === "marketing"),
      },
      {
        id: "personalization",
        name: "Personalization",
        description: "Enable personalized features like chat and recommendations.",
        required: false,
        cookies: COOKIE_REGISTRY.filter((c) => c.category === "personalization"),
      },
    ],
    consentVersion: "2.1",
  };
}

// Save consent preferences
export async function saveConsent(
  visitorId: string,
  preferences: Omit<ConsentPreferences, "timestamp" | "version">,
  metadata: { ip: string; userAgent: string }
): Promise<void> {
  const consent: ConsentPreferences = {
    ...preferences,
    necessary: true,
    timestamp: new Date().toISOString(),
    version: "2.1",
    ip: metadata.ip,
    userAgent: metadata.userAgent,
  };

  // Store consent record (GDPR requires proof of consent)
  await pool.query(
    `INSERT INTO consent_records (visitor_id, preferences, ip_address, user_agent, consent_version, created_at)
     VALUES ($1, $2, $3, $4, $5, NOW())`,
    [visitorId, JSON.stringify(consent), metadata.ip, metadata.userAgent, consent.version]
  );

  // Cache for quick lookup
  await redis.setex(`consent:${visitorId}`, 86400 * 365, JSON.stringify(consent));

  // Track consent analytics
  const categories = Object.entries(preferences)
    .filter(([k, v]) => k !== "necessary" && v === true)
    .map(([k]) => k);

  await redis.hincrby("consent:stats:total", "total", 1);
  await redis.hincrby("consent:stats:total", categories.length === 3 ? "accept_all" : categories.length === 0 ? "reject_all" : "partial", 1);
  for (const cat of categories) {
    await redis.hincrby("consent:stats:categories", cat, 1);
  }
}

// Get consent for a visitor
export async function getConsent(visitorId: string): Promise<ConsentPreferences | null> {
  const cached = await redis.get(`consent:${visitorId}`);
  if (cached) return JSON.parse(cached);

  const { rows: [record] } = await pool.query(
    "SELECT preferences FROM consent_records WHERE visitor_id = $1 ORDER BY created_at DESC LIMIT 1",
    [visitorId]
  );

  if (record) {
    const prefs = JSON.parse(record.preferences);
    await redis.setex(`consent:${visitorId}`, 86400 * 365, JSON.stringify(prefs));
    return prefs;
  }

  return null;
}

// Get allowed scripts based on consent
export async function getAllowedScripts(visitorId: string): Promise<Array<{ src: string; id: string }>> {
  const consent = await getConsent(visitorId);
  if (!consent) return SCRIPT_REGISTRY.necessary;

  const allowed = [...SCRIPT_REGISTRY.necessary];
  if (consent.analytics) allowed.push(...SCRIPT_REGISTRY.analytics);
  if (consent.marketing) allowed.push(...SCRIPT_REGISTRY.marketing);
  if (consent.personalization) allowed.push(...SCRIPT_REGISTRY.personalization);

  return allowed;
}

// Withdraw consent (GDPR right)
export async function withdrawConsent(visitorId: string, metadata: { ip: string; userAgent: string }): Promise<void> {
  await saveConsent(visitorId, {
    necessary: true, analytics: false, marketing: false, personalization: false,
  } as any, metadata);

  // Delete existing cookies for withdrawn categories
  // (client-side handles actual cookie deletion)
}

// Consent analytics
export async function getConsentStats(): Promise<{
  totalDecisions: number;
  acceptAll: number;
  rejectAll: number;
  partial: number;
  categoryAcceptRates: Record<string, number>;
}> {
  const totals = await redis.hgetall("consent:stats:total");
  const categories = await redis.hgetall("consent:stats:categories");
  const total = parseInt(totals.total || "0");

  return {
    totalDecisions: total,
    acceptAll: parseInt(totals.accept_all || "0"),
    rejectAll: parseInt(totals.reject_all || "0"),
    partial: parseInt(totals.partial || "0"),
    categoryAcceptRates: Object.fromEntries(
      Object.entries(categories).map(([k, v]) => [k, total > 0 ? Math.round((parseInt(v) / total) * 100) : 0])
    ),
  };
}

Results

  • GDPR compliance achieved — scripts blocked until explicit consent; consent records stored with timestamp, IP, and version; auditable proof for regulators
  • €20K fine risk eliminated — real script blocking (not just a banner) means non-consented cookies are never set; technically compliant, not just visually
  • Consent rate: 0% (broken banner) → 68% accept-all — clear categories with descriptions help users understand what they're accepting; most click "Accept All"
  • Analytics data loss minimized — 68% accept analytics vs 100% before, but the data is now legally usable; no regulatory risk
  • Preference center — users change consent anytime via footer link; withdrawal takes effect immediately; builds trust

Related use cases