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

@rustra/react-native

v0.9.2

Published

React Native adapter for rustra-bridge

Readme

English | 한국어

@rustra/react-native

Rustra's React Native JSI adapter and shared native sources for generated modules. Expo and bare React Native use the same generated entry and the standard autolinking path.

Recommended usage

The Rust crate exposes a static library and a mobile entry.

[lib]
crate-type = ["rlib", "staticlib"]
pub fn package() -> rustra::Package {
    // commands...
}

rustra::native_entry!(package);

Enable React Native generation in the app's rustra.json.

{
  "schema": "./generated/schema.json",
  "output": "./generated",
  "codegen": {
    "rustManifest": "./Cargo.toml",
    "rustBinary": "generate"
  },
  "reactNative": {}
}
bun add @rustra/react-native @rustra/types
bun add -d @rustra/cli
bunx --bun @rustra/cli doctor --config rustra.json
bunx --bun @rustra/cli codegen --config rustra.json
bun install

The generator handles the following automatically:

  • Generates an app-specific @rustra/generated-react-native package under modules/rustra-bridge
  • Links a Bun workspace dependency into the app package.json after checking for conflicts
  • Generates the iOS Podspec, Android Gradle/CMake/JNI, and the shared C++ JSI bridge
  • Infers the package and static library names from Cargo metadata
  • Generates the TypeScript/C++ codec and lazy bootstrap

The app only imports generated commands.

import { addNumbers } from './generated/react-native';

const result = await addNumbers({ a: 20, b: 22 });

The first call performs JSI installation, contract hash/schema version verification, and fast engine setup exactly once, concurrency-safe. Separate installRustraJSI(), createFastEngine(), and configure() calls are not needed.

Expo and bare React Native

  • bare RN: verify autolinking on both platforms with bunx --bun react-native config, and on iOS run cd ios && pod install before rebuilding the app.
  • Expo: use a development build or expo run:*. Expo Go cannot load JSI native code.
  • Neither environment requires editing the Podfile, settings.gradle, MainApplication, or CMake by hand.

reactNative: {} is fully automatic when the nearest Cargo.toml points at a single staticlib crate. If the monorepo workspace is ambiguous, specify the app crate.

{
  "reactNative": {
    "rustManifest": "../native/Cargo.toml"
  }
}

Only when multiple staticlibs remain even then is rustPackage needed. Override moduleDir, rustLibrary, and cppOutput only for unusual layouts.

Collision avoidance

Generated modules deliberately use fixed, dedicated names.

  • JavaScript package: @rustra/generated-react-native
  • React Native module: RustraBridge
  • Android namespace: dev.rustra.bridge
  • native shared library: rustra_bridge

If the same dependency would point at a different location, the generator stops instead of overwriting. Keep exactly one locally generated package per app so that names and build targets do not collide with other Expo/Nitro/Turbo modules.

Dev hot core (dylib hot swap)

In dev you can swap the Rust core dylib without rebuilding or remounting the app. The shared C++ bridge routes every FFI call through a function-pointer table, so a swap is just an atomic table replacement — JS bindings stay untouched.

  • Publishing: rustra dev with dev.target: "dylib" builds the cdylib and, only after the parity gate passes, atomically publishes <stem>-hot-live<ext> (temp + rename). The gate rejected a build, the previous live artifact stays and the app keeps the old core (fail-closed).
  • iOS (simulator only): set the RUSTRA_HOT_CORE_DIR environment variable to a directory. On install the adapter polls that directory every 300 ms. Note the naming contract: RUSTRA_HOT_CORE_DIR (RN) is a directory, while the Tauri-side RUSTRA_HOT_CORE is a file path — they are different variables.
  • Android: the polling directory is <filesDir>/rustra/hot; the generated JNI glue enables the same polling there — debuggable builds only. The template calls nativeConfigureHotCore inside an ApplicationInfo.FLAG_DEBUGGABLE check, so a release build never starts the watch thread (and never gains an appdata dlopen surface — the gate is silent by design).
  • Delivery: never overwrite the dylib in place. Push or copy it as a temp file and rename it into place (the CLI publish already does this). Overwriting a dylib that a process has mapped kills the process — SIGKILL on iOS/macOS (code signature tamper), SIGSEGV on Android (repaged modified file).

Each swap drops in-process core state (channels, event context) and re-registers the event sink on the new core; old cores are intentionally never unloaded. getRustraNative().hotCoreStatus() returns the last swap's old/new contract hashes (or the last error) in dev, and null in the default static build.

Swap polling has a retry cap: artifact bytes (sha256) that fail 5 swaps in a row are poisoned and skipped until new bytes are published — new bytes always get a fresh retry window, and the Rust-side watcher (spawn_dylib_watch) enforces the same per-bytes limit. The last failure stays observable through hotCoreStatus().error. Channel handles are routed through the core that issued them, so a channel call can never land on the wrong core after a swap.

Low-level API

Use createReactNativeEngine, createFastEngine, and getRustraNative only when you need direct control over the transport. The JSON path works with an exact ArrayBuffer regardless of whether Hermes provides TextEncoder/TextDecoder, and the fast path uses the generated postcard codec and caller-buffer FFI.