Skip to content

Conversation

@prosdev
Copy link
Contributor

@prosdev prosdev commented Dec 27, 2025

Phase 1: Display Condition Plugins

Implements 4 industry-leading display condition plugins with event-driven architecture, comprehensive testing, and complete documentation.

Overview

This PR implements the complete Phase 1 display condition plugins system with modern, composable, event-driven architecture.

What's Included

🎯 Display Condition Plugins (4 new plugins)

  1. Exit Intent Plugin - Closes [Phase 1] Scroll Depth Plugin #34

    • Velocity-based mouse tracking (industry standard)
    • Configurable sensitivity, min time on page
    • Session-aware (one trigger per session)
    • Mobile detection & disabling
    • 21 comprehensive tests
  2. Scroll Depth Plugin - Closes [Phase 1] Scroll Depth Plugin #34

    • Multiple threshold tracking (25%, 50%, 75%, 90%)
    • Throttled scroll listeners (100ms default)
    • Advanced metrics: velocity, direction, engagement score
    • Device-aware (mobile/tablet/desktop)
    • Time-to-threshold tracking
    • 25 comprehensive tests
  3. Page Visits Plugin - Closes [Phase 1] Page Visits Plugin #35

    • Session vs lifetime counters (sessionStorage + localStorage)
    • First-visit detection
    • Full operator support (>=, <, ===)
    • Timestamps (first/last visit)
    • Privacy-aware (DNT support, enable/disable API)
    • Cross-tab safe (sdk-kit storage)
    • 36 comprehensive tests
  4. Time Delay Plugin - Closes [Phase 1] Time Delay Plugin #36

    • Millisecond-precision delays
    • Page Visibility API integration (pause when hidden)
    • Active vs total elapsed time tracking
    • Auto-hide support
    • Reset capability
    • 19 comprehensive tests

🏗️ Core Infrastructure

  • Event-Driven Architecture: Plugins emit trigger:* events, core listens & updates context
  • Composable: AND/OR/NOT logic for combining display conditions
  • Type-Safe: Full TypeScript support with TriggerState interface
  • Pure Functions: Extracted testable logic from all plugins

✅ Integration & Testing - Closes #37

  • 314 total tests passing (up from 302)
  • 12 new integration tests covering plugin composition, performance, cleanup
  • Real-world scenarios: engaged users, returning visitors, first-time welcomes
  • Memory leak detection, performance benchmarks

📚 Documentation - Closes #32

  • Complete API reference for all 4 plugins
  • Configuration examples with defaults
  • Event payloads and usage examples
  • Combining display conditions (AND/OR/NOT patterns)
  • Restructured docs: Split into individual plugin pages for better navigation

🔧 Developer Experience

  • Biome config updates: Allow any in plugin implementation files for flexibility
  • Barrel exports: Clean imports for all plugins
  • Pure functions: Easier testing and maintenance
  • Type augmentation: Support for dynamic plugin APIs

Key Features

Composability

Plugins work independently or together using custom targeting logic:

// AND: All conditions must be met
targeting: {
  custom: (ctx) => {
    const scrolled = (ctx.triggers?.scrollDepth?.maxPercent || 0) >= 75;
    const timeSpent = ctx.triggers?.timeDelay?.triggered === true;
    const returning = (ctx.triggers?.pageVisits?.totalVisits || 0) >= 3;
    return scrolled && timeSpent && returning;
  }
}

// OR: Any condition triggers
targeting: {
  custom: (ctx) => {
    return ctx.triggers?.exitIntent?.triggered ||
           (ctx.triggers?.scrollDepth?.maxPercent || 0) >= 75 ||
           ctx.triggers?.timeDelay?.triggered;
  }
}

Advanced Metrics

Scroll Depth Plugin tracks:

  • Current & max scroll percentage
  • Scroll velocity & direction
  • Direction change count
  • Time to threshold
  • Engagement score (weighted metric)

Privacy-First

Page Visits Plugin:

  • Honors Do Not Track (DNT)
  • Enable/disable API
  • Clear data on demand
  • Cross-tab safe storage

Breaking Changes

None. All changes are backward compatible.

Stats

  • +6,725 lines added
  • -330 lines removed
  • 36 files changed
  • 4 new plugins
  • 101 new tests for display conditions
  • 12 integration tests
  • ~1,500 lines of documentation

Testing

pnpm build && pnpm test && pnpm lint

All 314 tests passing ✅

Next Steps

  • Issue [Phase 1] Playground Examples #33 (Playground Examples) will be handled in separate PR to experience-sdk-playground repo
  • Phase 2: Advanced features (jstag integration, advanced targeting)

…cture

