Ir al contenido

@presage-kit/core

El paquete core proporciona el cliente adaptativo, el motor de reglas, el tracker, los drivers de persistencia y todos los tipos compartidos.

Ventana de terminal
pnpm add @presage-kit/core

Crea una instancia de AdaptiveClient, el centro de operaciones para reglas, rastreo y gestion del contexto.

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,
},
})
interface AdaptiveClientConfig {
rules?: AdaptationRule[]
tracker?: {
adapters?: TrackerAdapter[]
builtIn?: Partial<BuiltInTrackerConfig>
}
persistence?: {
driver: StorageDriver
}
maturity?: Partial<MaturityConfig>
}
CampoTipoPor defectoDescripcion
rulesAdaptationRule[][]Conjunto inicial de reglas de adaptacion
tracker.adaptersTrackerAdapter[][]Adaptadores de analitica externos
tracker.builtIn.maxEventsnumber1000Maximo de eventos en memoria
tracker.builtIn.maxAgeMsnumber2592000000 (30 dias)Edad maxima de eventos antes de la poda
persistence.driverStorageDrivernullDriver de almacenamiento para atributos y senales
maturityPartial<MaturityConfig>Ver valores por defectoSobreescrituras de umbrales de madurez
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 atom reactivo que contiene el estado actual del store. Suscribase para recibir notificaciones de cambios.

const unsubscribe = client.state.subscribe((state) => {
console.log('Context changed:', state.context)
})

Asocia la sesion actual con un usuario. Fusiona los atributos proporcionados con los atributos existentes.

client.identify('user-123', {
role: 'admin',
plan: 'enterprise',
signupDate: '2025-01-15',
company: 'Acme Corp',
companySize: 150,
})

Tambien reenvía la llamada identify a todos los adaptadores de tracker registrados.

Fusiona nuevos atributos con los atributos existentes sin requerir una llamada completa a identify().

client.updateTraits({ plan: 'enterprise', locale: 'fr' })

Registra un evento. Activa el recalculo de senales con debounce (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' })

Resuelve un adaptation point evaluando las reglas y devolviendo la variante seleccionada.

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'

Evalua las reglas para un adaptation point y devuelve la accion sin procesar (o null si ninguna regla coincide). Util para flujos de control imperativos.

const action = client.evaluateAction('sidebar-nav')
if (action?.type === 'reorder') {
// Reorder navigation items
}

Devuelve una instantanea del UserContext actual.

const ctx = client.getContext()
console.log(ctx.traits.role) // 'admin'
console.log(ctx.signals.sessionCount) // 12
console.log(ctx.maturity) // 'active'

Destruye el cliente. Cancela los temporizadores pendientes y se desuscribe de los cambios de estado.

client.destroy()
interface AdaptiveStoreState {
context: UserContext
isReady: boolean
}
interface UserContext {
traits: UserTraits
signals: BehavioralSignals
maturity: Maturity
}
interface UserTraits {
userId?: string
role?: string
plan?: string
signupDate?: string
company?: string
companySize?: number
locale?: string
[key: string]: unknown
}

Extensible con cualquier par clave-valor personalizado.

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>
}
type Maturity = 'new' | 'onboarding' | 'active' | 'power' | 'dormant'
interface MaturityConfig {
newMaxSessions: number // Default: 3
onboardingMaxSessions: number // Default: 10
powerMinFeatures: number // Default: 5
dormantDaysInactive: number // Default: 14
}
interface AdaptationRule {
id: string
adaptationId: string
priority: number
conditions: ConditionGroup
action: AdaptationAction
}
interface Condition {
field: string
operator: ConditionOperator
value: unknown
}
type ConditionOperator =
| 'eq' | 'neq'
| 'gt' | 'gte' | 'lt' | 'lte'
| 'in' | 'notIn'
| 'contains' | 'notContains'
| 'exists' | 'notExists'
| 'between'
| 'matches'
interface ConditionGroup {
all?: (Condition | ConditionGroup)[]
any?: (Condition | ConditionGroup)[]
not?: Condition | ConditionGroup
}
type AdaptationAction =
| { type: 'show'; variantId: string }
| { type: 'hide' }
| { type: 'reorder'; order: string[] }
| { type: 'modify'; props: Record<string, unknown> }
interface AdaptationPoint {
id: string
variants: readonly string[]
defaultVariant: string
strategy: Strategy
}
interface ResolvedAdaptation {
adaptationId: string
selectedVariant: string
strategy: Strategy['type'] // 'rules'
reason: string // e.g. 'Rule "my-rule" matched' or 'default'
resolvedAt: number // timestamp
}
type Strategy = { type: 'rules' }

Interfaz para conectar servicios de analitica externos al tracker.

interface TrackerAdapter {
name: string
track(event: TrackerEvent): void | Promise<void>
identify(userId: string, traits: UserTraits): void | Promise<void>
flush?(): Promise<void>
}
interface TrackerEvent {
name: string
properties?: Record<string, unknown>
timestamp: number
userId?: string
sessionId: string
}
interface BuiltInTrackerConfig {
maxEvents: number // Default: 1000
maxAgeMs: number // Default: 2592000000 (30 days)
}

Interfaz para drivers de persistencia personalizados.

interface StorageDriver {
get<T>(key: string): T | null
set<T>(key: string, value: T): void
remove(key: string): void
clear(): void
}

Crea un StorageDriver respaldado por localStorage. Las claves se almacenan como prefix:key.

import { createLocalStorageDriver } from '@presage-kit/core'
const driver = createLocalStorageDriver('my-app')
// Stores as 'my-app:traits', 'my-app:signals'

Devuelve null de forma segura en get() cuando localStorage no esta disponible (por ejemplo, SSR).

Crea un StorageDriver en memoria. Los datos se pierden cuando el proceso termina.

import { createMemoryDriver } from '@presage-kit/core'
const driver = createMemoryDriver()

El motor de reglas puede usarse directamente para testing o casos de uso avanzados.

import { RulesEngine } from '@presage-kit/core'
const engine = new RulesEngine()

Agrega una regla individual. Reordena las reglas por prioridad.

engine.addRule({
id: 'my-rule',
adaptationId: 'onboarding',
priority: 10,
conditions: { all: [{ field: 'maturity', operator: 'eq', value: 'new' }] },
action: { type: 'show', variantId: 'guided-tour' },
})

Agrega multiples reglas a la vez. Reordena una sola vez.

engine.addRules([rule1, rule2, rule3])

Elimina una regla por su ID.

engine.removeRule('my-rule')

Evalua todas las reglas para el ID de adaptacion dado contra un UserContext. Devuelve la primera accion que coincida, o null.

const action = engine.evaluate('onboarding', {
traits: { role: 'admin' },
signals: { sessionCount: 2, totalEvents: 10, /* ... */ },
maturity: 'new',
})

Devuelve todas las reglas registradas como un array de solo lectura.

const rules = engine.getRules() // readonly AdaptationRule[]

Una primitiva reactiva minima para casos de uso avanzados. Sigue el contrato de store de Svelte.

import { atom } from '@presage-kit/core'
const count = atom(0)
// Read
console.log(count.get()) // 0
// Write
count.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}`)
})
// Cleanup
unsub()
unlisten()
interface ReadableAtom<T> {
get(): T
subscribe(listener: Listener<T>): Unsubscribe
listen(listener: Listener<T>): Unsubscribe
}

Extiende ReadableAtom con un metodo set().

interface WritableAtom<T> extends ReadableAtom<T> {
set(value: T): void
}
type Listener<T> = (value: T, oldValue: T) => void
type Unsubscribe = () => void