Files
gw-svelte/src/hooks.server.ts
T
admin 171b193498 Add hero CTA A/B test (hero_cta: control vs free_emphasis)
Sticky 50/50 variant assignment via gw_ab_hero cookie, server-rendered
so no flicker. Tracks exposures, CTA clicks, and booking conversions
to ab_events (table self-creates on first POST). Bot UAs are dropped;
exposures/clicks dedupe per session.

- ?ab=control / ?ab=free_emphasis forces and persists a variant
- /owner/experiments shows per-variant CVR and relative lift
- AB only runs on the marketing surface

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 08:14:18 +12:00

38 lines
1.4 KiB
TypeScript

import type { Handle } from '@sveltejs/kit';
import { resolveSurface } from '$lib/server/surface';
import { resolveAnonId, resolveHeroVariant } from '$lib/server/ab';
const ADMIN_PATH = '/owner/welcome';
export const handle: Handle = async ({ event, resolve }) => {
const { surface } = resolveSurface(event.url, event.cookies);
const path = event.url.pathname;
// Sticky A/B assignment, marketing surface only — no point polluting the
// owner/clients hosts with marketing-experiment cookies, and it skews
// exposure counts when staff hit the public site from the dashboard.
if (surface === 'marketing') {
event.locals.anonId = resolveAnonId(event.cookies);
event.locals.abHero = resolveHeroVariant(event.url, event.cookies);
}
// The admin host (cp.*) serves the dashboard at its root.
if (surface === 'cp' && (path === '/' || path === '')) {
return new Response(null, {
status: 302,
headers: { location: ADMIN_PATH },
});
}
// Block the admin dashboard from the public marketing site so /owner/*
// only renders on the cp surface (or on the clients surface during the
// legacy onboarding-host transition window). Localhost dev preview is
// allowed: resolveSurface returns 'cp' there too when ?preview=cp or
// ?preview=admin is set.
if (surface === 'marketing' && path.startsWith('/owner/')) {
return new Response('Not Found', { status: 404 });
}
return resolve(event);
};