Skip to content
loro-devPublic

About

FFI binding for loro

Topics

Resources

Stars

17 stars

Watchers

1 watching

Forks

Latest commit

 

History

52 Commits

Folders and files

Repository files navigation

Loro FFI

MIT License Version Rust

Foreign Function Interface (FFI) bindings for Loro, a high-performance CRDT library for building collaborative applications.

Overview

Loro FFI provides cross-language bindings for the Loro CRDT library, enabling developers to use Loro's powerful collaborative editing capabilities in various programming languages. Built with UniFFI, this library generates clean, idiomatic bindings for multiple target languages.

JSONPath tooling now includes subscribe_jsonpath, a lightweight notification hook that fires (with possible false positives) when changes might affect a query—ideal for debounced or throttled watchers.

Supported Languages

CHANGE LOG

You can find the updates here

Persisting pending imported updates

LoroDoc::export_pending_updates() exports remote operations that were imported but cannot yet be applied because causal dependencies are missing. These are different from local edits in an uncommitted transaction. Ordinary snapshot and update exports do not preserve these pending operations.

The Rust return type is Result<Option<Vec<u8>>, LoroEncodeError>; the UniFFI declaration is [Throws=LoroEncodeError] bytes? export_pending_updates(). Generated bindings return Swift Data? and Python typing.Optional[bytes]. None (native nil/None) means there are no pending operations; it is not an empty byte array. The method does not consume pending operations, commit local edits, or advance the applied version vector.

Encoding failures are thrown as LoroEncodeError, including InternalError. Swift receives LoroEncodeError.InternalError(message:); Python receives LoroEncodeError.InternalError. Both preserve the core's display message. An encoding error is distinct from an absent pending result.

To preserve a checkpoint, export both a snapshot and pending updates without allowing edits or imports between the exports, then save the pair atomically. If pending updates are absent, clear any previously stored pending bytes. If either export fails, keep the previous complete checkpoint. Restore by importing the snapshot and, when present, the pending bytes through the ordinary import method. The restored pending operations still wait for the actual missing dependencies; for example, importing B without its dependency A, saving and restoring, then importing A applies both A and B. Saving pending bytes does not establish that a document is synced or that a sync cursor can be advanced or history compacted.

The default dependency uses the published Loro 1.17.1 crate, which includes this API and the previously published loro-v1.16.2 fixes. The checked-in lockfile resolves the registry release, so validation needs no local core checkout or unpublished Git revision. The FFI package and generated language packages are versioned and published separately from the core.

To validate the locked dependency and generated bindings, run:

python3 scripts/verify-pending-updates.py

Add --offline to use cached Cargo dependencies. The script makes an isolated copy with cdylib output while preserving the default dependency and lockfile. It runs the Rust tests, generates Swift and Python bindings, and runs the Python recovery and error tests. On macOS with swiftc, it also runs the Swift recovery and error tests. Set CARGO_TARGET_DIR to reuse a build cache. The temporary copy and generated artifacts remain available for inspection at the printed path.

The dependency update preserves the legacy JSON export and state_vv signatures. Legacy JSON exports still return the JSON schema directly and panic on unreadable history. Use try_export_json_updates or try_export_json_updates_without_peer_compression to receive a typed LoroError instead. The binding also maps the current core decode and unreadable-history error variants without changing the existing error tags.

For development against a different core checkout, pass its workspace path as an optional argument. This changes the dependency only in the temporary copy and validates that local checkout instead of the checked-in core revision.

Build Binding

Read the UniFFI documentation and modify the uniffi.toml configuration in this repository as needed.

Add this repository as a git submodule for your bindings:

git submodule add https://github.com/loro-dev/loro-ffi.git loro-ffi
git add .gitmodules loro-ffi

Build language-specific wrapper layers around the current UniFFI binding results to provide a better developer experience, including but not limited to:

  • Callback Function Wrapping: Provide direct closure support for functions that require callback parameters, such as LoroDoc::subscribe(), UndoManager::set_on_push(), and EphemeralStore::subscribe().

  • Native Type Extensions: Implement LoroValueLike ContainerIdLike extensions for built-in types in target languages, enabling direct parameter usage:

    LoroMap.insert("key", "value");
    LoroMap.insert("key", 123);
    LoroMap.insert("key", true);
    LoroMap.insert("key", [1, 2, 3]);
    // ... and more
  • Unified Export Interface: Create a unified entry point for LoroDoc::export:

    class LoroDoc {
      function export(mode: ExportMode) {
          // Wraps:
          // exportSnapshot()
          // exportUpdates(from)
          // exportUpdatesInRange(spans)
          // exportShallowSnapshot(frontiers)
          // exportStateOnly(frontiers)
          // exportSnapshotAt(frontiers)
      }
    }
  • Unified Container Creation Interface: Provide unified entry points for creating child containers in LoroMap/LoroList/LoroMovableList:

    class LoroMap {
      function insertContainer(key: string, child: Container) {
          // Wraps:
          // insertListContainer
          // insertMapContainer
          // insertTextContainer
          // insertTreeContainer
          // insertMovableListContainer
          // insertCounterContainer
      }
      function getOrCreateContainer() {}
      // ... and more
    }
  • Standard Language Interfaces: Implement essential interfaces (equality, comparison, ordering, etc.) for necessary data types as required by target programming languages.

  • Additional Enhancements: And many other improvements to enhance usability and developer experience.

Community

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

FFI binding for loro

Topics

Resources

Stars

17 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages