Core Concepts
SDK Kit’s architecture is built on four core concepts: Plugins, Capabilities, Events, and Configuration. Understanding these will help you build powerful, maintainable SDKs.
Plugins Are Functions
Plugins are pure functions that extend the SDK:
function myPlugin(
plugin: Plugin, // Plugin capabilities
instance: SDK, // SDK instance
config: Config // Configuration
): void {
plugin.ns('my.plugin')
plugin.expose({ myMethod() {} })
instance.on('sdk:ready', () => {})
}Why functions? They have explicit dependencies, support tree-shaking, and are easier to test than classes.
Capabilities
Plugins receive three objects with different responsibilities:
plugin - Your Plugin’s Capabilities
plugin.ns('storage') // Set namespace
plugin.defaults({ storage: {} }) // Default config
plugin.expose({ storage: {} }) // Public API
plugin.emit('storage:ready', {}) // Emit eventsinstance - SDK Coordination
instance.on('sdk:ready', () => {}) // Listen to events
instance.emit('custom:event', data) // Emit eventsconfig - Read Configuration
config.get('storage.namespace') // Read value
config.get('storage.backend', 'localStorage') // With defaultEvents
Plugins coordinate through events, not direct calls. This keeps them loosely coupled.
Basic Events
// Emit
plugin.emit('storage:set', { key, value })
// Listen
sdk.on('storage:set', (event, data) => {
console.log(data)
})
// Unsubscribe
const unsub = sdk.on('storage:set', handler)
unsub()Wildcard Patterns
// All storage events
sdk.on('storage:*', (event, data) => {})
// All 'ready' events
sdk.on('*:ready', () => {})Lifecycle Events
sdk.on('sdk:init', () => {}) // On init()
sdk.on('sdk:ready', () => {}) // After init completes
sdk.on('sdk:destroy', () => {}) // On destroy()Plugin Order: Register plugins in dependency order. If B depends on A, register A first.
Configuration
User config always wins. Plugin defaults only fill missing values.
await sdk.init({
storage: { namespace: 'myapp' } // User value
})
plugin.defaults({
storage: {
namespace: 'default', // Ignored (user set 'myapp')
backend: 'localStorage' // Added (user didn't set)
}
})Access with dot-notation:
config.get('storage.namespace')
config.get('nested.value', 'defaultValue')Type Safety
Extend the SDK interface for full type checking:
import type { SDK } from '@lytics/sdk-kit'
interface MySDK extends SDK {
storage: {
set(key: string, value: unknown): void
get(key: string): unknown
}
}
const sdk = new SDK() as MySDK
sdk.use(storagePlugin)
sdk.storage.set('key', 'value') // Fully typedKey Principles
Explicit Dependencies
Plugins receive what they need as parameters. No hidden globals.
Event-Driven
Plugins coordinate through events, keeping them loosely coupled.
Tree-Shakeable
Only the plugins you use end up in your bundle.
Zero Dependencies
The core SDK has no runtime dependencies. ~4KB gzipped.
What’s Next?
Build Your First SDK
Start with Getting Started to compose your first SDK from plugins
Build Custom Plugins
Read Plugin Guide to create plugins tailored to your needs