crdt_socket_sync_dart_frog 0.1.0+1 copy "crdt_socket_sync_dart_frog: ^0.1.0+1" to clipboard
crdt_socket_sync_dart_frog: ^0.1.0+1 copied to clipboard

Dart Frog adapter for crdt_socket_sync - serve CRDT sync and relay WebSocket sessions from a Dart Frog backend.

CRDT Socket Sync — Dart Frog #

crdt_socket_sync_dart_frog_badge pub points pub likes codecov ci_badge License: MIT pub publisher

docs_badge

This package is one of the sync adapters for popular Dart server frameworks. It keeps the documents of the crdt_lf library in sync through a Dart Frog backend. New to CRDTs? Start from the crdt_lf documentation.

What it does #

crdt_socket_sync for Dart Frog.

It gives Dart Frog handlers, ready to return from a route, that serve crdt_socket_sync sessions: crdtSyncWebSocketHandler for the server–client mode and crdtRelayWebSocketHandler for the relay mode, plus a middleware provider for each host. Clients connect with the plain WebSocketClient / WebSocketRelayClient of crdt_socket_sync.

Installation #

dart pub add crdt_socket_sync_dart_frog

One library per communication mode, like crdt_socket_sync itself:

// Server–client mode: crdtSyncWebSocketHandler, crdtSyncHostProvider,
// DocumentSessionHost
import 'package:crdt_socket_sync_dart_frog/sync.dart';

// Relay mode: crdtRelayWebSocketHandler, crdtRelayHostProvider,
// RelaySessionHost
import 'package:crdt_socket_sync_dart_frog/relay.dart';

Quick start #

Relay mode #

// lib/src/host.dart — built once, shared by every request
import 'package:crdt_socket_sync/relay_server.dart';

final relayHost = RelaySessionHost(store: InMemoryRelayStore());
// routes/_middleware.dart
import 'package:crdt_socket_sync_dart_frog/relay.dart';
import 'package:dart_frog/dart_frog.dart';

import '../lib/src/host.dart';

Handler middleware(Handler handler) {
  return handler.use(crdtRelayHostProvider(relayHost));
}
// routes/relay.dart
import 'package:crdt_socket_sync_dart_frog/relay.dart';
import 'package:dart_frog/dart_frog.dart';

Future<Response> onRequest(RequestContext context) async {
  return crdtRelayWebSocketHandler(context.read<RelaySessionHost>())(context);
}

Clients connect with WebSocketRelayClient from crdt_socket_sync, pointed at ws://localhost:8080/relay.

Server–client mode #

// lib/src/host.dart
import 'package:crdt_socket_sync/server.dart';

final syncHost = DocumentSessionHost(
  serverRegistry: InMemoryCRDTServerRegistry(),
);
// routes/_middleware.dart
Handler middleware(Handler handler) {
  return handler.use(crdtSyncHostProvider(syncHost));
}
// routes/sync.dart
import 'package:crdt_socket_sync_dart_frog/sync.dart';
import 'package:dart_frog/dart_frog.dart';

Future<Response> onRequest(RequestContext context) async {
  return crdtSyncWebSocketHandler(context.read<DocumentSessionHost>())(context);
}

Use a persistent registry (PersistentServerRegistry with crdt_lf_hive, crdt_lf_sqlite, crdt_lf_drift) for anything that must survive a restart — example/ does, with Hive. A registry is opened asynchronously, so build it in Dart Frog's init() (see Lifecycle) and keep it in a top-level late final.

Authenticating before the upgrade #

The handler is a value: check the request first, and call the handler only when the check passes.

Future<Response> onRequest(RequestContext context) async {
  final token = context.request.headers['authorization'];
  if (!await isAuthorized(token)) {
    return Response(statusCode: HttpStatus.unauthorized);
  }
  return crdtSyncWebSocketHandler(context.read<DocumentSessionHost>())(context);
}

A rejected client gets a plain 401 and never reaches the protocol.

Lifecycle #

Build the host once, outside the request. A host per request would give every client its own empty room.

start() is optional: a host with no transport of its own starts on its first connection. Call it explicitly from a custom init method when you also want an orderly shutdown — Dart Frog has no shutdown hook, so signals are the place for it. init() runs once, before the handler tree is built; the custom entrypoint run() is called again on every hot reload, so a listener registered there would pile up:

// main.dart
import 'dart:io';

import 'package:dart_frog/dart_frog.dart';

Future<void> init(InternetAddress ip, int port) async {
  await relayHost.start();

  for (final signal in [ProcessSignal.sigint, ProcessSignal.sigterm]) {
    signal.watch().listen((_) async {
      await relayHost.dispose();
      exit(0);
    });
  }
}

dispose() closes the open sessions and, in server–client mode, the registry under them — for a durable registry that is where pending writes get flushed, so skipping it loses data. A RelayStore has no close: a durable one is yours to close after dispose().

Gotchas #

  • Handler is ambiguous. crdt_lf exports a CRDT Handler and dart_frog exports a request Handler. In a file that needs both, hide one: import 'package:crdt_lf/crdt_lf.dart' hide Handler;.
  • One plugin instance per host. A ServerSyncPlugin binds to the host it is given to, once; handing the same instance to a second host throws a LateInitializationError far from the line that caused it.
  • A refused connection is closed, not rejected. Once the response is a 101, there is no status code left to send. A host that is stopped or disposed closes the socket instead, which the client sees as its stream ending.

Examples #

Two runnable Dart Frog projects, one per mode, each with a custom init() that starts the host and a custom entrypoint:

  • example/ — server–client mode on /sync, documents kept in Hive through crdt_lf_hive, a token check in front of the route.
  • relay_example/ — relay mode on /relay, rooms kept in memory.
cd example   # or relay_example
dart_frog dev

The greyhound_markdown app also runs locally against a Dart Frog relay server: server_dart_frog/.

Roadmap #

A roadmap is available in the project page.

Apps #

Packages #

Other bricks of the crdt "system" are:

1
likes
160
points
336
downloads

Documentation

API reference

Publisher

verified publishermattiapispisa.it

Weekly Downloads

Dart Frog adapter for crdt_socket_sync - serve CRDT sync and relay WebSocket sessions from a Dart Frog backend.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#crdt #sync #websocket #dart-frog #server

License

MIT (license)

Dependencies

crdt_socket_sync, dart_frog, dart_frog_web_socket

More

Packages that depend on crdt_socket_sync_dart_frog