@mixpeek/prebid 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,439 +1,331 @@
1
- # Mixpeek Contextual Adapter for Prebid.js
1
+
2
+ <p align="center">
3
+ <img src="assets/header.png" alt="Mixpeek Multimodal Contextual Targeting Adapter" />
4
+ </p>
5
+
6
+ # Mixpeek RTD (Real-Time Data) Adapter for Prebid.js
7
+
8
+ **This is a Real-Time Data (RTD) module for Prebid.js that replaces cookie-based targeting with sub-100ms contextual signals.**
2
9
 
3
10
  [![npm version](https://img.shields.io/npm/v/@mixpeek/prebid.svg)](https://www.npmjs.com/package/@mixpeek/prebid)
4
11
  [![npm downloads](https://img.shields.io/npm/dm/@mixpeek/prebid.svg)](https://www.npmjs.com/package/@mixpeek/prebid)
5
12
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
- [![Node Version](https://img.shields.io/node/v/@mixpeek/prebid.svg)](https://www.npmjs.com/package/@mixpeek/prebid)
7
13
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@mixpeek/prebid)](https://bundlephobia.com/package/@mixpeek/prebid)
8
- [![Dependencies](https://img.shields.io/librariesio/release/npm/@mixpeek/prebid)](https://www.npmjs.com/package/@mixpeek/prebid)
9
- [![GitHub Stars](https://img.shields.io/github/stars/mixpeek/prebid?style=social)](https://github.com/mixpeek/prebid)
10
- [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/mixpeek/prebid/pulls)
14
+ [![Status](https://img.shields.io/badge/Status-Production--Ready-brightgreen.svg)](https://www.npmjs.com/package/@mixpeek/prebid)
11
15
 
12
- ## 🎯 Overview
16
+ ---
13
17
 
14
- The Mixpeek Contextual Adapter enables publishers and SSPs using **Prebid.js** to enrich bid requests with real-time contextual data powered by Mixpeek's multimodal AI engine. This adapter provides:
18
+ ## What This Is
15
19
 
16
- - **Privacy-First Targeting**: No cookies, just content-based context
17
- - **Multimodal Analysis**: Text, images, video, and audio processing
18
- - **IAB Taxonomy**: Automatic classification into IAB content categories
19
- - **Brand Safety**: Real-time brand safety scoring
20
- - **Ad Adjacency Awareness**: Tracks previous ad to avoid repetition and improve user experience
21
- - **Sub-100ms Performance**: Optimized for header bidding speed requirements
22
- - **Graceful Fallbacks**: Never blocks the auction
20
+ | Type | Status |
21
+ |------|--------|
22
+ | **RTD Module** (bid enrichment) | Supported |
23
+ | Bidder Adapter | Not a bidder |
24
+ | Analytics Adapter | Not analytics |
25
+ | Identity Module | Not identity |
23
26
 
24
- ## 🚀 Quick Start
27
+ **SSPs and DSPs can immediately consume these signals via `ortb2.site.content` and `ortb2Imp.ext.data` without custom integration.**
25
28
 
26
- ### Installation
29
+ ---
27
30
 
28
- ```bash
29
- npm install @mixpeek/prebid
30
- ```
31
+ ## Who This Is For
32
+
33
+ - **Publishers** migrating off cookie-based targeting
34
+ - **SSPs** looking for contextual + adjacency signals
35
+ - **DSPs** pricing inventory using OpenRTB 2.6 context
36
+
37
+ Used in Mixpeek production demos and SSP pilots.
38
+
39
+ ---
31
40
 
32
- ### Basic Setup
41
+ ## Why Use This
42
+
43
+ 1. **Ad Adjacency Awareness** - Competitive separation, no repeat creatives
44
+ 2. **Privacy-First Contextual Targeting** - No cookies, no user tracking
45
+ 3. **IAB Taxonomy Classification** - Content categorization (v3.0)
46
+ 4. **Brand Safety Scoring** - Real-time sentiment analysis
47
+ 5. **Multimodal Analysis** - Text, image, video content understanding
48
+ 6. **Sub-100ms RTD Performance** - Optimized for header bidding latency
49
+
50
+ **Graceful Failure:** If Mixpeek is unavailable or times out, auctions proceed normally. The module never blocks bid requests.
51
+
52
+ ---
53
+
54
+ ## Minimal Setup (Copy-Paste Ready)
33
55
 
34
56
  ```javascript
35
- // 1. Include the Mixpeek RTD module
36
57
  import '@mixpeek/prebid'
37
58
 
38
- // 2. Configure Mixpeek as an RTD provider
39
59
  pbjs.setConfig({
40
60
  realTimeData: {
41
- auctionDelay: 250, // Max time to wait for contextual data (ms)
42
61
  dataProviders: [{
43
62
  name: 'mixpeek',
44
- waitForIt: true, // Wait for Mixpeek before starting auction
45
63
  params: {
46
- apiKey: 'YOUR_MIXPEEK_API_KEY',
47
- collectionId: 'your-collection-id',
48
-
49
- // Use development server (temporary)
50
- endpoint: 'https://server-xb24.onrender.com',
51
- // Or production: endpoint: 'https://api.mixpeek.com',
52
-
53
- namespace: 'your-namespace', // optional
54
- featureExtractors: ['taxonomy', 'brand-safety'],
55
- mode: 'page', // 'page', 'video', or 'auto'
56
- timeout: 5000, // ms - higher for dev server
57
- cacheTTL: 300 // seconds
64
+ apiKey: 'YOUR_API_KEY',
65
+ collectionId: 'YOUR_COLLECTION_ID',
66
+ namespace: 'YOUR_NAMESPACE'
58
67
  }
59
68
  }]
60
69
  }
61
70
  })
62
-
63
- // 3. The RTD module automatically enriches bid requests!
64
- pbjs.requestBids({
65
- adUnits: [...],
66
- bidsBackHandler: function(bids) {
67
- // Bids now include Mixpeek contextual data in ortb2
68
- }
69
- })
70
71
  ```
71
72
 
72
- ## 📋 Prerequisites
73
-
74
- 1. **Mixpeek Account**: Sign up at [mixpeek.com](https://mixpeek.com/start)
75
- 2. **API Key**: Generate an API key in your Mixpeek dashboard
76
- 3. **Collection**: Create a collection with feature extractors configured
77
- 4. **Prebid.js**: Version 6.0.0 or higher
73
+ That's it. The RTD module automatically enriches all bid requests.
78
74
 
79
- ## 🔧 Configuration Options
75
+ ---
80
76
 
81
- ### RTD Configuration
77
+ ## Installation
82
78
 
83
- | Option | Type | Required | Default | Description |
84
- |--------|------|----------|---------|-------------|
85
- | `realTimeData.auctionDelay` | number | ❌ | 250 | Max time to wait for all RTD providers (ms) |
86
- | `realTimeData.dataProviders[].name` | string | ✅ | - | Must be `'mixpeek'` |
87
- | `realTimeData.dataProviders[].waitForIt` | boolean | ❌ | false | Wait for Mixpeek before starting auction |
88
-
89
- ### Mixpeek Parameters
79
+ ```bash
80
+ npm install @mixpeek/prebid
81
+ ```
90
82
 
91
- | Option | Type | Required | Default | Description |
92
- |--------|------|----------|---------|-------------|
93
- | `params.apiKey` | string | ✅ | - | Your Mixpeek API key |
94
- | `params.collectionId` | string | ✅ | - | Mixpeek collection ID for document processing |
95
- | `params.endpoint` | string | ❌ | `https://server-xb24.onrender.com` | Mixpeek API endpoint (dev server default) |
96
- | `params.namespace` | string | ❌ | - | Optional namespace for data isolation |
97
- | `params.featureExtractors` | array | ❌ | `['taxonomy']` | Feature extractors to use (taxonomy, brand-safety, etc.) |
98
- | `params.mode` | string | ❌ | `auto` | Content mode: `page`, `video`, `image`, or `auto` |
99
- | `params.timeout` | number | ❌ | 250 | API request timeout in milliseconds |
100
- | `params.cacheTTL` | number | ❌ | 300 | Cache TTL in seconds |
101
- | `params.enableCache` | boolean | ❌ | `true` | Enable local caching |
102
- | `params.debug` | boolean | ❌ | `false` | Enable debug logging |
103
- | `params.batchSize` | number | ❌ | 1 | Number of concurrent requests |
104
- | `params.retryAttempts` | number | ❌ | 2 | Number of retry attempts on failure |
83
+ ## Prerequisites
105
84
 
106
- ## 📊 Output: OpenRTB 2.6 Data Structure
85
+ 1. **Mixpeek Account** - Sign up at [mixpeek.com](https://mixpeek.com/start)
86
+ 2. **API Key** - Generate in your Mixpeek dashboard
87
+ 3. **Collection** - Create a collection with feature extractors
88
+ 4. **Namespace** - Your namespace ID (format: `ns_xxxxx`)
89
+ 5. **Prebid.js** - Version 6.0.0 or higher
107
90
 
108
- The RTD module injects contextual data into your bid requests using the OpenRTB 2.6 standard:
91
+ ---
109
92
 
110
- ### Site-Level Data (`ortb2.site.content`)
111
- ```javascript
112
- {
113
- "ortb2": {
114
- "site": {
115
- "content": {
116
- "cat": ["IAB19-11"], // IAB Content Categories
117
- "cattax": 6, // IAB Content Taxonomy v3.0
118
- "genre": "Technology - AI", // Human-readable category
119
- "keywords": "ai,technology,ml", // Extracted keywords
120
- "language": "en", // Content language
121
- "title": "Article Title", // Page title
122
- "url": "https://example.com", // Page URL
123
- "ext": {
124
- "data": {
125
- "mixpeek": {
126
- "score": 0.94, // Confidence score
127
- "brandSafety": 0.98, // Brand safety score
128
- "sentiment": "positive", // Content sentiment
129
- "embeddingId": "emb_abc123" // Embedding ID
130
- }
131
- }
132
- }
133
- }
134
- }
135
- }
136
- }
137
- ```
93
+ ## Full Configuration
138
94
 
139
- ### Impression-Level Data (`ortb2Imp.ext.data`)
140
95
  ```javascript
141
- {
142
- // Current page context
143
- "hb_mixpeek_taxonomy": "IAB19-11", // Primary IAB taxonomy code
144
- "hb_mixpeek_category": "Technology > AI", // Human-readable category
145
- "hb_mixpeek_node": "node_tech_ai", // Taxonomy node ID
146
- "hb_mixpeek_path": "tech/ai/ml", // Hierarchical path
147
- "hb_mixpeek_score": "0.94", // Confidence score
148
- "hb_mixpeek_safety": "0.98", // Brand safety score
149
- "hb_mixpeek_keywords": "AI,ML,tech", // Extracted keywords
150
- "hb_mixpeek_embed": "emb_abc123", // Embedding ID for retrieval
151
-
152
- // Previous ad context (adjacency awareness)
153
- "hb_mixpeek_prev_creative": "12345", // Last creative ID shown
154
- "hb_mixpeek_prev_bidder": "appnexus", // Last bidder that won
155
- "hb_mixpeek_prev_adunit": "sidebar-1", // Last ad unit code
156
- "hb_mixpeek_prev_cat": "IAB18-1,IAB12-3" // Last ad categories
157
- }
158
- ```
159
-
160
- ## 🎥 Usage Examples
161
-
162
- ### Page Context (Articles, Blogs)
96
+ import '@mixpeek/prebid'
163
97
 
164
- ```javascript
165
98
  pbjs.setConfig({
166
99
  realTimeData: {
167
- auctionDelay: 250,
100
+ auctionDelay: 250, // Max wait for contextual data (ms)
168
101
  dataProviders: [{
169
102
  name: 'mixpeek',
170
- waitForIt: true,
103
+ waitForIt: true, // Wait for Mixpeek before auction
171
104
  params: {
172
- apiKey: 'sk_your_api_key',
173
- collectionId: 'col_articles',
174
- mode: 'page',
175
- featureExtractors: ['taxonomy', 'brand-safety', 'keywords']
105
+ // Required
106
+ apiKey: 'YOUR_API_KEY',
107
+ collectionId: 'YOUR_COLLECTION_ID',
108
+ namespace: 'YOUR_NAMESPACE', // e.g., 'ns_abc123'
109
+
110
+ // Optional
111
+ endpoint: 'https://api.mixpeek.com', // Default
112
+ mode: 'auto', // 'page', 'video', 'image', or 'auto'
113
+ timeout: 250, // API timeout in ms
114
+ cacheTTL: 300, // Cache TTL in seconds
115
+ enableCache: true, // Enable local caching
116
+ debug: false // Enable debug logging
176
117
  }
177
118
  }]
178
119
  }
179
120
  })
180
- ```
181
121
 
182
- ### Video Context (Pre-roll, Mid-roll)
183
-
184
- ```javascript
185
- pbjs.setConfig({
186
- realTimeData: {
187
- auctionDelay: 300, // Longer delay for video processing
188
- dataProviders: [{
189
- name: 'mixpeek',
190
- waitForIt: true,
191
- params: {
192
- apiKey: 'sk_your_api_key',
193
- collectionId: 'col_videos',
194
- mode: 'video',
195
- videoSelector: '#main-video', // CSS selector for video element
196
- featureExtractors: ['taxonomy', 'scene-detection']
197
- }
198
- }]
122
+ // Bids are automatically enriched with contextual data
123
+ pbjs.requestBids({
124
+ adUnits: [...],
125
+ bidsBackHandler: function(bids) {
126
+ // Bids now include Mixpeek contextual data in ortb2
199
127
  }
200
128
  })
201
129
  ```
202
130
 
203
- ### Multi-Content Auto-Detection
131
+ ---
204
132
 
205
- ```javascript
206
- pbjs.setConfig({
207
- realTimeData: {
208
- auctionDelay: 250,
209
- dataProviders: [{
210
- name: 'mixpeek',
211
- waitForIt: true,
212
- params: {
213
- apiKey: 'sk_your_api_key',
214
- collectionId: 'col_mixed',
215
- mode: 'auto', // Automatically detects page, video, or image content
216
- featureExtractors: ['taxonomy', 'brand-safety', 'clustering']
217
- }
218
- }]
219
- }
220
- })
221
- ```
133
+ ## Ad Adjacency Awareness (Key Feature)
222
134
 
223
- ## 🏗️ How It Works
224
-
225
- ```mermaid
226
- sequenceDiagram
227
- participant Publisher
228
- participant Prebid
229
- participant MixpeekAdapter
230
- participant MixpeekAPI
231
- participant SSP
232
-
233
- Publisher->>Prebid: Request Bids
234
- Prebid->>MixpeekAdapter: beforeRequestBids event
235
- MixpeekAdapter->>MixpeekAdapter: Extract page/video content
236
- MixpeekAdapter->>MixpeekAdapter: Check cache
237
- alt Cache Miss
238
- MixpeekAdapter->>MixpeekAPI: POST /collections/{id}/documents
239
- MixpeekAPI->>MixpeekAPI: Process with feature extractors
240
- MixpeekAPI-->>MixpeekAdapter: Return enrichments
241
- MixpeekAdapter->>MixpeekAdapter: Cache result
242
- end
243
- MixpeekAdapter->>Prebid: Inject contextual key-values
244
- Prebid->>SSP: Send enriched bid request
245
- SSP-->>Prebid: Return bids
246
- Prebid-->>Publisher: Render ad
247
- ```
135
+ The adapter automatically tracks previously served ads to enable:
248
136
 
249
- ## 🧪 Testing
137
+ - **Competitive Separation** - Avoid showing competing brands consecutively
138
+ - **Creative Frequency** - Prevent the same ad from showing repeatedly
139
+ - **Category Diversity** - Improve ad variety for better user experience
250
140
 
251
- ```bash
252
- # Run all tests
253
- npm test
141
+ ### How SSPs/DSPs Use It
254
142
 
255
- # Run with coverage
256
- npm run test:coverage
143
+ ```javascript
144
+ // Data automatically injected into ortb2Imp.ext.data:
145
+ {
146
+ "hb_mixpeek_prev_creative": "12345", // Last creative ID
147
+ "hb_mixpeek_prev_bidder": "appnexus", // Last winning bidder
148
+ "hb_mixpeek_prev_adunit": "sidebar-1", // Last ad unit
149
+ "hb_mixpeek_prev_cat": "IAB18-1,IAB12-3" // Last ad categories
150
+ }
257
151
 
258
- # Watch mode
259
- npm run test:watch
152
+ // DSP can use for competitive separation:
153
+ if (prevCategories.includes('IAB18-1') && currentAd.category === 'IAB18-1') {
154
+ // Don't show competing fashion ads back-to-back
155
+ }
260
156
  ```
261
157
 
262
- ## 📖 Advanced Configuration
158
+ ### Privacy Safe
263
159
 
264
- ### Custom Feature Extractors
160
+ - No user tracking or identifiers
161
+ - Only ad metadata stored (< 200 bytes)
162
+ - Session-scoped, localStorage with memory fallback
163
+ - GDPR/CCPA compliant (contextual, not behavioral)
265
164
 
266
- ```javascript
267
- pbjs.setConfig({
268
- realTimeData: {
269
- auctionDelay: 250,
270
- dataProviders: [{
271
- name: 'mixpeek',
272
- waitForIt: true,
273
- params: {
274
- apiKey: 'sk_your_api_key',
275
- collectionId: 'col_custom',
276
- customExtractors: [
277
- {
278
- feature_extractor_id: 'sentiment-analyzer',
279
- payload: {
280
- model: 'sentiment-v2',
281
- threshold: 0.7
282
- }
283
- }
284
- ]
285
- }
286
- }]
287
- }
288
- })
289
- ```
165
+ ---
290
166
 
291
- ### Conditional Loading
167
+ ## OpenRTB 2.6 Output
168
+
169
+ ### Site-Level Data (`ortb2.site.content`)
292
170
 
293
171
  ```javascript
294
- // Only enrich on specific pages
295
- if (window.location.pathname.startsWith('/articles/')) {
296
- pbjs.setConfig({
297
- realTimeData: {
298
- auctionDelay: 250,
299
- dataProviders: [{
300
- name: 'mixpeek',
301
- waitForIt: true,
302
- params: {
303
- apiKey: 'sk_your_api_key',
304
- collectionId: 'col_articles',
305
- mode: 'page'
172
+ {
173
+ "site": {
174
+ "content": {
175
+ "cat": ["IAB19-11"], // IAB Content Categories
176
+ "cattax": 6, // IAB Taxonomy v3.0
177
+ "genre": "Technology", // Human-readable category
178
+ "keywords": "ai,technology,ml", // Extracted keywords
179
+ "language": "en", // Content language
180
+ "ext": {
181
+ "data": {
182
+ "mixpeek": {
183
+ "score": 0.94, // Classification confidence
184
+ "brandSafety": 0.98, // Brand safety score
185
+ "sentiment": "positive" // Content sentiment
186
+ }
306
187
  }
307
- }]
188
+ }
308
189
  }
309
- })
190
+ }
310
191
  }
311
192
  ```
312
193
 
313
- ### Event Callbacks
194
+ ### Impression-Level Keys (`ortb2Imp.ext.data`)
314
195
 
315
- ```javascript
316
- pbjs.onEvent('mixpeekContextReady', function(context) {
317
- console.log('Mixpeek context loaded:', context)
318
- // Custom analytics or modifications
319
- })
196
+ | Key | Description | Example |
197
+ |-----|-------------|---------|
198
+ | `hb_mixpeek_category` | Content category | `"Technology"` |
199
+ | `hb_mixpeek_score` | Classification confidence | `"0.94"` |
200
+ | `hb_mixpeek_safety` | Brand safety score | `"0.98"` |
201
+ | `hb_mixpeek_keywords` | Extracted keywords | `"AI,ML,tech"` |
202
+ | `hb_mixpeek_sentiment` | Content sentiment | `"positive"` |
203
+ | `hb_mixpeek_prev_creative` | Last creative ID | `"12345"` |
204
+ | `hb_mixpeek_prev_bidder` | Last winning bidder | `"appnexus"` |
205
+ | `hb_mixpeek_prev_cat` | Last ad categories | `"IAB18-1"` |
320
206
 
321
- pbjs.onEvent('mixpeekContextError', function(error) {
322
- console.error('Mixpeek context error:', error)
323
- // Custom error handling
324
- })
325
- ```
207
+ ---
326
208
 
327
- ## 🔄 Previous Ad Tracking (Adjacency Awareness)
209
+ ## How It Works
328
210
 
329
- The adapter automatically tracks the most recently served ad to enable adjacency-aware targeting. This helps:
211
+ ```
212
+ USER → WEBSITE → PREBID.JS
213
+
214
+ ├──→ MIXPEEK RTD Module
215
+ │ (Extract page content)
216
+ │ (Get previous ad info)
217
+ │ ↓
218
+ │ Returns: categories, keywords, sentiment
219
+ │ ↓
220
+ (enrich bid request with ortb2)
221
+
222
+ ├──→ SSP 1 ──→ DSPs (use contextual signals)
223
+ ├──→ SSP 2 ──→ DSPs (use adjacency data)
224
+ └──→ SSP N
225
+
226
+ (collect bids)
227
+
228
+
229
+ AD SERVER
230
+
231
+
232
+ RELEVANT AD
233
+ ```
330
234
 
331
- - **Avoid Ad Repetition**: Prevent showing the same creative or category repeatedly
332
- - **Frequency Capping**: Build frequency cap rules based on previous impressions
333
- - **Competitive Separation**: Avoid showing competing brands consecutively
334
- - **Enhanced User Experience**: Improve ad diversity and relevance
235
+ ---
335
236
 
336
- ### How It Works
237
+ ## Content Modes
337
238
 
338
- 1. **Automatic Tracking**: On every `bidResponse` event, the adapter stores minimal information about the winning ad
339
- 2. **Lightweight Storage**: Data is stored in memory + localStorage (privacy-safe, no PII)
340
- 3. **Targeting Keys**: Previous ad data is automatically injected into subsequent bid requests
239
+ ### Page Context (Default)
341
240
 
342
- ### Data Tracked
241
+ ```javascript
242
+ params: {
243
+ mode: 'page', // Analyze article/page content
244
+ // ...
245
+ }
246
+ ```
343
247
 
344
- | Field | Description | Example |
345
- |-------|-------------|---------|
346
- | `creativeId` | Winning creative ID | `"12345"` |
347
- | `bidder` | Winning bidder code | `"appnexus"` |
348
- | `adUnitCode` | Ad unit that served the ad | `"sidebar-1"` |
349
- | `categories` | IAB categories of the ad | `["IAB18-1", "IAB12-3"]` |
350
- | `timestamp` | When the ad was served | `1697123456789` |
248
+ ### Video Context
351
249
 
352
- ### Targeting Keys Injected
250
+ ```javascript
251
+ params: {
252
+ mode: 'video',
253
+ videoSelector: '#main-video', // CSS selector
254
+ // ...
255
+ }
256
+ ```
353
257
 
354
- The following keys are automatically added to `ortb2Imp.ext.data`:
258
+ ### Auto-Detection
355
259
 
356
- - `hb_mixpeek_prev_creative` - Last creative ID
357
- - `hb_mixpeek_prev_bidder` - Last winning bidder
358
- - `hb_mixpeek_prev_adunit` - Last ad unit code
359
- - `hb_mixpeek_prev_cat` - Last ad categories (comma-separated)
260
+ ```javascript
261
+ params: {
262
+ mode: 'auto', // Automatically detect content type
263
+ // ...
264
+ }
265
+ ```
360
266
 
361
- ### SSP/DSP Usage
267
+ ---
362
268
 
363
- SSPs and DSPs can use these keys for advanced targeting rules:
269
+ ## Event Callbacks
364
270
 
365
271
  ```javascript
366
- // Example: Avoid showing the same creative twice in a row
367
- if (bidRequest.ortb2Imp.ext.data.hb_mixpeek_prev_creative === currentCreative.id) {
368
- // Skip this creative or reduce bid
369
- }
370
-
371
- // Example: Competitive separation
372
- const prevCategories = bidRequest.ortb2Imp.ext.data.hb_mixpeek_prev_cat?.split(',') || []
373
- if (prevCategories.includes('IAB18-1') && currentAd.category === 'IAB18-1') {
374
- // Don't show competing fashion ads back-to-back
375
- }
376
- ```
272
+ // Context ready
273
+ pbjs.onEvent('mixpeekContextReady', function(context) {
274
+ console.log('Category:', context.taxonomy?.label)
275
+ console.log('Keywords:', context.keywords)
276
+ })
377
277
 
378
- ### Privacy & Storage
278
+ // Error handling
279
+ pbjs.onEvent('mixpeekContextError', function(error) {
280
+ // Errors don't block auction (graceful degradation)
281
+ console.error('Mixpeek error:', error)
282
+ })
379
283
 
380
- - **No User Tracking**: Only ad metadata is stored, no user identifiers or behavior
381
- - **Session-Scoped**: Data persists across page views within a session
382
- - **Local Storage**: Falls back to memory-only if localStorage is unavailable
383
- - **Minimal Data**: Only essential fields are stored (< 200 bytes)
384
- - **GDPR/CCPA Compliant**: No consent required as it doesn't track users
284
+ // Cache hit
285
+ pbjs.onEvent('mixpeekContextCached', function(data) {
286
+ console.log('Using cached context')
287
+ })
288
+ ```
385
289
 
386
- ### Programmatic Control
290
+ ---
387
291
 
388
- You can access the previous ad tracker directly if needed:
292
+ ## Testing
389
293
 
390
- ```javascript
391
- import previousAdTracker from '@mixpeek/prebid/utils/previousAdTracker'
294
+ ```bash
295
+ # Run all tests
296
+ npm test
392
297
 
393
- // Get last ad info
394
- const lastAd = previousAdTracker.getLast()
395
- console.log('Last creative:', lastAd?.creativeId)
298
+ # Run with live API (requires credentials)
299
+ MIXPEEK_API_KEY=your_key \
300
+ MIXPEEK_NAMESPACE=ns_xxxxx \
301
+ MIXPEEK_COLLECTION_ID=col_xxxxx \
302
+ npm run test:live
396
303
 
397
- // Clear history (e.g., on user logout or page type change)
398
- previousAdTracker.clear()
304
+ # Coverage report
305
+ npm run test:coverage
399
306
  ```
400
307
 
401
- ## 🔒 Security & Privacy
402
-
403
- - **No PII**: The adapter never sends user identifiers or cookies
404
- - **Content-Only**: Only page/video content is analyzed
405
- - **HTTPS**: All API calls use TLS encryption
406
- - **API Key Safety**: Store API keys securely (environment variables, server-side rendering)
407
- - **GDPR/CCPA Compliant**: Contextual targeting doesn't require user consent
308
+ ---
408
309
 
409
- ## 📚 Documentation
310
+ ## Documentation
410
311
 
411
- ### User Guides
412
312
  - [Quick Start](QUICKSTART.md) - Get running in 5 minutes
413
- - [Integration Guide](docs/integration-guide.md) - Step-by-step integration
414
- - [API Reference](docs/api-reference.md) - Complete API documentation
313
+ - [Integration Guide](docs/integration-guide.md) - Step-by-step setup
314
+ - [API Reference](docs/api-reference.md) - Complete API docs
415
315
  - [Testing Guide](TESTING.md) - How to test the adapter
416
- - [Endpoint Configuration](ENDPOINTS.md) - Configure API endpoints
417
- - [Health Check](docs/health-check.md) - Health check configuration
418
316
 
419
- ### Developer Resources
420
- - [Mixpeek API Docs](https://docs.mixpeek.com) - Platform documentation
421
- - [Internal Planning](tasks/) - Gap analysis & implementation plans (internal)
317
+ ---
422
318
 
423
- ## 🤝 Support
319
+ ## Support
424
320
 
425
- - **Email**: support@mixpeek.com
426
- - **GitHub Issues**: [Create an issue](https://github.com/mixpeek/prebid/issues)
427
321
  - **Documentation**: [docs.mixpeek.com](https://docs.mixpeek.com)
428
- - **Slack Community**: [Join our Slack](https://mixpeek.com/slack)
429
-
430
- ## 📄 License
431
-
432
- Apache 2.0 - see [LICENSE](LICENSE) file for details.
322
+ - **GitHub Issues**: [Create an issue](https://github.com/mixpeek/prebid/issues)
323
+ - **Email**: support@mixpeek.com
433
324
 
434
- ## 🙏 Credits
325
+ ---
435
326
 
436
- Built with ❤️ by [Mixpeek](https://mixpeek.com)
327
+ ## License
437
328
 
438
- Integrates with [Prebid.js](https://prebid.org) - an open-source header bidding solution
329
+ Apache 2.0 - see [LICENSE](LICENSE)
439
330
 
331
+ Built by [Mixpeek](https://mixpeek.com) | Integrates with [Prebid.js](https://prebid.org)