npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-automerge-generated

v0.1.0

Published

Native Automerge bindings for React Native via UniFFI

Readme

react-native-automerge-generated

Native Automerge bindings for React Native via compiled Rust + UniFFI.

Replaces the WebAssembly-based Automerge backend with a native Rust implementation exposed through JSI, enabling:

  • Hermes support — no longer requires JSC for WebAssembly
  • Android support — WASM-based automerge only worked reliably on iOS
  • No JSC patches — eliminates the createArrayBuffer patch for JSCRuntime.cpp
  • No Metro hacks — no more slim→fullfat resolver redirects
  • Better performance — direct native calls via JSI instead of WASM interpretation

Architecture

App TypeScript
       │
@automerge/automerge/slim  ←  UseApi(nativeApi)
       │
useapi-adapter.ts  (type conversion + API bridge)
       │
src/generated/automerge.ts  (auto-generated by ubrn)
       │  JSI (C++)
cpp/generated/  (auto-generated by ubrn)
       │  FFI (C ABI)
uniffi_automerge Rust crate  (UniFFI 0.29)
       │
automerge core (v0.7.3)

Hand-written components:

  1. rust/ — UniFFI-annotated Rust wrapper (ported from automerge-swift)
  2. src/useapi-adapter.ts — bridges generated API to @automerge/automerge/slim

Everything else (C++, ObjC++, Kotlin, generated TypeScript) is auto-generated by uniffi-bindgen-react-native.

Installation

npm install react-native-automerge-generated
# or
yarn add react-native-automerge-generated

iOS

cd ios && pod install

Android

Gradle sync should pick up the native module automatically.

Usage

import { UseApi } from '@automerge/automerge/slim';
import { nativeApi } from 'react-native-automerge-generated';

// Initialize once at app startup
UseApi(nativeApi);

// Then use automerge normally
import * as Automerge from '@automerge/automerge/slim';

let doc = Automerge.init();
doc = Automerge.change(doc, d => {
  d.key = 'value';
});

const saved = Automerge.save(doc);
const loaded = Automerge.load(saved);

Building from Source

Prerequisites

  • Rust toolchain (1.89+): rustup install stable
  • iOS targets: rustup target add aarch64-apple-ios aarch64-apple-ios-sim
  • Android targets: rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
  • Android NDK (via Android Studio)
  • uniffi-bindgen-react-native: npm install (dev dependency)

Build

# iOS (builds universal static lib + generates bindings)
npm run build:ios

# Android (builds for all ABIs + generates bindings)
npm run build:android

# Check Rust compiles
cd rust && cargo check

Project Structure

rust/
  src/
    automerge.udl     UniFFI interface definition (50+ methods)
    doc.rs            Doc wrapper around automerge::AutoCommit
    sync_state.rs     SyncState wrapper
    scalar_value.rs   ScalarValue enum (String, Int, Uint, F64, etc.)
    value.rs          Value enum (Object | Scalar)
    patches.rs        Patch/PatchAction types
    obj_id.rs         ObjId custom type (ArrayBuffer)
    actor_id.rs       ActorId custom type (ArrayBuffer)
    change_hash.rs    ChangeHash custom type (ArrayBuffer)
    cursor.rs         Cursor custom type (ArrayBuffer)
    ...
  Cargo.toml
src/
  useapi-adapter.ts   UseApi bridge (NativeAutomerge + nativeApi)
  generated/
    automerge.ts      Auto-generated TypeScript bindings
    automerge-ffi.ts  Auto-generated FFI layer
  index.ts            Package entry point

Type Mapping

The adapter converts between the generated API types and what @automerge/automerge/slim expects:

| automerge/slim | Generated API | Conversion | |---|---|---| | ObjId (string "_root") | ArrayBuffer | base64 with o: prefix | | ChangeHash (hex string) | ArrayBuffer | hex encode/decode | | ActorId (hex string) | ArrayBuffer | hex encode/decode | | Cursor (string) | ArrayBuffer | base64 with c: prefix | | Uint8Array | Array<number> | Array.from() / new Uint8Array() | | JS primitives | ScalarValue tagged union | tag-based dispatch |

Current Limitations

  • Stubbed methods: unmark(), spans(), getBlock(), updateBlock(), saveBundle(), encodeChange() throw or return empty values. These are not needed for core automerge operations.
  • getChanges() returns concatenated bytes — the Rust encode_changes_since() returns all changes as a single byte array rather than individually split changes. This works for loadIncremental() / applyChanges() but callers expecting individual change objects should be aware.
  • decodeChange() returns partial data — the native implementation returns change metadata (hash, actor, timestamp, message, deps) but not the full ops array or seq/startOp fields.
  • No WASM fallback — this is a complete replacement, not a supplement.

Development Notes

Post-Build Hook

The build process includes a post-build script (scripts/add-useapi-export.sh) that automatically injects the nativeApi export into index.ts after ubrn generates the bindings. This is necessary because ubrn regenerates index.ts from a template on each build.

The export line:

export { nativeApi, NativeAutomerge, NativeSyncState } from './useapi-adapter';

This runs automatically after npm run build:ios or npm run build:android.

Testing

The package includes a comprehensive test suite with 40+ test cases covering all major Automerge operations.

# Install dependencies
npm install

# Build the native module first (required)
npm run build:ios  # or build:android

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm test -- --coverage

Test coverage includes:

  • Document creation, cloning, and manipulation
  • Map, List, Text, and Counter operations
  • Save/load persistence
  • Merge operations and conflict resolution
  • Change history and decodeChange()
  • Sync protocol
  • Complex nested structures

See tests/README.md for detailed test documentation.

License

Apache 2.0