hivehook 1.0.0-rc.0 copy "hivehook: ^1.0.0-rc.0" to clipboard
hivehook: ^1.0.0-rc.0 copied to clipboard

A powerful plugin system for Hive that adds hooks, lifecycle management, and middleware capabilities with built-in TTL, LRU caching, validation, and custom transformations.

HiveHook #

A Hive CE storage adapter with hook-based middleware powered by hihook.

About this library #

  • 🔌 Hook-based middleware - Transform, validate, or intercept storage operations
  • 🔒 Meta hooks - Separate pipeline for metadata (encryption, TTL, audit)
  • 🏠 Environment isolation - Multiple envs share storage safely via key prefixing
  • Meta-first pattern - Check TTL/permissions before decrypting values
  • 📦 Lazy initialization - BoxCollections open on first access

This is how you use it:

final hive = await HHive.create('myapp');

await hive.put('user:1', {'name': 'Alice', 'role': 'admin'});
final user = await hive.get('user:1');
await hive.delete('user:1');

// With metadata
await hive.put('session', token, meta: {'expiresAt': timestamp});
final record = await hive.getWithMeta('session');
print(record.value);  // token
print(record.meta);   // {expiresAt: ...}

Table of Contents #

Getting Started #

Step 1: Add dependencies

dependencies:
  hivehook: ^1.0.0-alpha.1
  hive_ce: ^2.19.1

Step 2: Register and initialize

import 'package:hivehook/hivehook.dart';

void main() async {
  // Register environment(s)
  HHiveCore.register(HiveConfig(
    env: 'myapp',
    withMeta: true,  // Enable metadata storage
  ));

  // Initialize Hive
  await HHiveCore.initialize();

  // Create instance and use
  final hive = await HHive.create('myapp');
  await hive.put('key', 'value');
}

Step 3: Use it

// Basic CRUD
await hive.put('users/1', {'name': 'Alice'});
final user = await hive.get('users/1');
await hive.delete('users/1');
await hive.clear();

// With metadata
await hive.put('config', data, meta: {'version': 2});
final record = await hive.getWithMeta('config');

// Cache-aside pattern
final data = await hive.ifNotCached('expensive', () async {
  return await fetchFromApi();
});

Core API #

Basic Operations #

await hive.put(key, value);           // Store value
await hive.get(key);                  // Retrieve value
await hive.delete(key);               // Delete entry
await hive.clear();                   // Clear all entries in this env

Metadata Operations #

// Store with metadata
await hive.put('key', value, meta: {'ttl': 300, 'source': 'api'});

// Retrieve value + metadata together
final record = await hive.getWithMeta('key');
print(record.value);  // the value
print(record.meta);   // {ttl: 300, source: 'api'}

// Standalone metadata operations
final meta = await hive.getMeta('key');
await hive.putMeta('key', {'views': 100});
await hive.deleteMeta('key');

Cache-Aside Pattern #

// Only calls factory if key doesn't exist
final data = await hive.ifNotCached('users/list', () async {
  return await api.fetchUsers();
});

Hooks #

Hooks intercept storage operations to transform, validate, or log data.

Value Hooks #

Value hooks handle the main data operations: read, write, delete, clear.

final hive = await HHive.createFromConfig(HiveConfig(
  env: 'orders',
  hooks: [
    // Auto-calculate totals on write
    HiHook(
      uid: 'tax_calculator',
      events: ['write'],
      handler: (payload, ctx) {
        final value = payload.value as Map<String, dynamic>?;
        if (value != null && value.containsKey('amount')) {
          final amount = value['amount'] as num;
          final transformed = Map<String, dynamic>.from(value);
          transformed['tax'] = (amount * 0.1).toDouble();
          transformed['total'] = (amount * 1.1).toDouble();
          return HiContinue(payload: payload.copyWith(value: transformed));
        }
        return const HiContinue();
      },
    ),
  ],
));

await hive.put('order/1', {'amount': 100.0});
final order = await hive.get('order/1');
// order = {amount: 100.0, tax: 10.0, total: 110.0}

Meta Hooks #

