phive

Core runtime package for PHive.

Use this package to define model annotations, hook pipelines, generated adapter runtime support, generated router descriptors, and router-based storage flows.

What you get

  • @PHiveType and @PHiveAutoType — model annotation with explicit or registry-assigned typeId
  • @PHiveField, @PHivePrimaryKey, @PHiveRef
  • PTypeAdapter<T> runtime support
  • PHiveCtx and PHiveHook
  • PHiveActionException and PHiveActionBehavior
  • PHiveRouter, PHiveDynamicRouter, PHiveStaticRouter
  • PHiveRouterDescriptor and descriptor registration helpers
  • PHiveContainerHandle for parent-child containership

Install

dependencies:
  hive_ce: ^2.19.3
  phive: ^0.0.1

If you are in this monorepo:

dependencies:
  phive:
    path: ../phive

Quick Start

import 'package:phive/phive.dart';

@PHiveType(1)
class Session {
  @PHiveField(0)
  @PHivePrimaryKey(boxName: 'app_sessions')
  final String id;

  @PHiveField(1)
  final String token;

  const Session({required this.id, required this.token});
}

Then run phive_generator with build_runner to emit the adapter and router descriptor.

You can also opt into constructor-order field inference for simpler models:

@PHiveType(2, autoFields: true)
class AutoSession {
  final String id;
  final String token;

  const AutoSession(this.id, this.token);
}

Or skip the typeId entirely with @PHiveAutoType and let the generator assign it from phive_type_registry.json:

@PHiveAutoType()
class Note {
  @PHiveField(0)
  final String id;

  @PHiveField(1)
  final String body;

  const Note({required this.id, required this.body});
}

Run dart run phive_generator:assign_type_ids once after annotating, then build_runner as normal. See phive_generator for the full workflow.

Router Quick Use

Hive.registerAdapter(SessionAdapter());

final router = PHiveDynamicRouter()
  ..applyDescriptor(const SessionRouterDescriptor());

await router.store(const Session(id: '1', token: 'abc'));
final session = await router.get<Session>('1');

Manual registration remains available when you do not want generated descriptors.

Router Model

Generated descriptors embed literal storage names: the lowercase model name for primary stores, and __ref_Parent_Child for relationships using declared simple names. Minification does not change these literals. Override them with @PHivePrimaryKey(boxName: 'cards_v1') and @PHiveRef(Lesson, refBoxName: 'cards_by_lesson_v1') when names must survive class renames or distinguish same-named models. Backend normalization still applies.

For manual registration, pass boxName to register and refBoxName to createRef: their fallback still uses runtime type strings. Regenerate adapters on upgrade; formerly minified default stores may need application-owned recreation/repopulation or migration. PHive does not automatically migrate or delete them. Readable or opaque store names are identifiers, not a security boundary; serialization and encryption behavior are unchanged.

  • PHiveDynamicRouter uses runtime registration and LazyBox<T> for primary values so keyed reads can apply hook-driven exception behaviors.
  • PHiveStaticRouter uses one Hive CE BoxCollection with multiple named stores.
  • Both routers preserve PHive-generated adapter semantics and hook pipelines.

Use the dynamic router when the type set is flexible. Use the static router when a fixed set of types and refs should share one logical database, especially on web.

Updating relationships

Storing a child with the same primary key and a different parent moves its membership to the new parent's container. After store succeeds, the old container no longer returns that child, and deleting the old container or parent does not delete the moved child. Existing container handles remain usable.

Each store scans the affected relationship's existing reference lists, removes old memberships, and retains exactly one destination membership. This also repairs stale links when a primary key is reused after delete or clearType, without reading the old value or triggering its hooks. Untouched stale keys are not swept. Cost grows with the number and size of lists in each relationship.

Serialize mutations to the same logical store. Primary and reference writes are not an atomic transaction; storage errors propagate and can leave partial updates. delete and clearType still remove primary values only, and reads continue to tolerate missing children.

Metadata precedence

On generated reads, a field's persisted metadata takes precedence over global defaults, including an explicitly stored null. Missing field keys inherit global values. Whole-object hooks receive only global metadata. Regenerate adapters to apply this ordering; the version-2 header and field byte layout are unchanged.

Exception Behavior Model

PHive uses behavior-driven hook exceptions for read-side cleanup and fallback handling.

  • hooks throw PHiveActionException
  • the exception carries one or more PHiveActionBehavior values
  • routers execute those behaviors with storage context

This keeps hooks responsible for declaring value semantics and routers responsible for applying storage side effects.

PHive annotations expose two hook scopes:

  • hooks on @PHiveType and @PHiveAutoType are merged into each mapped field pipeline.
  • classHooks run once around the whole model instance during generated adapter read/write.
  • Hooked models write one versioned metadata header with global and perField sections before raw field values.
  • Global metadata is visible to whole-object hooks and field hooks, while per-field metadata remains scoped to the owning field.

Nullable Fields

Hooked fields keep their native Hive value types because metadata now lives in the record header instead of being encoded into field payloads. Nullable field types (int?, bool?, etc.) therefore round-trip without string coercion.

Notes

  • This package is runtime-only.
  • For ready-made hooks such as TTL and encryption, also add phive_barrel.
  • For adapter and descriptor generation, also add phive_generator.

Libraries

phive