- Add trigger state tracking to core Context type
- Implement trigger:* event listener in ExperienceRuntime
- Create exitIntentPlugin with Pathfora-compatible algorithm
  - Mouse position tracking (last 30 positions)
  - Upward movement detection with velocity check
  - Configurable: sensitivity, minTimeOnPage, delay, disableOnMobile
  - Session-based suppression (one trigger per session)
  - Clean API: isTriggered(), reset(), getPositions()
- Add comprehensive test suite (21 tests covering all Pathfora use cases)
- Event-driven pattern: plugins emit events, core orchestrates evaluation
- Composable: use context.triggers.exitIntent.triggered in custom rules

Foundation for all display condition plugins (scroll depth, page visits, etc).

All tests passing (222 total, up from 201).
- spec.md: goals, scope, user stories, architecture
- plan.md: implementation details for all 4 plugins
- tasks.md: 14 GitHub issues (tasks 1-2 complete)

Exit intent plugin complete. Remaining: scroll depth, page visits, time delay.
All 13 tasks completed:
- Core runtime + types
- Plugins (frequency, debug, banner)
- Integration + testing
- Playground deployed (https://xp-examples.vercel.app/)
- Bundle size: 8.4 KB gzipped (target: <15 KB)

222 tests passing, docs complete.
Phase 0 (Foundation) is complete.
Phase 1 (Display Conditions) is next.
No intermediate phase needed.
- Multiple thresholds support [25, 50, 75, 100] default
- Configurable throttling (100ms default)
- Max scroll percentage tracking
- Both viewport calculation methods (inclusive/exclusive)
- Reset capability for SPAs and testing
- Resize handling for responsive layouts
- Device detection (mobile/tablet/desktop) with opt-out
- Advanced metrics (opt-in via trackAdvancedMetrics flag):
  * Velocity tracking (scroll speed, px/ms)
  * Direction change detection (seeking behavior)
  * Time-to-threshold metrics (engagement speed)
  * Engagement quality scoring (0-100)
  * Time spent scrolling up (UX friction indicator)
- TypeScript types for all configs
- Comprehensive test suite (25 tests, 100% coverage)

Closes #34
Session and lifetime visit tracking for client-side SDKs.

Features:
- Session-scoped counter (sessionStorage)
- Lifetime counter with timestamps (localStorage)
- First-visit detection
- DNT (Do Not Track) support
- Automatic storage plugin loading
- TTL/expiration for GDPR compliance
- Event emission (pageVisits:incremented, pageVisits:reset)
- Generic sdk-kit plugin (designed for contribution back)
- Composable with frequencyPlugin

Plugin follows sdk-kit patterns:
- Reuses storagePlugin for persistence
- Event-driven architecture (same as exitIntent/scrollDepth)
- Auto-registers in ExperienceRuntime
- Updates Context triggers for evaluation

API:
- getTotalCount(): number
- getSessionCount(): number
- isFirstVisit(): boolean
- getFirstVisitTime(): number | undefined
- getLastVisitTime(): number | undefined
- increment(): void
- reset(): void
- getState(): PageVisitsEvent

Tests:
- 36 comprehensive tests covering all features
- Session and lifetime counting
- First-visit detection
- DNT support
- Storage persistence
- API methods
- Event emission
- Custom configuration
- Integration scenarios

All tests pass (283/283 total)

Related to #35
- Convert index files to barrel exports
- Extract pure functions for improved testability:
  - Exit Intent: isMobileDevice, hasMinTimeElapsed,
    calculateVelocity, shouldTriggerExitIntent,
    createExitIntentEvent
  - Scroll Depth: detectDevice, throttle,
    calculateScrollPercent, calculateEngagementScore
  - Page Visits: respectsDNT, buildStorageKey,
    createVisitsEvent
- Update biome.json to allow 'any' in plugin impl files
- Remove all biome-ignore comments from codebase

All tests passing (283/283), linting clean
- Create time delay plugin with millisecond precision
- Support pause/resume on Page Visibility API
- Track active vs total elapsed time
- Extract pure functions for testability
- Expose API: getElapsed, getActiveElapsed, getRemaining,
  isPaused, isTriggered, reset
- Auto-register in core runtime
- Add 19 comprehensive tests (302 total passing)
- Update TriggerState interface for timeDelay
- Barrel export pattern for clean imports

Event-driven architecture with full timer lifecycle management
- Test plugin composition (all 4 plugins loaded together)
- Test multiple triggers firing independently
- Test complex targeting logic (AND, OR, NOT)
- Test performance (overhead, memory leaks)
- Test cleanup on destroy
- Test real-world scenarios (engaged user, returning visitor, first-time welcome)

12 new tests, 314 total tests passing
@prosdev prosdev merged commit 02de640 into main Dec 27, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Phase 1] Integration Testing [Phase 1] Time Delay Plugin [Phase 1] Page Visits Plugin [Phase 1] Scroll Depth Plugin [Phase 1] Documentation

2 participants