Meta hooks handle metadata operations: readMeta, writeMeta, deleteMeta, clearMeta.

They run in a separate pipeline, enabling patterns like:

  • Encrypt/decrypt sensitive metadata
  • Check TTL before reading the value (meta-first pattern)
  • Audit trail for metadata access
final hive = await HHive.createFromConfig(HiveConfig(
  env: 'secure',
  withMeta: true,
  metaHooks: [
    // Encrypt API keys in metadata
    HiHook(
      uid: 'meta_encryptor',
      events: ['writeMeta'],
      handler: (payload, ctx) {
        final meta = payload.value as Map<String, dynamic>?;
        if (meta != null && meta.containsKey('apiKey')) {
          final encrypted = Map<String, dynamic>.from(meta);
          encrypted['apiKey'] = base64Encode(utf8.encode(meta['apiKey']));
          encrypted['_encrypted'] = true;
          return HiContinue(payload: payload.copyWith(value: encrypted));
        }
        return const HiContinue();
      },
    ),
    // Decrypt on read
    HiHook(
      uid: 'meta_decryptor',
      events: ['readMeta'],
      handler: (payload, ctx) {
        final meta = payload.value as Map<String, dynamic>?;
        if (meta?['_encrypted'] == true) {
          final decrypted = Map<String, dynamic>.from(meta!);
          decrypted['apiKey'] = utf8.decode(base64Decode(meta['apiKey']));
          decrypted.remove('_encrypted');
          return HiContinue(payload: payload.copyWith(value: decrypted));
        }
        return const HiContinue();
      },
    ),
  ],
));

Hook Results #

Hooks return one of two results:

Result Effect
HiContinue() Proceed to next hook (optionally with modified payload)
HiBreak(returnValue) Stop pipeline immediately and return value

Validation example using HiBreak:

HiHook(
  uid: 'validator',
  events: ['write'],
  handler: (payload, ctx) {
    final value = payload.value as Map<String, dynamic>?;
    final email = value?['email'] as String?;
    if (email != null && !email.contains('@')) {
      // Block the write operation
      return HiBreak(returnValue: null);
    }
    return const HiContinue();
  },
)

Hook Priority #

Hooks execute in priority order (higher first):

hooks: [
  HiHook(uid: 'first', priority: 100, ...),   // Runs first
  HiHook(uid: 'second', priority: 50, ...),   // Runs second
  HiHook(uid: 'third', priority: 10, ...),    // Runs third
]

Advanced Features #

Environment Isolation #

Multiple environments can share the same storage file while remaining completely isolated. Keys are prefixed internally with {env}::.

// Register multiple environments
HHiveCore.register(HiveConfig(env: 'v1', boxName: 'data'));
HHiveCore.register(HiveConfig(env: 'v2', boxName: 'data'));
await HHiveCore.initialize();

final v1 = await HHive.create('v1');
final v2 = await HHive.create('v2');

await v1.put('config', 'old');  // Stored as 'v1::config'
await v2.put('config', 'new');  // Stored as 'v2::config'

print(await v1.get('config'));  // 'old'
print(await v2.get('config'));  // 'new'

await v1.clear();  // Only clears v1 keys, v2 unaffected

TTL & Caching #

Implement time-to-live with metadata:

// Store with TTL
Future<void> putWithTtl(String key, dynamic value, int ttlSeconds) async {
  await hive.put(key, value, meta: {
    'createdAt': DateTime.now().millisecondsSinceEpoch,
    'ttlSeconds': ttlSeconds,
  });
}

// Check if expired
Future<T?> getIfValid<T>(String key) async {
  final record = await hive.getWithMeta(key);
  if (record.value == null || record.meta == null) return null;
  
  final createdAt = record.meta!['createdAt'] as int;
  final ttl = record.meta!['ttlSeconds'] as int;
  final elapsed = DateTime.now().millisecondsSinceEpoch - createdAt;
  
  if (elapsed > ttl * 1000) {
    await hive.delete(key);  // Clean up expired
    return null;
  }
  return record.value as T;
}

