Skip to content
Merged
28 changes: 28 additions & 0 deletions .changeset/display-condition-plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
"@prosdevlab/experience-sdk": minor
"@prosdevlab/experience-sdk-plugins": minor
---

feat: add display condition plugins with event-driven architecture

Add 4 new display condition plugins with comprehensive testing and documentation:

**New Plugins:**
- Exit Intent: Velocity-based mouse tracking with session awareness
- Scroll Depth: Multiple thresholds with advanced engagement metrics
- Page Visits: Session and lifetime counters with first-visit detection
- Time Delay: Millisecond-precision delays with visibility API integration

**Core Enhancements:**
- Event-driven trigger architecture (`trigger:*` events)
- Composable display conditions (AND/OR/NOT logic)
- TriggerState interface for type-safe context updates

**Developer Experience:**
- 101 new tests across 4 plugins (314 total)
- 12 integration tests for plugin composition
- Complete API documentation with examples
- Pure functions for easier testing

All plugins are backward compatible and work independently or together.

4 changes: 4 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
"packages/core/src/types.ts",
"packages/core/src/runtime.ts",
"packages/plugins/src/types.ts",
"packages/plugins/src/exit-intent/exit-intent.ts",
"packages/plugins/src/scroll-depth/scroll-depth.ts",
"packages/plugins/src/page-visits/page-visits.ts",
"packages/plugins/src/utils/sanitize.ts",
"specs/**/contracts/types.ts",
"docs/**/*.tsx"
],
Expand Down
1 change: 0 additions & 1 deletion docs/pages/reference/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@
"events": "Events",
"plugins": "Plugins"
}

11 changes: 11 additions & 0 deletions docs/pages/reference/plugins/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"index": "Overview",
"banner": "Banner",
"frequency": "Frequency",
"debug": "Debug",
"exit-intent": "Exit Intent",
"scroll-depth": "Scroll Depth",
"page-visits": "Page Visits",
"time-delay": "Time Delay"
}

Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
# Plugins

The Experience SDK uses a plugin architecture powered by [@lytics/sdk-kit](https://github.com/Lytics/sdk-kit). Plugins extend the runtime with additional capabilities.

## Official Plugins

### Banner Plugin
# Banner Plugin

Renders banner experiences in the DOM with automatic positioning, theming, and responsive layout.

Expand Down Expand Up @@ -236,219 +230,3 @@ content: {
}
}
```

---

### Frequency Plugin

Manages impression tracking and frequency capping using persistent storage.

#### Configuration

```typescript
const experiences = createInstance({
frequency: {
enabled: true, // Enable frequency tracking (default: true)
storage: 'local' // 'local' | 'session' | 'memory' (default: 'local')
}
});
```

#### Experience-Level Frequency

Define frequency caps per experience:

```typescript
experiences.register('welcome', {
type: 'banner',
content: { message: 'Welcome!' },
frequency: {
max: 3, // Maximum impressions
per: 'session' // 'session' | 'day' | 'week'
}
});
```

#### API Methods

##### `frequency.getImpressionCount(experienceId, period?)`

Get impression count for an experience.

```typescript
const count = experiences.frequency.getImpressionCount('welcome', 'session');
console.log(`Shown ${count} times this session`);
```

**Parameters:**
- `experienceId: string` - Experience to check
- `period?: 'session' | 'day' | 'week'` - Time period (optional)

**Returns:** `number`

##### `frequency.recordImpression(experienceId)`

Manually record an impression.

```typescript
experiences.frequency.recordImpression('welcome');
```

##### `frequency.reset(experienceId?)`

Reset impression counts.

```typescript
// Reset specific experience
experiences.frequency.reset('welcome');

// Reset all experiences
experiences.frequency.reset();
```

#### How It Works

1. **Impressions** are recorded when `experiences:shown` event is emitted
2. **Storage** persists counts in localStorage (or sessionStorage/memory)
3. **Evaluation** checks counts before showing experiences
4. **Dismissals** do NOT count as impressions

#### Storage Keys

Frequency data is stored with namespaced keys:

```
experiences.frequency.session:welcome = 3
experiences.frequency.day:2024-12-25:welcome = 1
experiences.frequency.week:2024-W52:welcome = 5
```

---

### Debug Plugin

Provides console logging and window event emission for debugging and Chrome extension integration.

#### Configuration

```typescript
const experiences = createInstance({
debug: {
enabled: true, // Enable debug output (default: false)
windowEvents: true, // Emit to window (default: true when enabled)
prefix: 'experiences' // Log prefix (default: 'experiences')
}
});
```

Or use the shorthand:

```typescript
const experiences = createInstance({ debug: true });
```

#### API Methods

##### `debug.log(message, data?)`

Log a message with optional data.

```typescript
experiences.debug.log('User clicked button', { action: 'cta' });
```

**Console output:**
```
[experiences] User clicked button { action: 'cta' }
```

**Window event:**
```javascript
window.addEventListener('experiences:debug', (event) => {
console.log(event.detail);
// { message: 'User clicked button', data: { action: 'cta' }, timestamp: ... }
});
```

#### Automatic Logging

When debug mode is enabled, the plugin automatically logs:

- SDK initialization
- Experience registration
- Evaluation results
- Decision reasons
- Event emissions

```typescript
const experiences = createInstance({ debug: true });

