Aller au contenu

Suivi et signaux

Le suivi est la manière dont Presage apprend le comportement des utilisateurs. Chaque appel à client.track() alimente le pipeline de calcul des signaux, qui met à jour le UserContext et peut déclencher une réévaluation des règles.

Presage est livré avec un store d’événements intégré qui conserve les événements en mémoire. Aucun service externe n’est requis.

const client = createAdaptiveClient({
tracker: {
builtIn: {
maxEvents: 1000, // Keep at most 1000 events (default)
maxAgeMs: 30 * 24 * 60 * 60 * 1000, // Prune events older than 30 days (default)
},
},
})

Le tracker intégré automatiquement :

  • Attribue un sessionId (persisté dans sessionStorage)
  • Horodate chaque événement
  • Supprime les anciens événements pour rester dans les limites mémoire

Chaque événement suivi suit cette structure :

interface TrackerEvent {
name: string
properties?: Record<string, unknown>
timestamp: number // Auto-set to Date.now()
userId?: string // Set after client.identify()
sessionId: string // Auto-generated per browser session
}
// Feature usage — increments signals.featureUsage['dashboard-export']
client.track('feature_used', { featureId: 'dashboard-export' })
// Click tracking — increments signals.clickMap['sidebar-analytics']
client.track('click', { elementId: 'sidebar-analytics' })
// Custom signals — increments signals.customSignals['engagement'] by 5
client.track('custom_signal', { signalId: 'engagement', value: 5 })
// Generic event — counted in signals.totalEvents
client.track('page_view', { path: '/dashboard' })

Les signaux sont recalculés automatiquement après chaque appel track() (avec un debounce de 100ms pour éviter les problèmes de performance). Voici comment les événements correspondent aux signaux :

Nom de l’événementPropriétésSignal mis à jour
feature_used{ featureId: string }signals.featureUsage[featureId] += 1
click{ elementId: string }signals.clickMap[elementId] += 1
custom_signal{ signalId: string, value?: number }signals.customSignals[signalId] += value
Tout événementsignals.totalEvents += 1

De plus, ces signaux sont calculés à partir de l’historique complet des événements :

SignalCalcul
sessionCountNombre de valeurs sessionId uniques
firstSeenAtHorodatage du premier événement
lastSeenAtHorodatage du dernier événement
daysSinceSignupJours entre traits.signupDate et maintenant

Vous pouvez transférer les événements vers des services d’analytique externes en implémentant l’interface TrackerAdapter :

interface TrackerAdapter {
name: string
track(event: TrackerEvent): void | Promise<void>
identify(userId: string, traits: UserTraits): void | Promise<void>
flush?(): Promise<void>
}
import posthog from 'posthog-js'
import type { TrackerAdapter, TrackerEvent, UserTraits } from '@presage-kit/core'
const posthogAdapter: TrackerAdapter = {
name: 'posthog',
track(event: TrackerEvent) {
posthog.capture(event.name, event.properties)
},
identify(userId: string, traits: UserTraits) {
posthog.identify(userId, traits)
},
async flush() {
// PostHog batches automatically, but you can force a flush here
},
}
import type { TrackerAdapter, TrackerEvent, UserTraits } from '@presage-kit/core'
const segmentAdapter: TrackerAdapter = {
name: 'segment',
track(event: TrackerEvent) {
window.analytics.track(event.name, {
...event.properties,
sessionId: event.sessionId,
})
},
identify(userId: string, traits: UserTraits) {
window.analytics.identify(userId, traits)
},
}

Passez les adaptateurs dans la configuration du client :

const client = createAdaptiveClient({
tracker: {
adapters: [posthogAdapter, segmentAdapter],
},
// ...rules, persistence
})

Tous les adaptateurs reçoivent chaque appel track() et identify() en parallèle du store intégré.

Le recalcul des signaux est déboncé à 100ms. Si vous déclenchez plusieurs appels track() en succession rapide (ex. lors d’une rafale de clics), les signaux ne sont recalculés qu’une seule fois après la stabilisation de la rafale.

Le store d’événements intégré applique deux limites :

  • maxEvents (défaut : 1000) — Quand dépassé, les événements les plus anciens sont supprimés
  • maxAgeMs (défaut : 30 jours) — Les événements plus anciens sont écartés

Ces valeurs par défaut conviennent à la plupart des applications SaaS. Ajustez-les si votre cas d’utilisation génère de très gros volumes d’événements.

Une fois les signaux calculés, utilisez-les dans les conditions de règles exactement comme les traits :

{
id: 'frequent-user-shortcut',
adaptationId: 'toolbar',
priority: 10,
conditions: {
all: [
{ field: 'signals.sessionCount', operator: 'gte', value: 20 },
{ field: 'signals.featureUsage.export', operator: 'gte', value: 5 },
],
},
action: { type: 'show', variantId: 'with-keyboard-shortcuts' },
}