@presage-kit/core
Le package core fournit le client adaptatif, le moteur de règles, le tracker, les drivers de persistance et tous les types partagés.
pnpm add @presage-kit/corecreateAdaptiveClient(config)
Section intitulée « createAdaptiveClient(config) »Crée une instance AdaptiveClient — le hub central pour les règles, le suivi et la gestion du contexte.
import { createAdaptiveClient, createLocalStorageDriver } from '@presage-kit/core'
const client = createAdaptiveClient({ rules: [/* ... */], tracker: { adapters: [/* ... */], builtIn: { maxEvents: 500 }, }, persistence: { driver: createLocalStorageDriver('my-app'), }, maturity: { newMaxSessions: 5, dormantDaysInactive: 21, },})AdaptiveClientConfig
Section intitulée « AdaptiveClientConfig »interface AdaptiveClientConfig { rules?: AdaptationRule[] tracker?: { adapters?: TrackerAdapter[] builtIn?: Partial<BuiltInTrackerConfig> } persistence?: { driver: StorageDriver } maturity?: Partial<MaturityConfig>}| Champ | Type | Défaut | Description |
|---|---|---|---|
rules | AdaptationRule[] | [] | Ensemble initial de règles d’adaptation |
tracker.adapters | TrackerAdapter[] | [] | Adaptateurs d’analytique externes |
tracker.builtIn.maxEvents | number | 1000 | Nombre maximum d’événements en mémoire |
tracker.builtIn.maxAgeMs | number | 2592000000 (30 jours) | Age maximum des événements avant suppression |
persistence.driver | StorageDriver | null | Driver de stockage pour les traits et signaux |
maturity | Partial<MaturityConfig> | Voir les défauts | Surcharges des seuils de maturité |
AdaptiveClient
Section intitulée « AdaptiveClient »interface AdaptiveClient { state: ReadableAtom<AdaptiveStoreState> identify(userId: string, traits?: UserTraits): void updateTraits(traits: Partial<UserTraits>): void track(event: string, properties?: Record<string, unknown>): void resolve(point: AdaptationPoint): ResolvedAdaptation evaluateAction(adaptationId: string): AdaptationAction | null getContext(): UserContext destroy(): void}Un atome réactif contenant l’état courant du store. Abonnez-vous pour recevoir les notifications de changement.
const unsubscribe = client.state.subscribe((state) => { console.log('Context changed:', state.context)})identify(userId, traits?)
Section intitulée « identify(userId, traits?) »Associe la session courante à un utilisateur. Fusionne les traits fournis avec les traits existants.
client.identify('user-123', { role: 'admin', plan: 'enterprise', signupDate: '2025-01-15', company: 'Acme Corp', companySize: 150,})Transmet également l’appel identify à tous les adaptateurs de tracker enregistrés.
updateTraits(traits)
Section intitulée « updateTraits(traits) »Fusionne de nouveaux traits dans les traits existants sans nécessiter un appel complet à identify().
client.updateTraits({ plan: 'enterprise', locale: 'fr' })track(event, properties?)
Section intitulée « track(event, properties?) »Enregistre un événement. Déclenche un recalcul différé des signaux (100ms).
client.track('feature_used', { featureId: 'export' })client.track('click', { elementId: 'sidebar-settings' })client.track('custom_signal', { signalId: 'engagement', value: 3 })client.track('page_view', { path: '/dashboard' })resolve(point)
Section intitulée « resolve(point) »Résout un point d’adaptation en évaluant les règles et en retournant la variante sélectionnée.
const result = client.resolve({ id: 'onboarding', variants: ['guided-tour', 'standard', 'minimal'], defaultVariant: 'standard', strategy: { type: 'rules' },})
console.log(result.selectedVariant) // 'guided-tour'console.log(result.reason) // 'Rule "new-user-tour" matched'evaluateAction(adaptationId)
Section intitulée « evaluateAction(adaptationId) »Evalue les règles pour un point d’adaptation et retourne l’action brute (ou null si aucune règle ne correspond). Utile pour le contrôle de flux impératif.
const action = client.evaluateAction('sidebar-nav')
if (action?.type === 'reorder') { // Reorder navigation items}getContext()
Section intitulée « getContext() »Retourne un instantané du UserContext courant.
const ctx = client.getContext()console.log(ctx.traits.role) // 'admin'console.log(ctx.signals.sessionCount) // 12console.log(ctx.maturity) // 'active'destroy()
Section intitulée « destroy() »Détruit le client. Annule les timers en attente et se désabonne des changements d’état.
client.destroy()AdaptiveStoreState
Section intitulée « AdaptiveStoreState »interface AdaptiveStoreState { context: UserContext isReady: boolean}UserContext
Section intitulée « UserContext »interface UserContext { traits: UserTraits signals: BehavioralSignals maturity: Maturity}UserTraits
Section intitulée « UserTraits »interface UserTraits { userId?: string role?: string plan?: string signupDate?: string company?: string companySize?: number locale?: string [key: string]: unknown}Extensible avec n’importe quelle paire clé-valeur personnalisée.
BehavioralSignals
Section intitulée « BehavioralSignals »interface BehavioralSignals { sessionCount: number totalEvents: number featureUsage: Record<string, number> lastSeenAt: string // ISO 8601 timestamp firstSeenAt: string // ISO 8601 timestamp currentSessionDuration: number daysSinceSignup: number clickMap: Record<string, number> customSignals: Record<string, number>}Maturity
Section intitulée « Maturity »type Maturity = 'new' | 'onboarding' | 'active' | 'power' | 'dormant'MaturityConfig
Section intitulée « MaturityConfig »interface MaturityConfig { newMaxSessions: number // Default: 3 onboardingMaxSessions: number // Default: 10 powerMinFeatures: number // Default: 5 dormantDaysInactive: number // Default: 14}AdaptationRule
Section intitulée « AdaptationRule »interface AdaptationRule { id: string adaptationId: string priority: number conditions: ConditionGroup action: AdaptationAction}Condition
Section intitulée « Condition »interface Condition { field: string operator: ConditionOperator value: unknown}ConditionOperator
Section intitulée « ConditionOperator »type ConditionOperator = | 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'notIn' | 'contains' | 'notContains' | 'exists' | 'notExists' | 'between' | 'matches'ConditionGroup
Section intitulée « ConditionGroup »interface ConditionGroup { all?: (Condition | ConditionGroup)[] any?: (Condition | ConditionGroup)[] not?: Condition | ConditionGroup}AdaptationAction
Section intitulée « AdaptationAction »type AdaptationAction = | { type: 'show'; variantId: string } | { type: 'hide' } | { type: 'reorder'; order: string[] } | { type: 'modify'; props: Record<string, unknown> }AdaptationPoint
Section intitulée « AdaptationPoint »interface AdaptationPoint { id: string variants: readonly string[] defaultVariant: string strategy: Strategy}ResolvedAdaptation
Section intitulée « ResolvedAdaptation »interface ResolvedAdaptation { adaptationId: string selectedVariant: string strategy: Strategy['type'] // 'rules' reason: string // e.g. 'Rule "my-rule" matched' or 'default' resolvedAt: number // timestamp}Strategy
Section intitulée « Strategy »type Strategy = { type: 'rules' }TrackerAdapter
Section intitulée « TrackerAdapter »Interface pour brancher des services d’analytique externes sur le tracker.
interface TrackerAdapter { name: string track(event: TrackerEvent): void | Promise<void> identify(userId: string, traits: UserTraits): void | Promise<void> flush?(): Promise<void>}TrackerEvent
Section intitulée « TrackerEvent »interface TrackerEvent { name: string properties?: Record<string, unknown> timestamp: number userId?: string sessionId: string}BuiltInTrackerConfig
Section intitulée « BuiltInTrackerConfig »interface BuiltInTrackerConfig { maxEvents: number // Default: 1000 maxAgeMs: number // Default: 2592000000 (30 days)}StorageDriver
Section intitulée « StorageDriver »Interface pour les drivers de persistance personnalisés.
interface StorageDriver { get<T>(key: string): T | null set<T>(key: string, value: T): void remove(key: string): void clear(): void}createLocalStorageDriver(prefix)
Section intitulée « createLocalStorageDriver(prefix) »Crée un StorageDriver basé sur localStorage. Les clés sont stockées sous la forme prefix:key.
import { createLocalStorageDriver } from '@presage-kit/core'
const driver = createLocalStorageDriver('my-app')// Stores as 'my-app:traits', 'my-app:signals'Retourne gracieusement null sur get() quand localStorage n’est pas disponible (ex. SSR).
createMemoryDriver()
Section intitulée « createMemoryDriver() »Crée un StorageDriver en mémoire. Les données sont perdues quand le processus se termine.
import { createMemoryDriver } from '@presage-kit/core'
const driver = createMemoryDriver()RulesEngine
Section intitulée « RulesEngine »Le moteur de règles peut être utilisé directement pour les tests ou les cas d’utilisation avancés.
import { RulesEngine } from '@presage-kit/core'
const engine = new RulesEngine()addRule(rule)
Section intitulée « addRule(rule) »Ajoute une seule règle. Re-trie les règles par priorité.
engine.addRule({ id: 'my-rule', adaptationId: 'onboarding', priority: 10, conditions: { all: [{ field: 'maturity', operator: 'eq', value: 'new' }] }, action: { type: 'show', variantId: 'guided-tour' },})addRules(rules)
Section intitulée « addRules(rules) »Ajoute plusieurs règles en une fois. Re-trie une seule fois.
engine.addRules([rule1, rule2, rule3])removeRule(ruleId)
Section intitulée « removeRule(ruleId) »Supprime une règle par son ID.
engine.removeRule('my-rule')evaluate(adaptationId, context)
Section intitulée « evaluate(adaptationId, context) »Evalue toutes les règles pour l’ID d’adaptation donné par rapport à un UserContext. Retourne la première action correspondante, ou null.
const action = engine.evaluate('onboarding', { traits: { role: 'admin' }, signals: { sessionCount: 2, totalEvents: 10, /* ... */ }, maturity: 'new',})getRules()
Section intitulée « getRules() »Retourne toutes les règles enregistrées sous forme de tableau en lecture seule.
const rules = engine.getRules() // readonly AdaptationRule[]atom(initialValue)
Section intitulée « atom(initialValue) »Une primitive réactive minimaliste pour les cas d’utilisation avancés. Suit le contrat du store Svelte.
import { atom } from '@presage-kit/core'
const count = atom(0)
// Readconsole.log(count.get()) // 0
// Writecount.set(1)
// Subscribe (called immediately with current value)const unsub = count.subscribe((value, oldValue) => { console.log(`Changed from ${oldValue} to ${value}`)})
// Listen (NOT called immediately)const unlisten = count.listen((value, oldValue) => { console.log(`Changed from ${oldValue} to ${value}`)})
// Cleanupunsub()unlisten()ReadableAtom<T>
Section intitulée « ReadableAtom<T> »interface ReadableAtom<T> { get(): T subscribe(listener: Listener<T>): Unsubscribe listen(listener: Listener<T>): Unsubscribe}WritableAtom<T>
Section intitulée « WritableAtom<T> »Etend ReadableAtom avec une méthode set().
interface WritableAtom<T> extends ReadableAtom<T> { set(value: T): void}Listener<T>
Section intitulée « Listener<T> »type Listener<T> = (value: T, oldValue: T) => voidUnsubscribe
Section intitulée « Unsubscribe »type Unsubscribe = () => void