experiences.register('welcome', { ... });
// [experiences] Experience registered: welcome

const decision = experiences.evaluate();
// [experiences] Evaluating 1 experience(s)
// [experiences] Decision: show=true, experience=welcome
// [experiences] Reasons: ["✅ URL matches", "✅ Frequency: 0/3 this session"]
```

#### Chrome Extension Integration

The debug plugin emits events to `window` that can be captured by Chrome extensions:

```javascript
// In Chrome extension content script
window.addEventListener('experiences:debug', (event) => {
chrome.runtime.sendMessage({
type: 'EXPERIENCE_DEBUG',
payload: event.detail
});
});
```

This enables building DevTools panels for inspecting experience decisions in real-time.

---

## Plugin Development

The SDK uses [@lytics/sdk-kit](https://github.com/Lytics/sdk-kit)'s plugin system. Custom plugins can be created using the `PluginFunction` interface:

```typescript
import type { PluginFunction } from '@lytics/sdk-kit';

const myPlugin: PluginFunction = (plugin, instance, config) => {
// Namespace
plugin.ns('my.plugin');

// Default config
plugin.defaults({ myPlugin: { enabled: true } });

// Expose API
plugin.expose({
myMethod() {
console.log('Hello from plugin!');
}
});

// Listen to events
instance.on('experiences:evaluated', (decision) => {
// React to evaluations
});
};

// Use the plugin
const experiences = createInstance();
experiences.use(myPlugin);
```

See the [sdk-kit documentation](https://github.com/Lytics/sdk-kit) for complete plugin development guide.

---

## Next Steps

- [Banner Examples](/demo/banner) - Complete banner usage examples
- [Events Reference](/reference/events) - All SDK events
- [API Reference](/reference) - Core API documentation

88 changes: 88 additions & 0 deletions docs/pages/reference/plugins/debug.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Debug Plugin

Provides console logging and window event emission for debugging and Chrome extension integration.

#### Configuration

```typescript
const experiences = createInstance({
debug: {
enabled: true, // Enable debug output (default: false)
windowEvents: true, // Emit to window (default: true when enabled)
prefix: 'experiences' // Log prefix (default: 'experiences')
}
});
```

Or use the shorthand:

```typescript
const experiences = createInstance({ debug: true });
```

#### API Methods

##### `debug.log(message, data?)`

Log a message with optional data.

```typescript
experiences.debug.log('User clicked button', { action: 'cta' });
```

**Console output:**
```
[experiences] User clicked button { action: 'cta' }
```

**Window event:**
```javascript
window.addEventListener('experiences:debug', (event) => {
console.log(event.detail);
// { message: 'User clicked button', data: { action: 'cta' }, timestamp: ... }
});
```

#### Automatic Logging

When debug mode is enabled, the plugin automatically logs:

- SDK initialization
- Experience registration
- Evaluation results
- Decision reasons
- Event emissions

```typescript
const experiences = createInstance({ debug: true });

experiences.register('welcome', { ... });
// [experiences] Experience registered: welcome

const decision = experiences.evaluate();
// [experiences] Evaluating 1 experience(s)
// [experiences] Decision: show=true, experience=welcome
// [experiences] Reasons: ["✅ URL matches", "✅ Frequency: 0/3 this session"]
```

#### Chrome Extension Integration

The debug plugin emits events to `window` that can be captured by Chrome extensions:

```javascript
// In Chrome extension content script
window.addEventListener('experiences:debug', (event) => {
chrome.runtime.sendMessage({
type: 'EXPERIENCE_DEBUG',
payload: event.detail
});
});
```

This enables building DevTools panels for inspecting experience decisions in real-time.

---

## Display Condition Plugins

Display condition plugins enable advanced triggering logic beyond simple URL and user targeting. These plugins detect user behavior and emit `trigger:*` events that update the evaluation context.
Loading