zema_hive 0.1.0 copy "zema_hive: ^0.1.0" to clipboard
zema_hive: ^0.1.0 copied to clipboard

Zema schema validation integration for Hive. Type-safe local storage without TypeAdapters or code generation.

example/main.dart

import 'dart:io';

import 'package:hive_ce/hive_ce.dart';
import 'package:zema/zema.dart';
import 'package:zema_hive/zema_hive.dart';

// ---------------------------------------------------------------------------
// Schemas
// ---------------------------------------------------------------------------

final userSchemaV1 = z.object({
  'id': z.string(),
  'name': z.string().min(1),
  'email': z.string().email(),
});

final userSchemaV2 = z.object({
  'id': z.string(),
  'name': z.string().min(1),
  'email': z.string().email(),
  'role': z.string().withDefault('user'),
});

// ---------------------------------------------------------------------------
// Extension type — same runtime representation as Map<String, dynamic>
// ---------------------------------------------------------------------------

extension type User(Map<String, dynamic> _) {
  String get id => _['id'] as String;
  String get name => _['name'] as String;
  String get email => _['email'] as String;
  String get role => _['role'] as String;
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

Future<void> main() async {
  final dir = await Directory.systemTemp.createTemp('zema_hive_example_');
  Hive.init(dir.path);

  await _basicExample();
  await _extensionTypeExample();
  await _migrationExample();
  await _errorHandlingExample();

  await Hive.close();
  await dir.delete(recursive: true);
}

// --- Basic read / write ---

Future<void> _basicExample() async {
  print('\n--- basic ---');

  final box = await Hive.openBox('basic');
  final userBox = box.withZema(userSchemaV1);

  await userBox.put('alice', {
    'id': 'alice',
    'name': 'Alice',
    'email': 'alice@example.com',
  });

  final user = userBox.get('alice');
  print('name: ${user?['name']}'); // Alice
  print('email: ${user?['email']}'); // alice@example.com

  // Missing key returns null
  final missing = userBox.get('nobody');
  print('missing: $missing'); // null

  await box.close();
}

// --- Extension type ---

Future<void> _extensionTypeExample() async {
  print('\n--- extension type ---');

  // The schema produces Map<String, dynamic>. Wrap in the extension type
  // afterwards to get named field access without any runtime overhead.
  final box = await Hive.openBox('ext');
  final userBox = box.withZema(userSchemaV2);

  await userBox.put('bob', {
    'id': 'bob',
    'name': 'Bob',
    'email': 'bob@example.com',
    'role': 'admin',
  });

  final raw = userBox.get('bob');
  if (raw != null) {
    final user = User(raw);
    print('name: ${user.name}'); // Bob
    print('role: ${user.role}'); // admin
  }

  // Wrap all values on the way out
  for (final u in userBox.values.map(User.new)) {
    print('  user: ${u.name}');
  }

  await box.close();
}

// --- Schema migration ---

Future<void> _migrationExample() async {
  print('\n--- migration ---');

  final box = await Hive.openBox('migration');

  // Simulate data written with V1 schema (no 'role' field)
  await box.put('alice', {
    'id': 'alice',
    'name': 'Alice',
    'email': 'alice@example.com',
  });

  // Wrap with V2 schema + migration callback
  final userBox = box.withZema(
    userSchemaV2,
    migrate: (rawData) {
      // V1 -> V2: back-fill the 'role' field
      if (!rawData.containsKey('role')) {
        rawData['role'] = 'user';
      }
      return rawData;
    },
  );

  // get() detects validation failure, applies migration, writes back
  final user = userBox.get('alice');
  print('name: ${user?['name']}'); // Alice
  print('role: ${user?['role']}'); // user  ← migrated

  await box.close();
}

// --- Error handling with onParseError ---

Future<void> _errorHandlingExample() async {
  print('\n--- error handling ---');

  final box = await Hive.openBox('errors');

  // Write a document that intentionally fails validation
  await box.put('corrupt', {
    'id': 'corrupt',
    'name': '', // too short
    'email': 'not-an-email',
  });

  final userBox = box.withZema(
    userSchemaV1,
    onParseError: (key, rawData, issues) {
      // In production: Sentry.captureException(issues)
      print(
          '  parse error on "$key": ${issues.map((i) => i.message).join(', ')}');
      return {
        'id': key,
        'name': 'Unknown',
        'email': 'unknown@example.com',
      };
    },
  );

  final user = userBox.get('corrupt');
  print('fallback name: ${user?['name']}'); // Unknown

  // Attempting to write invalid data throws ZemaHiveException
  try {
    await userBox.put('bad', {
      'id': 'bad',
      'name': '',
      'email': 'not-valid',
    });
  } on ZemaHiveException catch (e) {
    print('write rejected: ${e.issues?.first.message}');
  }

  await box.close();
}
0
likes
160
points
71
downloads

Documentation

API reference

Publisher

verified publishermeragix.dev

Weekly Downloads

Zema schema validation integration for Hive. Type-safe local storage without TypeAdapters or code generation.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#hive #local-storage #validation #schema #zema

License

MIT (license)

Dependencies

hive_ce, zema

More

Packages that depend on zema_hive