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