// Cache-aside with TTL
Future<T> getCachedOrFetch<T>(
  String key,
  Future<T> Function() fetcher, {
  int ttlSeconds = 60,
}) async {
  final cached = await getIfValid<T>(key);
  if (cached != null) return cached;
  
  final fresh = await fetcher();
  await putWithTtl(key, fresh, ttlSeconds);
  return fresh;
}

Metadata Encryption #

Use meta hooks for transparent encryption:

final hive = await HHive.createFromConfig(HiveConfig(
  env: 'secure',
  withMeta: true,
  metaHooks: [
    HiHook(
      uid: 'encrypt',
      events: ['writeMeta'],
      handler: (payload, ctx) {
        final meta = payload.value as Map<String, dynamic>?;
        if (meta == null) return const HiContinue();
        
        final encrypted = yourEncryptFunction(jsonEncode(meta));
        return HiContinue(
          payload: payload.copyWith(value: {'_e': encrypted}),
        );
      },
    ),
    HiHook(
      uid: 'decrypt',
      events: ['readMeta'],
      handler: (payload, ctx) {
        final meta = payload.value as Map<String, dynamic>?;
        if (meta == null || !meta.containsKey('_e')) {
          return const HiContinue();
        }
        
        final decrypted = jsonDecode(yourDecryptFunction(meta['_e']));
        return HiContinue(
          payload: payload.copyWith(value: decrypted),
        );
      },
    ),
  ],
));

Configuration #

HiveConfig Options #

Option Type Default Description
env String required Unique environment identifier
boxName String? env Physical box name (allows sharing)
boxCollectionName String 'hivehooks' BoxCollection name
type HiveBoxType boxCollection Storage type (see below)
withMeta bool true Enable metadata storage
hooks List<HiHook> [] Value operation hooks
metaHooks List<HiHook> [] Metadata operation hooks

Storage Types #

Type Description
HiveBoxType.boxCollection Uses BoxCollection (default). All boxes must be registered before initialize().
HiveBoxType.box Uses individual Box. Opens lazily, ideal for dynamic scenarios.
// BoxCollection (default) - register all before init
HHiveCore.register(HiveConfig(env: 'users'));
HHiveCore.register(HiveConfig(env: 'orders'));
await HHiveCore.initialize();

// Individual Box - can register anytime, opens lazily
final hive = await HHive.createFromConfig(HiveConfig(
  env: 'temp_${DateTime.now().millisecondsSinceEpoch}',
  type: HiveBoxType.box,
));

BoxCollectionConfig #

Optionally pre-configure collections with custom settings:

// Pre-configure collection (before any HiveConfig registration)
HHiveCore.registerCollection(BoxCollectionConfig(
  name: 'myapp',
  storagePath: '/custom/path',      // Overrides global path
  encryptionCipher: myCipher,       // Overrides global cipher
  includeMeta: true,                // Force meta box inclusion
));

// HiveConfigs reference the collection
HHiveCore.register(HiveConfig(env: 'users', boxCollectionName: 'myapp'));
HHiveCore.register(HiveConfig(env: 'orders', boxCollectionName: 'myapp'));
Option Type Default Description
name String required Collection identifier
storagePath String? global Storage path override
encryptionCipher HiveCipher? global Encryption cipher override
includeMeta bool? auto-detect null=auto, true=force, false=forbid

Initialization Options #

// Custom storage path (passed to initialize)
await HHiveCore.initialize(path: '/custom/path');

// Or set globally before initialize
HHiveCore.storagePath = '/custom/path';
await HHiveCore.initialize();

Creating Instances #

// From registered config
final hive = await HHive.create('myapp');

// With inline config (auto-registers)
final hive = await HHive.createFromConfig(HiveConfig(
  env: 'orders',
  withMeta: true,
  hooks: [...],
));

Learn More #

  • hihook - The hook engine powering HiveHook
  • Hive CE - The storage backend
1
likes
150
points
407
downloads

Publisher

verified publisherpathverse.ca

Weekly Downloads

A powerful plugin system for Hive that adds hooks, lifecycle management, and middleware capabilities with built-in TTL, LRU caching, validation, and custom transformations.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

hihook, hive_ce

More

Packages that depend on hivehook