Skip to Content
SDK Kit is in active development. APIs may change.
Core Concepts

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 events

instance - SDK Coordination

instance.on('sdk:ready', () => {}) // Listen to events instance.emit('custom:event', data) // Emit events

config - Read Configuration

config.get('storage.namespace') // Read value config.get('storage.backend', 'localStorage') // With default

Events

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 typed

Key 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

Last updated on