This example demonstrates how to build a complete SDK using @prosdevlab/sdk-kit and its essential plugins.
Learn how to build your own plugins:
- Logger Plugin - Console logging with levels
- Analytics Plugin - Event tracking with batching
- Monitor Plugin - Cross-plugin event monitoring
See how to use battle-tested plugins:
- Storage Plugin - Multi-backend storage (localStorage, sessionStorage, cookies)
- Consent Plugin - GDPR/CCPA compliance with state management
- Context Plugin - Automatic page, device, and environment context collection
- Poll Plugin - Wait for external scripts or conditions
- Queue Plugin - Event batching with persistence
- Transport Plugin - HTTP transport with retry logic
- Consent-aware tracking - Only send events when consent is granted
- Context attachment - Automatically attach context to all events
- Queue + Transport - Batch and send events efficiently
- Storage persistence - Survive page reloads
- Plugin composition - Multiple plugins working together
examples/minimal-sdk/
├── src/
│ ├── index.ts # Main SDK with 2 demos
│ └── plugins/
│ ├── logger.ts # Custom logging plugin
│ ├── analytics.ts # Custom analytics plugin
│ └── monitor.ts # Custom monitoring plugin
├── package.json
├── tsconfig.json
└── README.md
From the repository root:
pnpm installcd examples/minimal-sdk
pnpm buildThe example includes two demos:
Shows all 6 essential plugins working together:
// In src/index.ts, uncomment:
demoEssentialPlugins().catch(console.error);Then run:
pnpm startShows only the custom plugins (original example):
// In src/index.ts, uncomment:
demoCustomPlugins().catch(console.error);Then run:
pnpm start// Set values in different backends
sdk.storage.set('user_id', 'user-123', { backend: 'localStorage' });
sdk.storage.set('session_id', 'session-456', { backend: 'sessionStorage' });
sdk.storage.set('preference', { theme: 'dark' }, {
backend: 'cookie',
ttl: 86400 // 1 day
});
// Get values
const userId = sdk.storage.get('user_id');
const preference = sdk.storage.get('preference', { backend: 'cookie' });Features:
- Multi-backend support (localStorage, sessionStorage, cookies, memory)
- TTL expiration
- Namespace isolation
- JSON serialization
- Graceful degradation
// Grant consent
sdk.consent.grant(['analytics', 'functional']);
// Check consent
if (sdk.consent.isGranted('analytics')) {
// Track analytics
}
// Wait for consent
const granted = await sdk.consent.waitForConsent('marketing', {
timeout: 5000
});Features:
- Category-based consent (necessary, functional, analytics, marketing, social)
- Persistence to storage
- Promise-based
waitForConsent()API - State change events
- Platform adapter support (OneTrust, Cookiebot, Usercentrics)
// Collect all context
const context = sdk.context.get();
console.log(context.page.url); // Page URL
console.log(context.device.type); // 'mobile' or 'desktop'
console.log(context.screen.width); // Screen dimensions
console.log(context.environment.language); // Browser languageFeatures:
- Page context (URL, path, query params, referrer, title, session)
- Device context (type, user agent)
- Screen context (dimensions)
- Environment context (timezone, language, cookies, DNT, storage)
- Privacy-first query parameter handling (UTM-only by default)
- Caching with manual refresh
// Wait for a condition
const ready = await sdk.poll.waitFor(
() => window.myLib !== undefined,
{ timeout: 5000, interval: 100 }
);
// Wait for DOM element
await sdk.poll.element('#my-element');
// Wait for global variable
await sdk.poll.global('myLib');Features:
- Promise-based API
- Immediate check (no delay on first attempt)
- Configurable interval and timeout
- Event emission
- Lifecycle integration
// Add events to queue
sdk.queue.add({ event: 'page_view', page: '/home' });
sdk.queue.add({ event: 'button_click', button: 'cta' });
// Queue auto-flushes when maxSize reached
sdk.on('queue:flush', async ({ items }) => {
// Send to your backend
await sdk.transport.send({
url: 'https://api.example.com/events',
method: 'POST',
data: items
});
});Features:
- Auto-flush based on size or interval
- Persistence to storage (survives page reloads)
- FIFO limiting
- Lifecycle integration (flush on destroy)
- Event emission
// Send HTTP request with automatic retry
const response = await sdk.transport.send({
url: 'https://api.example.com/events',
method: 'POST',
data: { events: [...] }
});
if (response.ok) {
console.log('Success!');
}Features:
- Multiple transports (fetch, beacon, XHR, pixel)
- Automatic transport selection
- Retry logic with exponential backoff
- Request/response hooks
- Timeout handling
- Event emission
// Only send events when consent is granted
sdk.on('queue:flush', async ({ items }) => {
if (sdk.consent.isGranted('analytics')) {
// Get context
const context = sdk.context.get();
// Send with context attached
await sdk.transport.send({
url: 'https://api.example.com/events',
method: 'POST',
data: {
events: items,
context: context
}
});
}
});
// Events are queued automatically
sdk.queue.add({ event: 'page_view' });
sdk.queue.add({ event: 'click' });
// When consent is granted, queue flushes
sdk.consent.grant('analytics');File: src/plugins/logger.ts
export const loggerPlugin: PluginFunction = (plugin, instance, config) => {
plugin.ns('logger');
plugin.defaults({
logger: {
enabled: true,
level: 'info',
},
});
plugin.expose({
debug(message: string) { /* ... */ },
info(message: string) { /* ... */ },
warn(message: string) { /* ... */ },
error(message: string) { /* ... */ },
});
instance.on('sdk:ready', () => {
console.log('SDK ready!');
});
};Demonstrates:
- Setting a namespace (
plugin.ns()) - Providing config defaults
- Exposing public methods
- Listening to lifecycle events
- Emitting custom events
File: src/plugins/analytics.ts
export const analyticsPlugin: PluginFunction = (plugin, instance, config) => {
plugin.ns('analytics');
plugin.expose({
track(eventName: string, properties?: any) {
eventQueue.push({ eventName, properties });
plugin.emit('analytics:tracked', { eventName });
},
flush: async () => {
// Send to server
}
});
instance.on('sdk:ready', () => {
startFlushTimer();
});
instance.on('sdk:destroy', () => {
clearInterval(flushTimer);
});
};Demonstrates:
- Event batching and auto-flush
- Timer management in lifecycle
- Emitting events for other plugins
- Async operations
File: src/plugins/monitor.ts
export const monitorPlugin: PluginFunction = (plugin, instance, config) => {
plugin.ns('monitor');
// Listen to all analytics events
instance.on('analytics:*', (data) => {
console.log('Analytics event:', data);
});
// Listen to specific events
instance.on('analytics:tracked', () => {
stats.trackedEvents++;
});
plugin.expose({
getStats() {
return stats;
}
});
};Demonstrates:
- Wildcard event subscriptions (
analytics:*) - Plugin-to-plugin communication
- Statistics collection
- Reading config from other plugins
============================================================
SDK Kit - Essential Plugins Demo
============================================================
✅ SDK is ready!
📦 Storage Plugin Demo
------------------------------------------------------------
localStorage user_id: user-123
sessionStorage session_id: session-456
cookie preference: { theme: 'dark', notifications: true }
🔒 Consent Plugin Demo
------------------------------------------------------------
Analytics consent: pending
Marketing consent: pending
✅ Granted: analytics, functional
Analytics consent: granted
Marketing consent: pending
🌍 Context Plugin Demo
------------------------------------------------------------
Page URL: http://localhost:3000/
Device type: desktop
Screen size: 1920x1080
Language: en-US
Timezone offset: -8
⏳ Poll Plugin Demo
------------------------------------------------------------
Waiting for storage to be ready...
Storage ready: ✅
🚀 Queue + Transport Demo
------------------------------------------------------------
Queue size: 3 events
📤 Flushing 3 events...
✅ Events sent successfully
🔗 Integration with Custom Plugins
------------------------------------------------------------
Custom analytics events tracked
⏱️ waitForConsent Demo
------------------------------------------------------------
Waiting for marketing consent...
User granted marketing consent
Marketing consent: granted ✅
📊 Monitoring Stats
------------------------------------------------------------
{ trackedEvents: 5, identifyCalls: 1, pageViews: 0, ... }
🧹 Cleanup
------------------------------------------------------------
Flushing remaining events...
Destroying SDK...
✅ Demo complete!
============================================================
- Plugins are pure functions that receive capabilities
- Each plugin sets a unique namespace
- Plugins can expose methods that become part of the SDK API
- Plugins communicate via events (decoupled)
- Storage - Universal storage abstraction
- Consent - GDPR/CCPA compliance helpers
- Context - Automatic context collection
- Poll - Wait for external conditions
- Queue - Event batching with persistence
- Transport - HTTP transport with retry
- Hierarchical config with dot-notation access
- User config wins over defaults
- Config is accessible to all plugins
- Can be updated at runtime
- Pub/sub pattern for loose coupling
- Wildcard subscriptions (e.g.,
analytics:*) - Lifecycle events (
sdk:init,sdk:ready,sdk:destroy) - Custom events for plugin communication
init()- Start the SDK, emit eventsdestroy()- Clean up resources, flush data- Plugins can hook into lifecycle via events
- Proper cleanup prevents memory leaks
Generic, battle-tested plugins that live in @prosdevlab/sdk-kit-plugins:
- Storage, Consent, Context, Poll, Queue, Transport
- Available to all SDK Kit users
- Maintained by the SDK Kit team
Domain-specific plugins in separate npm packages:
- Lytics-specific plugins (e.g.,
@prosdevlab/sdk-kit-plugin-lytics) - Your custom plugins
- Community plugins
import type { PluginFunction } from '@prosdevlab/sdk-kit';
export const myPlugin: PluginFunction = (plugin, instance, config) => {
// 1. Set namespace
plugin.ns('my.plugin');
// 2. Provide defaults
plugin.defaults({
my: { plugin: { setting: 'value' } }
});
// 3. Expose public API
plugin.expose({
myMethod() {
const setting = config.get('my.plugin.setting');
plugin.emit('my:event', { setting });
}
});
// 4. Listen to lifecycle
instance.on('sdk:ready', () => {
console.log('Plugin initialized');
});
instance.on('sdk:destroy', () => {
// Cleanup
});
};See the Plugin Development Guide for more details.
- Check out the Core Package README for full SDK API
- See the Plugins Package README for all essential plugins
- Read the Plugin Development Guide
- Build your own plugins following these patterns
- Check the specs for design documentation