Conversion-Tracking mit Einwilligung: GA4, Meta Pixel + Conversions API, TikTok Pixel + Events API, dedupliziert
Installieren
genpm add @core/pixelsWas du bekommst
- Quellcode in src/lib/pixels/, 7 Dateien. (21,2 kB)
- KI-Regeln in src/lib/pixels/AGENTS.md, dazu Regeldateien für die IDE.
- Umgebungsvariablen in .env.example ergänzt: GA4_MEASUREMENT_ID, GA4_API_SECRET, META_PIXEL_ID, META_CAPI_TOKEN, TIKTOK_PIXEL_ID, TIKTOK_EVENTS_TOKEN.
- Löst @core/antispam, @core/consent, @core/contracts, @core/db, @core/money für dich auf.
README
Dieses Paket hat keine README.
Genau das liest deine KI, wenn sie in src/lib/pixels arbeitet. Sonst wird ihrem Kontext nichts hinzugefügt.
@core/pixels — rules for AI agents
Purpose
One place to measure business events (view_item, add_to_cart, begin_checkout, purchase, lead from
@core/contracts) in GA4, Meta and TikTok: browser pixels loaded only inside ConsentGate (@core/consent) and
server-side events (GA4 Measurement Protocol, Meta Conversions API, TikTok Events API) sent only with consent, with
the same eventId on both sides so platforms deduplicate. Emails/phones are SHA-256 hashed. Never throws on vendor errors.
Map
index.ts— server:trackServer,attributionFromRequest,saveAttribution,takeAttribution,browserCalls,registerPixelPlatform.client.ts— client:PixelScripts,track,newEventId.browser.ts—browserCalls(no server code; safe in the browser).schema.ts—pixel_attributions(consent + first-party ids kept until the payment webhook, 7 days).
Integration
- Env (all optional; a platform is active only with both of its vars):
GA4_MEASUREMENT_ID+GA4_API_SECRET,META_PIXEL_ID+META_CAPI_TOKEN(optionalMETA_GRAPH_VERSION,META_TEST_EVENT_CODE),TIKTOK_PIXEL_ID+TIKTOK_EVENTS_TOKEN. Check each platform's current API version and event names before going live. - Layout:
<ConsentGate category="analytics"><PixelScripts ga4MeasurementId=… /></ConsentGate>and<ConsentGate category="marketing"><PixelScripts metaPixelId=… tiktokPixelId=… /></ConsentGate>.PixelScriptsrenders nothing: once mounted (i.e. with consent) it injects each platform's loader into<head>once per page, so re-mounts don't repeatconfig/PageView. A CSP must allow those script and connect origins. - Browser events:
const eventId = newEventId(); track({ type: 'add_to_cart', eventId, … })and send the sameeventIdto the server if it also tracks it. - Purchases: after
startCheckoutreturns{ sessionId },saveAttribution("checkout:" + sessionId, attributionFromRequest(req)); when the order is paid (@core/ordersonOrderEvent('paid')),trackServer(purchaseEvent(order), await takeAttribution("checkout:" + order.cartId))(@core/checkout stores the Stripe session id inorder.cartId;purchaseEventcomes from @core/checkout). - Verify with each platform's test tools (GA4 DebugView, Meta Test Events, TikTok Test Events).
Conventions
- List in the privacy policy what each platform receives (events, hashed email, IP, user agent, click ids).
- Use real values only;
purchaseuses the order total and the order id.
Don't
- Don't load pixels or call
trackServerwithout the matching consent category. - Don't send raw emails, phones, names or addresses to ad platforms.
- Don't invent events (fake purchases to "train" campaigns) or fire
purchasefrom the success page without an order.
# @core/pixels — rules for AI agents
## Purpose
One place to measure business events (`view_item`, `add_to_cart`, `begin_checkout`, `purchase`, `lead` from
@core/contracts) in GA4, Meta and TikTok: browser pixels loaded only inside `ConsentGate` (@core/consent) and
server-side events (GA4 Measurement Protocol, Meta Conversions API, TikTok Events API) sent only with consent, with
the same `eventId` on both sides so platforms deduplicate. Emails/phones are SHA-256 hashed. Never throws on vendor errors.
## Map
- `index.ts` — server: `trackServer`, `attributionFromRequest`, `saveAttribution`, `takeAttribution`, `browserCalls`, `registerPixelPlatform`.
- `client.ts` — client: `PixelScripts`, `track`, `newEventId`. `browser.ts` — `browserCalls` (no server code; safe in the browser).
- `schema.ts` — `pixel_attributions` (consent + first-party ids kept until the payment webhook, 7 days).
## Integration
1. Env (all optional; a platform is active only with both of its vars): `GA4_MEASUREMENT_ID` + `GA4_API_SECRET`,
`META_PIXEL_ID` + `META_CAPI_TOKEN` (optional `META_GRAPH_VERSION`, `META_TEST_EVENT_CODE`), `TIKTOK_PIXEL_ID` + `TIKTOK_EVENTS_TOKEN`.
Check each platform's current API version and event names before going live.
2. Layout: `<ConsentGate category="analytics"><PixelScripts ga4MeasurementId=… /></ConsentGate>` and
`<ConsentGate category="marketing"><PixelScripts metaPixelId=… tiktokPixelId=… /></ConsentGate>`.
`PixelScripts` renders nothing: once mounted (i.e. with consent) it injects each platform's loader into `<head>` once
per page, so re-mounts don't repeat `config`/`PageView`. A CSP must allow those script and connect origins.
3. Browser events: `const eventId = newEventId(); track({ type: 'add_to_cart', eventId, … })` and send the same `eventId` to the server if it also tracks it.
4. Purchases: after `startCheckout` returns `{ sessionId }`, `saveAttribution("checkout:" + sessionId, attributionFromRequest(req))`;
when the order is paid (@core/orders `onOrderEvent('paid')`), `trackServer(purchaseEvent(order), await takeAttribution("checkout:" + order.cartId))`
(@core/checkout stores the Stripe session id in `order.cartId`; `purchaseEvent` comes from @core/checkout).
5. Verify with each platform's test tools (GA4 DebugView, Meta Test Events, TikTok Test Events).
## Conventions
- List in the privacy policy what each platform receives (events, hashed email, IP, user agent, click ids).
- Use real values only; `purchase` uses the order total and the order id.
## Don't
- Don't load pixels or call `trackServer` without the matching consent category.
- Don't send raw emails, phones, names or addresses to ad platforms.
- Don't invent events (fake purchases to "train" campaigns) or fire `purchase` from the success page without an order.
Der genaue Baum, der nach .genpmignore eingebunden wird. Gepinnt an
'use client';
// Cliente: carga los píxeles (solo dentro de <ConsentGate>) y envía eventos con el mismo eventId que el servidor.
import { type ReactNode, useEffect } from 'react';
import type { TrackedEvent } from '../contracts/index.ts';
import { browserCalls } from './browser.ts';
type W = Window & { gtag?: (...a: unknown[]) => void; fbq?: (...a: unknown[]) => void; ttq?: { track: (...a: unknown[]) => void } };
/**
* Envía el evento a las librerías cargadas en la página (las que no estén, se ignoran). Se aplaza a la siguiente tarea:
* así un `track()` en el efecto de una página llega después de que `<PixelScripts>` (más abajo en el árbol) prepare las colas.
*/
export function track(e: TrackedEvent): void {
setTimeout(() => {
const w = window as W;
const calls = browserCalls(e);
w.gtag?.(...calls.gtag);
w.fbq?.(...calls.fbq);
w.ttq?.track(...calls.ttq);
}, 0);
}
/** Id de evento nuevo para compartir entre navegador y servidor. */
export const newEventId = () => `evt_${crypto.randomUUID().replaceAll('-', '')}`;
const validId = (s?: string) => (s && /^[A-Za-z0-9-]{4,40}$/.test(s) ? s : undefined);
/** Cada cargador se instala una sola vez por página (sobrevive a desmontajes, navegaciones y recargas del módulo). */
function once(key: string): boolean {
const w = window as W & { __genpmPixels?: Set<string> };
w.__genpmPixels ??= new Set();
if (w.__genpmPixels.has(key)) return false;
w.__genpmPixels.add(key);
return true;
}
function loadScript(src: string): void {
const s = document.createElement('script');
s.async = true;
s.src = src;
document.head.appendChild(s);
}
// Colas oficiales de cada plataforma (equivalentes a sus fragmentos, sin `<script>` en línea): las llamadas se guardan
// hasta que llega la librería, que las procesa al cargar.
type Stub = ((...a: unknown[]) => void) & Record<string, unknown>;
function installGa4(id: string): void {
const w = window as unknown as { dataLayer?: unknown[]; gtag?: (...a: unknown[]) => void };
w.dataLayer ??= [];
w.gtag ??= function gtag() {
// gtag.js espera el objeto `arguments`, no un array.
// eslint-disable-next-line prefer-rest-params -- la cola de gtag.js exige el objeto `arguments`
w.dataLayer!.push(arguments);
};
w.gtag('js', new Date());
w.gtag('config', id); // envía el page_view una vez; las navegaciones las mide GA4 (historial del navegador).
loadScript(`https://www.googletagmanager.com/gtag/js?id=${id}`);
}
function installMeta(id: string): void {
const w = window as unknown as { fbq?: Stub; _fbq?: Stub };
if (!w.fbq) {
const n = function fbq() {
// fbevents.js procesa la cola de objetos `arguments`.
// eslint-disable-next-line prefer-rest-params -- la cola de fbevents.js exige el objeto `arguments`
n.callMethod ? (n.callMethod as (...a: unknown[]) => void).apply(n, arguments as unknown as unknown[]) : (n.queue as unknown[]).push(arguments);
} as Stub;
Object.assign(n, { push: n, loaded: true, version: '2.0', queue: [] });
w.fbq = n;
w._fbq ??= n;
loadScript('https://connect.facebook.net/en_US/fbevents.js');
}
w.fbq('init', id);
w.fbq('track', 'PageView');
}
function installTiktok(id: string): void {
const w = window as unknown as { TiktokAnalyticsObject?: string; ttq?: unknown[] & Record<string, unknown> };
w.TiktokAnalyticsObject = 'ttq';
const ttq = (w.ttq ??= [] as unknown as unknown[] & Record<string, unknown>);
if (typeof ttq.load !== 'function') {
const methods = ['page', 'track', 'identify', 'instances', 'debug', 'on', 'off', 'once', 'ready', 'alias', 'group', 'enableCookie', 'disableCookie'];
const setAndDefer = (t: unknown[] & Record<string, unknown>, m: string) => {
t[m] = (...a: unknown[]) => void t.push([m, ...a]);
};
Object.assign(ttq, { methods, setAndDefer });
for (const m of methods) setAndDefer(ttq, m);
ttq.load = (e: string) => {
ttq._i ??= {};
(ttq._i as Record<string, unknown>)[e] = [];
ttq._t ??= {};
(ttq._t as Record<string, number>)[e] = Date.now();
loadScript(`https://analytics.tiktok.com/i18n/pixel/events.js?sdkid=${e}&lib=ttq`);
};
}
(ttq.load as (e: string) => void)(id);
(ttq.page as () => void)();
}
/**
* Cargadores oficiales de cada plataforma con sus ids (públicos). Ponlo SIEMPRE dentro de `<ConsentGate>`:
* `<ConsentGate category="marketing"><PixelScripts metaPixelId=… tiktokPixelId=… /></ConsentGate>` y GA4 dentro de "analytics".
* Inyecta los scripts al montarse (React no ejecuta `<script>` creados en el cliente) y una sola vez por página:
* volver a montarlo (p. ej. al cambiar el consentimiento o de ruta) no repite `config` ni el PageView.
*/
export function PixelScripts(props: { ga4MeasurementId?: string; metaPixelId?: string; tiktokPixelId?: string }): ReactNode {
const ga = validId(props.ga4MeasurementId);
const metaId = validId(props.metaPixelId);
const tt = validId(props.tiktokPixelId);
useEffect(() => {
if (ga && once(`ga4:${ga}`)) installGa4(ga);
if (metaId && once(`meta:${metaId}`)) installMeta(metaId);
if (tt && once(`tiktok:${tt}`)) installTiktok(tt);
}, [ga, metaId, tt]);
return null;
}
Dieses Paket deklariert keine MCP-Server.
| Version | Commit | Veröffentlicht | Prüfung |
|---|---|---|---|
| 1.0.1 | abb4ca2 | vor 3 Stunden | Prüfung bestanden |
- genpm
- @core/antispam ^1.0.0@core/consent ^1.0.0@core/contracts ^1.0.0@core/db ^1.0.0@core/money ^1.0.0
- vorgeschlagen
- GenPM schlägt den npm-Befehl vor und führt ihn nur aus, wenn du zustimmst.
- Verwendet von (1)
- @core/kit-landing ^1.0.0
- Prüfung
- Prüfung bestanden · 0 Befunde
- Commit
- v1.0.1 → abb4ca25745fd3d140e351fc27ecbcccb9f334f4 · nach dem Abruf verifiziert
- Skripte
- Keine. GenPM führt niemals Paketcode aus.
- Lizenz
- MIT
- Qualität
- 100/100
- Anerkannte Lizenzerfüllt
- AGENTS.md erklärt den Zweckerfüllt
- AGENTS.md enthält Integrationsschritteerfüllt
- AGENTS.md nennt Konventionen oder Verboteerfüllt
- Enthält Testserfüllt
- Sicherheitsscan bestandenerfüllt
- In den letzten 6 Monaten veröffentlichterfüllt
- Verifizierter Herausgebererfüllt
- Zusammenfassung und Schlagwörtererfüllt
- Meldung
- Stimmt etwas nicht?