regent 0.8.0 copy "regent: ^0.8.0" to clipboard
regent: ^0.8.0 copied to clipboard

Optimistic, message-driven state engine: a journal of sealed facts folded into keyed stores and units, with guards, correlation, and merge edges. Pure Dart.

example/example.dart

// The classic todo app, regent-style — compare it with the version you
// already know. Three things to notice:
//
//  1. Messages are FACTS (`TodoAdded`), never calls. Stores fold them.
//  2. The toggle is OPTIMISTIC with no wire ids: the request itself is the
//     prediction (one dispatch sends and folds); the server's echo settles
//     it by state comparison, and silence reverts it — automatically.
//  3. The queue is positional: the veto row above the store drops duplicate
//     adds before the store ever sees them.
import 'package:regent/regent.dart';

// ── The facts ──
sealed class TodoMsg extends Msg {
  const TodoMsg();
}

class TodoAdded extends TodoMsg {
  const TodoAdded(this.id, this.title);
  final String id;
  final String title;
}

/// The intent AND the prediction: dispatching it folds instantly and tells
/// the transport to send — one dispatch, both jobs. Note it states the
/// TARGET (`done: true`), not the operation ("toggle"): verdicts settle by
/// comparing state, so facts should be absolute — re-applying an absolute
/// fact is a no-op, re-applying a toggle never is.
class CompleteTodo extends TodoMsg {
  const CompleteTodo(this.id, {required this.done});
  final String id;
  final bool done;
}

/// The server's echo — the resolver that settles the prediction.
class TodoToggled extends TodoMsg {
  const TodoToggled(this.id, {required this.done});
  final String id;
  final bool done;
}

// ── The state ──
class Todo with Identifiable<String> {
  const Todo(this.id, this.title, {this.done = false});
  @override
  final String id;
  final String title;
  final bool done;

  Todo completed(bool done) => Todo(id, title, done: done);

  // Verdicts settle by STATE COMPARISON — value equality is the contract.
  @override
  bool operator ==(Object o) =>
      o is Todo && o.id == id && o.title == title && o.done == done;
  @override
  int get hashCode => Object.hash(id, title, done);
}

// ── The store: a pure fold, optimism declared in one line ──
final class ToggleVerdict extends Verdict<CompleteTodo, TodoToggled> {
  const ToggleVerdict();
  @override
  Duration get deadline => const Duration(seconds: 3);
}

final class Todos extends Store<String, Todo, TodoMsg> {
  const Todos() : super(verdict: const ToggleVerdict());

  @override
  IdentifiableMap<String, Todo> reduce(
          IdentifiableMap<String, Todo> todos, TodoMsg msg) =>
      switch (msg) {
        TodoAdded(:final id, :final title) => todos.upsert(Todo(id, title)),
        CompleteTodo(:final id, :final done) ||
        TodoToggled(:final id, :final done) =>
          todos.updateById(id, (t) => t.completed(done)),
      };
}

void main() async {
  final ledger = Ledger();

  // Row order is semantics: the veto stands ABOVE the store it protects.
  late final StoreMemory<String, Todo, TodoMsg> todos;
  ledger.veto<TodoAdded>((msg) => todos[msg.id] != null); // duplicates drop
  todos = ledger.store(const Todos());

  // An effect: the transport lives OUTSIDE the fold, listening post-queue.
  ledger.on<CompleteTodo>().listen((msg) async {
    // fake server: echoes the first write, loses the second
    if (msg.id == 'milk') {
      await Future<void>.delayed(const Duration(milliseconds: 100));
      ledger.dispatch(TodoToggled(msg.id, done: msg.done));
    }
  });

  ledger.dispatch(const TodoAdded('milk', 'Buy milk'));
  ledger.dispatch(const TodoAdded('milk', 'Buy milk')); // vetoed — a no-op
  ledger.dispatch(const TodoAdded('tea', 'Brew tea'));

  // Optimistic write: done flips NOW, `pending` until the echo settles it.
  ledger.dispatch(const CompleteTodo('milk', done: true));
  print('milk done=${todos['milk']!.done} '
      '(${todos.flagsOf('milk')?.stability})'); // true (pending)

  await Future<void>.delayed(const Duration(milliseconds: 200));
  print('milk done=${todos['milk']!.done} '
      '(${todos.flagsOf('milk')?.stability})'); // true (confirmed)

  // The server never answers this one — the deadline reverts it, no code.
  ledger.dispatch(const CompleteTodo('tea', done: true));
  print('tea done=${todos['tea']!.done}'); // true, hopeful
  await Future<void>.delayed(const Duration(seconds: 4));
  print('tea done=${todos['tea']!.done} '
      '(${todos.flagsOf('tea')?.stability})'); // false (reverted)

  ledger.close();
}
1
likes
0
points
1.16k
downloads

Publisher

unverified uploader

Weekly Downloads

Optimistic, message-driven state engine: a journal of sealed facts folded into keyed stores and units, with guards, correlation, and merge edges. Pure Dart.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

identifiable, meta

More

Packages that depend on regent