Plugins API
Extend Storken functionality with plugins for persistence, logging, middleware, and more.
Creating Plugins
Plugins are objects that implement the Storken plugin interface.
interface StorkenPlugin<T = any> {
name: string
init?: (storken: Storken<T>) => void | Promise<void>
beforeGet?: (key: string) => void | Promise<void>
afterGet?: (key: string, value: T) => void | Promise<void>
beforeSet?: (key: string, value: T) => void | Promise<void>
afterSet?: (key: string, value: T) => void | Promise<void>
beforeReset?: (key: string) => void | Promise<void>
afterReset?: (key: string) => void | Promise<void>
destroy?: () => void | Promise<void>
}
Using Plugins
import { create } from 'storken'
const [useStorken] = create({
initialValues: {
theme: 'light',
user: null
},
plugins: {
logger: loggerPlugin,
persist: persistPlugin,
analytics: analyticsPlugin
}
})
// Access plugins in components
function Component() {
const [value, , , , , plugins] = useStorken('theme')
// Use plugin methods
plugins.logger.log('Theme accessed')
plugins.persist.save()
}
Built-in Plugins
Persistence Plugin
Automatically persist state to localStorage or sessionStorage.
import { createPersistPlugin } from 'storken/plugins'
const persistPlugin = createPersistPlugin({
storage: localStorage, // or sessionStorage
prefix: 'app_',
serialize: JSON.stringify,
deserialize: JSON.parse,
debounce: 500
})
const [useStorken] = create({
plugins: {
persist: persistPlugin
}
})
Logger Plugin
Log all state changes for debugging.
const loggerPlugin = {
name: 'logger',
afterSet(key, value) {
console.log(`[Storken] Set ${key}:`, value)
},
afterGet(key, value) {
console.log(`[Storken] Get ${key}:`, value)
},
afterReset(key) {
console.log(`[Storken] Reset ${key}`)
}
}
Validation Plugin
Validate state changes before they're applied.
const validationPlugin = {
name: 'validation',
beforeSet(key, value) {
// Example: Validate email format
if (key === 'email' && !isValidEmail(value)) {
throw new Error('Invalid email format')
}
// Example: Validate number range
if (key === 'age' && (value < 0 || value > 150)) {
throw new Error('Age must be between 0 and 150')
}
}
}
Custom Plugin Examples
Analytics Plugin
const analyticsPlugin = {
name: 'analytics',
init(storken) {
// Initialize analytics SDK
this.analytics = window.analytics
},
afterSet(key, value) {
// Track state changes
this.analytics?.track('State Changed', {
key,
value,
timestamp: Date.now()
})
},
afterGet(key) {
// Track state access patterns
this.analytics?.track('State Accessed', {
key,
timestamp: Date.now()
})
}
}
Sync Plugin
const syncPlugin = {
name: 'sync',
init(storken) {
// Connect to WebSocket
this.ws = new WebSocket('wss://api.example.com/sync')
this.ws.onmessage = (event) => {
const { key, value } = JSON.parse(event.data)
storken.set(key, value)
}
},
afterSet(key, value) {
// Sync changes to server
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ key, value }))
}
},
destroy() {
// Cleanup
this.ws?.close()
}
}
History Plugin
const historyPlugin = {
name: 'history',
history: new Map(),
maxHistorySize: 50,
afterSet(key, value) {
if (!this.history.has(key)) {
this.history.set(key, [])
}
const keyHistory = this.history.get(key)
keyHistory.push({
value,
timestamp: Date.now()
})
// Limit history size
if (keyHistory.length > this.maxHistorySize) {
keyHistory.shift()
}
},
getHistory(key) {
return this.history.get(key) || []
},
undo(key, storken) {
const keyHistory = this.history.get(key)
if (keyHistory && keyHistory.length > 1) {
keyHistory.pop() // Remove current
const previous = keyHistory[keyHistory.length - 1]
storken.set(key, previous.value)
}
}
}
Plugin Composition
Combine multiple plugins for powerful functionality.
const [useStorken] = create({
initialValues: {
user: null,
preferences: {},
cart: []
},
plugins: {
// Persist user preferences
persist: createPersistPlugin({
keys: ['preferences'],
storage: localStorage
}),
// Log in development
logger: process.env.NODE_ENV === 'development'
? loggerPlugin
: null,
// Validate cart items
validation: {
name: 'cart-validation',
beforeSet(key, value) {
if (key === 'cart') {
value.forEach(item => {
if (item.quantity < 0) {
throw new Error('Invalid quantity')
}
})
}
}
},
// Track analytics
analytics: analyticsPlugin
}
})
💡 Plugin Best Practices
- • Keep plugins focused on a single responsibility
- • Handle errors gracefully in plugin methods
- • Clean up resources in the destroy method
- • Use async methods for I/O operations
- • Document plugin configuration options
- • Test plugins independently from your app logic