UNPKG

signalforge

Version:

Fine-grained reactive state management with automatic dependency tracking - Ultra-optimized, zero dependencies

268 lines (251 loc) 8.18 kB
/** * NativeSignalForge - TurboModule Specification * * This file defines the TurboModule interface for SignalForge. * TurboModules are the new architecture for React Native native modules, * providing better type safety and performance through code generation. * * Note: While our JSI implementation provides direct function bindings, * this TurboModule spec serves as: * 1. A formal interface definition for tooling/documentation * 2. An alternative integration path for projects using TurboModules * 3. A bridge for future React Native features that require TurboModules * * The JSI implementation (jsiStore.cpp) is more performant for signal * operations as it avoids the TurboModule bridge overhead. */ import { TurboModule, TurboModuleRegistry } from 'react-native'; // Ensure React Native codegen sees a direct TurboModuleRegistry usage. // This must remain a literal call inside a function body (no indirection). export function __ensureTurboModuleLoaded__(): void { TurboModuleRegistry.get<Spec>('NativeSignalForge'); } /** * TurboModule Interface Specification * * These methods mirror the JSI bindings but are accessed through * the TurboModule system instead of direct global function calls. * * Code generation will create native bindings from this specification. */ export interface Spec extends TurboModule { /** * Create a new signal with an initial value * * @param initialValue - The initial value (any serializable type) * @returns Unique signal identifier * * Native implementation: * - Allocates Signal in C++ memory (shared_ptr) * - Stores in thread-safe unordered_map * - Returns generated unique ID */ createSignal(initialValue: Object): string; /** * Get the current value of a signal * * @param signalId - Unique signal identifier * @returns Current signal value * @throws Error if signal doesn't exist * * Native implementation: * - Looks up signal by ID * - Acquires read lock * - Returns value (converted to JS type) */ getSignal(signalId: string): Object; /** * Update a signal's value * * @param signalId - Unique signal identifier * @param newValue - New value to set * @throws Error if signal doesn't exist * * Native implementation: * - Looks up signal by ID * - Acquires write lock * - Updates value * - Atomically increments version * - Notifies subscribers */ setSignal(signalId: string, newValue: Object): void; /** * Check if a signal exists * * @param signalId - Unique signal identifier * @returns true if signal exists, false otherwise * * Native implementation: * - Quick hash map lookup */ hasSignal(signalId: string): boolean; /** * Delete a signal from the store * * @param signalId - Unique signal identifier * * Native implementation: * - Removes from store * - shared_ptr handles memory cleanup */ deleteSignal(signalId: string): void; /** * Get the current version number of a signal * * Version numbers enable efficient change detection: * - Increments on every setValue call * - Lock-free atomic read * - No need to compare values * * @param signalId - Unique signal identifier * @returns Current version number * @throws Error if signal doesn't exist * * Native implementation: * - Atomic load from std::atomic<uint64_t> * - memory_order_acquire semantics */ getSignalVersion(signalId: string): number; /** * Batch update multiple signals * * More efficient than individual updates for multiple signals: * - Single TurboModule call * - Reduced serialization overhead * - All updates processed in native code * * @param updates - Array of [signalId, value] pairs * * Native implementation: * - Validates all signals exist * - Updates each signal in sequence * - Version increments are atomic per signal */ batchUpdate(updates: Array<[string, Object]>): void; /** * Get the total number of signals in the store * * Useful for: * - Memory monitoring * - Debugging and diagnostics * - Performance profiling * * @returns Number of active signals * * Native implementation: * - Returns size of internal unordered_map */ getSignalCount(): number; /** * Clear all signals from the store * * Warning: This will invalidate all existing signal references! * Use with caution - primarily for testing/cleanup scenarios. * * Native implementation: * - Clears the unordered_map * - All Signal objects are destroyed (shared_ptr cleanup) */ clearAllSignals(): void; /** * Get implementation metadata * * Returns information about the native implementation: * - Version number * - Build configuration * - Performance characteristics * * @returns Metadata object */ getMetadata(): { version: string; buildType: 'Debug' | 'Release'; threadSafe: boolean; supportsAtomic: boolean; }; } /** * Get the TurboModule instance * * This function retrieves the native module using React Native's * TurboModuleRegistry. If the module is not available (not linked, * old architecture, etc.), returns null. * * Usage: * ```typescript * const NativeSignalForge = getNativeModule(); * if (NativeSignalForge) { * const signalId = NativeSignalForge.createSignal(0); * } * ``` * * Note: In most cases, you should use jsiBridge.ts instead, which * automatically handles fallback and provides a cleaner API. */ // React Native codegen expects the module to be referenced with the exact // TurboModule spec name. Load using the primary TurboModule name first to // satisfy codegen's "Unused NativeModule spec" validation. Codegen requires // the literal module name to appear in a `TurboModuleRegistry.get` call, so // avoid additional direct references outside the ensure function above. const moduleName = 'NativeSignalForge'; const legacyModuleName = 'SignalForge'; export function getNativeModule(): Spec | null { try { // Access TurboModuleRegistry from React Native environment const registry = (global as any)?.TurboModuleRegistry ?? TurboModuleRegistry; if (registry && typeof registry.get === 'function') { return ( registry.get(moduleName) ?? registry.get(legacyModuleName) ) as Spec | null; } return null; } catch (e) { // Module not available (not using new architecture or not linked) return null; } } /** * Default export for convenience */ export default getNativeModule(); /** * Integration Notes: * * 1. For JSI Direct Binding (Recommended): * - Use jsiBridge.ts for best performance * - JSI bindings installed via global functions * - No TurboModule overhead * * 2. For TurboModule Integration: * - Import this module * - Call methods through the TurboModule interface * - Slightly more overhead than JSI direct binding * * 3. Code Generation: * - Run React Native's codegen on this file * - Generates native C++/ObjC++ bridge code * - Add generated code to your native project * * Example Implementation Mapping: * * C++ JSI Direct: * global.__signalForgeCreateSignal(value) * * TurboModule: * NativeSignalForge.createSignal(value) * * TypeScript Wrapper (jsiBridge.ts): * jsiBridge.createSignal(value) * └─> Tries JSI direct first * └─> Falls back to TurboModule * └─> Falls back to JS implementation * * Performance Comparison (1 million operations): * - JSI Direct: ~50ms (fastest, no bridge) * - TurboModule: ~150ms (good, some serialization) * - Old Bridge: ~500ms (slow, JSON serialization) * - JS Fallback: ~200ms (pure JS, no native) * * Recommendation: Use JSI direct binding (jsiBridge.ts) for production. */