@tursodatabase/sync-react-native 0.5.0-pre.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/android/CMakeLists.txt +53 -0
- package/android/build.gradle +84 -0
- package/android/cpp-adapter.cpp +49 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/turso/sync/reactnative/TursoBridge.java +44 -0
- package/android/src/main/java/com/turso/sync/reactnative/TursoModule.java +82 -0
- package/android/src/main/java/com/turso/sync/reactnative/TursoPackage.java +29 -0
- package/cpp/TursoConnectionHostObject.cpp +179 -0
- package/cpp/TursoConnectionHostObject.h +52 -0
- package/cpp/TursoDatabaseHostObject.cpp +98 -0
- package/cpp/TursoDatabaseHostObject.h +49 -0
- package/cpp/TursoHostObject.cpp +561 -0
- package/cpp/TursoHostObject.h +24 -0
- package/cpp/TursoStatementHostObject.cpp +414 -0
- package/cpp/TursoStatementHostObject.h +65 -0
- package/cpp/TursoSyncChangesHostObject.cpp +41 -0
- package/cpp/TursoSyncChangesHostObject.h +52 -0
- package/cpp/TursoSyncDatabaseHostObject.cpp +328 -0
- package/cpp/TursoSyncDatabaseHostObject.h +61 -0
- package/cpp/TursoSyncIoItemHostObject.cpp +304 -0
- package/cpp/TursoSyncIoItemHostObject.h +52 -0
- package/cpp/TursoSyncOperationHostObject.cpp +168 -0
- package/cpp/TursoSyncOperationHostObject.h +53 -0
- package/ios/TursoModule.h +8 -0
- package/ios/TursoModule.mm +95 -0
- package/lib/commonjs/Database.js +445 -0
- package/lib/commonjs/Database.js.map +1 -0
- package/lib/commonjs/Statement.js +339 -0
- package/lib/commonjs/Statement.js.map +1 -0
- package/lib/commonjs/index.js +229 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/internal/asyncOperation.js +124 -0
- package/lib/commonjs/internal/asyncOperation.js.map +1 -0
- package/lib/commonjs/internal/ioProcessor.js +315 -0
- package/lib/commonjs/internal/ioProcessor.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +133 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/Database.js +441 -0
- package/lib/module/Database.js.map +1 -0
- package/lib/module/Statement.js +335 -0
- package/lib/module/Statement.js.map +1 -0
- package/lib/module/index.js +205 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/internal/asyncOperation.js +116 -0
- package/lib/module/internal/asyncOperation.js.map +1 -0
- package/lib/module/internal/ioProcessor.js +309 -0
- package/lib/module/internal/ioProcessor.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +163 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/Database.d.ts +140 -0
- package/lib/typescript/Database.d.ts.map +1 -0
- package/lib/typescript/Statement.d.ts +105 -0
- package/lib/typescript/Statement.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +175 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/internal/asyncOperation.d.ts +39 -0
- package/lib/typescript/internal/asyncOperation.d.ts.map +1 -0
- package/lib/typescript/internal/ioProcessor.d.ts +48 -0
- package/lib/typescript/internal/ioProcessor.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +316 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/package.json +97 -0
- package/src/Database.ts +480 -0
- package/src/Statement.ts +372 -0
- package/src/index.ts +240 -0
- package/src/internal/asyncOperation.ts +147 -0
- package/src/internal/ioProcessor.ts +328 -0
- package/src/types.ts +391 -0
- package/turso-sync-react-native.podspec +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Turso Sync React Native SDK
|
|
2
|
+
|
|
3
|
+
React Native bindings for Turso embedded replicas - sync your local SQLite database with Turso cloud.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @tursodatabase/sync-react-native
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### iOS
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd ios && pod install
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Android
|
|
18
|
+
|
|
19
|
+
Requires `minSdkVersion` 21+ in `android/build.gradle`.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Database, getDbPath } from '@tursodatabase/sync-react-native';
|
|
25
|
+
|
|
26
|
+
// Get platform-specific writable path
|
|
27
|
+
const dbPath = getDbPath('myapp.db');
|
|
28
|
+
|
|
29
|
+
// Create database with sync
|
|
30
|
+
const db = new Database({
|
|
31
|
+
path: dbPath,
|
|
32
|
+
url: 'libsql://your-db.turso.io',
|
|
33
|
+
authToken: 'your-auth-token',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Connect (bootstraps from remote if empty)
|
|
37
|
+
await db.connect();
|
|
38
|
+
|
|
39
|
+
// Query local replica (fast)
|
|
40
|
+
const users = await db.all('SELECT * FROM users');
|
|
41
|
+
|
|
42
|
+
// Make local changes
|
|
43
|
+
await db.run('INSERT INTO users (name) VALUES (?)', ['Alice']);
|
|
44
|
+
|
|
45
|
+
// Sync with remote
|
|
46
|
+
await db.push(); // Push local changes
|
|
47
|
+
await db.pull(); // Pull remote changes
|
|
48
|
+
|
|
49
|
+
// Close when done
|
|
50
|
+
await db.close();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Local-Only Database
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const db = new Database({ path: getDbPath('local.db') });
|
|
57
|
+
await db.connect();
|
|
58
|
+
|
|
59
|
+
await db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
|
|
60
|
+
await db.run('INSERT INTO users (name) VALUES (?)', ['Bob']);
|
|
61
|
+
const user = await db.get('SELECT * FROM users WHERE id = ?', [1]);
|
|
62
|
+
|
|
63
|
+
await db.close();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Encrypted Remote Database
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const db = new Database({
|
|
70
|
+
path: getDbPath('encrypted.db'),
|
|
71
|
+
url: 'libsql://your-db.turso.io',
|
|
72
|
+
authToken: 'your-auth-token',
|
|
73
|
+
remoteEncryption: {
|
|
74
|
+
cipher: 'aes256gcm',
|
|
75
|
+
key: 'base64-encoded-key',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API
|
|
81
|
+
|
|
82
|
+
### Database Methods
|
|
83
|
+
|
|
84
|
+
| Method | Description |
|
|
85
|
+
|--------|-------------|
|
|
86
|
+
| `connect()` | Open/bootstrap the database |
|
|
87
|
+
| `exec(sql)` | Execute SQL (no results) |
|
|
88
|
+
| `run(sql, params?)` | Execute SQL, return `{ changes, lastInsertRowid }` |
|
|
89
|
+
| `get(sql, params?)` | Query single row |
|
|
90
|
+
| `all(sql, params?)` | Query all rows |
|
|
91
|
+
| `prepare(sql)` | Create prepared statement |
|
|
92
|
+
| `close()` | Close database |
|
|
93
|
+
|
|
94
|
+
### Sync Methods (when `url` is provided)
|
|
95
|
+
|
|
96
|
+
| Method | Description |
|
|
97
|
+
|--------|-------------|
|
|
98
|
+
| `push()` | Push local changes to remote |
|
|
99
|
+
| `pull()` | Pull remote changes to local |
|
|
100
|
+
| `sync()` | Push then pull |
|
|
101
|
+
| `stats()` | Get sync statistics |
|
|
102
|
+
|
|
103
|
+
### Transactions
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
await db.transaction(async () => {
|
|
107
|
+
await db.run('INSERT INTO users (name) VALUES (?)', ['Alice']);
|
|
108
|
+
await db.run('INSERT INTO users (name) VALUES (?)', ['Bob']);
|
|
109
|
+
// Commits on success, rolls back on error
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Links
|
|
114
|
+
|
|
115
|
+
- [Turso Documentation](https://docs.turso.tech)
|
|
116
|
+
- [GitHub](https://github.com/tursodatabase/turso)
|
|
117
|
+
- [npm](https://www.npmjs.com/package/@tursodatabase/sync-react-native)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22)
|
|
2
|
+
project(turso-sync-react-native)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
|
|
7
|
+
# Find packages
|
|
8
|
+
find_package(ReactAndroid REQUIRED CONFIG)
|
|
9
|
+
find_package(fbjni REQUIRED CONFIG)
|
|
10
|
+
|
|
11
|
+
# Source files
|
|
12
|
+
set(TURSO_SOURCES
|
|
13
|
+
cpp-adapter.cpp
|
|
14
|
+
../cpp/TursoHostObject.cpp
|
|
15
|
+
../cpp/TursoDatabaseHostObject.cpp
|
|
16
|
+
../cpp/TursoConnectionHostObject.cpp
|
|
17
|
+
../cpp/TursoStatementHostObject.cpp
|
|
18
|
+
../cpp/TursoSyncDatabaseHostObject.cpp
|
|
19
|
+
../cpp/TursoSyncOperationHostObject.cpp
|
|
20
|
+
../cpp/TursoSyncIoItemHostObject.cpp
|
|
21
|
+
../cpp/TursoSyncChangesHostObject.cpp
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Create shared library
|
|
25
|
+
add_library(${PROJECT_NAME} SHARED ${TURSO_SOURCES})
|
|
26
|
+
|
|
27
|
+
# 16KB page alignment for Android 15+ compatibility
|
|
28
|
+
target_link_options(${PROJECT_NAME} PRIVATE "-Wl,-z,max-page-size=16384")
|
|
29
|
+
|
|
30
|
+
# Include directories
|
|
31
|
+
target_include_directories(${PROJECT_NAME} PRIVATE
|
|
32
|
+
../cpp
|
|
33
|
+
../libs/android
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Pre-built Rust shared library (.so) loaded at runtime
|
|
37
|
+
add_library(turso_sync_sdk_kit SHARED IMPORTED)
|
|
38
|
+
|
|
39
|
+
# IMPORTED_NO_SONAME is important to properly adjust import paths for runtime by linker (so they will not be reused from build time)
|
|
40
|
+
set_target_properties(turso_sync_sdk_kit PROPERTIES
|
|
41
|
+
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../libs/android/${ANDROID_ABI}/libturso_sync_sdk_kit.so
|
|
42
|
+
IMPORTED_NO_SONAME TRUE
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Link libraries (record runtime dependency on Rust .so)
|
|
46
|
+
target_link_libraries(${PROJECT_NAME}
|
|
47
|
+
ReactAndroid::jsi
|
|
48
|
+
ReactAndroid::reactnative
|
|
49
|
+
fbjni::fbjni
|
|
50
|
+
turso_sync_sdk_kit
|
|
51
|
+
android
|
|
52
|
+
log
|
|
53
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
dependencies {
|
|
7
|
+
classpath "com.android.tools.build:gradle:8.2.0"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
plugins {
|
|
12
|
+
id "com.android.library"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def reactNativeArchitectures() {
|
|
16
|
+
def value = project.getProperties().get("reactNativeArchitectures")
|
|
17
|
+
return value ? value.split(",") : ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
android {
|
|
21
|
+
namespace "com.turso.sync.reactnative"
|
|
22
|
+
compileSdk 34
|
|
23
|
+
|
|
24
|
+
defaultConfig {
|
|
25
|
+
minSdk 24
|
|
26
|
+
targetSdk 34
|
|
27
|
+
|
|
28
|
+
externalNativeBuild {
|
|
29
|
+
cmake {
|
|
30
|
+
cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all"
|
|
31
|
+
abiFilters(*reactNativeArchitectures())
|
|
32
|
+
arguments "-DANDROID_STL=c++_shared"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
buildFeatures {
|
|
38
|
+
prefab true
|
|
39
|
+
buildConfig true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
externalNativeBuild {
|
|
43
|
+
cmake {
|
|
44
|
+
path "CMakeLists.txt"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
buildTypes {
|
|
49
|
+
release {
|
|
50
|
+
minifyEnabled false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
packaging {
|
|
55
|
+
excludes += [
|
|
56
|
+
"**/libjsi.so",
|
|
57
|
+
"**/libfbjni.so",
|
|
58
|
+
"**/libc++_shared.so",
|
|
59
|
+
"**/libreactnative.so"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
sourceSets {
|
|
63
|
+
main {
|
|
64
|
+
jniLibs.srcDirs = ["../libs/android"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
repositories {
|
|
70
|
+
google()
|
|
71
|
+
mavenCentral()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
dependencies {
|
|
75
|
+
compileOnly "com.facebook.react:react-android"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Task to build Rust library for Android
|
|
79
|
+
task buildRustLibrary(type: Exec) {
|
|
80
|
+
workingDir "${projectDir}/.."
|
|
81
|
+
commandLine "make", "android"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
preBuild.dependsOn buildRustLibrary
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#include "TursoHostObject.h"
|
|
2
|
+
#include <ReactCommon/CallInvokerHolder.h>
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
#include <jni.h>
|
|
5
|
+
#include <jsi/jsi.h>
|
|
6
|
+
#include <typeinfo>
|
|
7
|
+
|
|
8
|
+
namespace jsi = facebook::jsi;
|
|
9
|
+
namespace react = facebook::react;
|
|
10
|
+
namespace jni = facebook::jni;
|
|
11
|
+
|
|
12
|
+
// This file is not using raw jni but rather fbjni, do not change how the native
|
|
13
|
+
// functions are registered
|
|
14
|
+
// https://github.com/facebookincubator/fbjni/blob/main/docs/quickref.md
|
|
15
|
+
struct TursoBridge : jni::JavaClass<TursoBridge>
|
|
16
|
+
{
|
|
17
|
+
static constexpr auto kJavaDescriptor = "Lcom/turso/sync/reactnative/TursoBridge;";
|
|
18
|
+
|
|
19
|
+
static void registerNatives()
|
|
20
|
+
{
|
|
21
|
+
javaClassStatic()->registerNatives(
|
|
22
|
+
{makeNativeMethod("installNativeJsi", TursoBridge::installNativeJsi),
|
|
23
|
+
makeNativeMethod("clearStateNativeJsi", TursoBridge::clearStateNativeJsi)});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private:
|
|
27
|
+
static void installNativeJsi(
|
|
28
|
+
jni::alias_ref<jni::JObject> thiz, jlong jsiRuntimePtr,
|
|
29
|
+
jni::alias_ref<react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
|
|
30
|
+
jni::alias_ref<jni::JString> dbPath)
|
|
31
|
+
{
|
|
32
|
+
auto jsiRuntime = reinterpret_cast<jsi::Runtime *>(jsiRuntimePtr);
|
|
33
|
+
auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
|
|
34
|
+
std::string dbPathStr = dbPath->toStdString();
|
|
35
|
+
|
|
36
|
+
turso::install(*jsiRuntime, jsCallInvoker, dbPathStr.c_str());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static void clearStateNativeJsi(jni::alias_ref<jni::JObject> thiz)
|
|
40
|
+
{
|
|
41
|
+
turso::invalidate();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *)
|
|
46
|
+
{
|
|
47
|
+
return jni::initialize(vm, []
|
|
48
|
+
{ TursoBridge::registerNatives(); });
|
|
49
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package com.turso.sync.reactnative;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactContext;
|
|
4
|
+
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl;
|
|
5
|
+
|
|
6
|
+
import java.lang.Exception;
|
|
7
|
+
|
|
8
|
+
// bridge layer, installNativeJsi/clearStateNativeJsi methods are set through cpp-adapter.cpp
|
|
9
|
+
public class TursoBridge {
|
|
10
|
+
private static final TursoBridge instance = new TursoBridge();
|
|
11
|
+
|
|
12
|
+
public static TursoBridge getInstance() {
|
|
13
|
+
return instance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private TursoBridge() {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private native void installNativeJsi(
|
|
20
|
+
long jsContextNativePointer,
|
|
21
|
+
CallInvokerHolderImpl jsCallInvokerHolder,
|
|
22
|
+
String dbPath);
|
|
23
|
+
|
|
24
|
+
private native void clearStateNativeJsi();
|
|
25
|
+
|
|
26
|
+
public void install(ReactContext context) throws Exception {
|
|
27
|
+
long jsContextPointer = context.getJavaScriptContextHolder().get();
|
|
28
|
+
if (jsContextPointer == 0) {
|
|
29
|
+
throw new Exception("jsContextPointer == 0");
|
|
30
|
+
}
|
|
31
|
+
CallInvokerHolderImpl jsCallInvokerHolder = (CallInvokerHolderImpl) context.getCatalystInstance()
|
|
32
|
+
.getJSCallInvokerHolder();
|
|
33
|
+
|
|
34
|
+
// getDatabasePath(...) returns file path - so we pass dummy value and remove it
|
|
35
|
+
// after to get directory path
|
|
36
|
+
String dbPath = context.getDatabasePath("tursoDatabaseFile").getAbsolutePath().replace("tursoDatabaseFile", "");
|
|
37
|
+
|
|
38
|
+
installNativeJsi(jsContextPointer, jsCallInvokerHolder, dbPath);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public void invalidate() {
|
|
42
|
+
clearStateNativeJsi();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
package com.turso.sync.reactnative;
|
|
2
|
+
|
|
3
|
+
import java.io.File;
|
|
4
|
+
import java.lang.Exception;
|
|
5
|
+
import java.util.HashMap;
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
|
|
8
|
+
import androidx.annotation.NonNull;
|
|
9
|
+
|
|
10
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
11
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
12
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
13
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
14
|
+
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl;
|
|
15
|
+
|
|
16
|
+
// The React Native bridge - exposes methods to JavaScript
|
|
17
|
+
@ReactModule(name = TursoModule.NAME)
|
|
18
|
+
public class TursoModule extends ReactContextBaseJavaModule {
|
|
19
|
+
public static final String NAME = "Turso";
|
|
20
|
+
|
|
21
|
+
static {
|
|
22
|
+
System.loadLibrary("turso_sync_sdk_kit");
|
|
23
|
+
System.loadLibrary("turso-sync-react-native");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public TursoModule(ReactApplicationContext reactContext) {
|
|
27
|
+
super(reactContext);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Override
|
|
31
|
+
@NonNull
|
|
32
|
+
public String getName() {
|
|
33
|
+
return NAME;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Override
|
|
37
|
+
public Map<String, Object> getConstants() {
|
|
38
|
+
final Map<String, Object> constants = new HashMap<>();
|
|
39
|
+
ReactApplicationContext context = getReactApplicationContext();
|
|
40
|
+
|
|
41
|
+
// getDatabasePath(...) returns file path - so we pass dummy value and remove it
|
|
42
|
+
// after to get directory path
|
|
43
|
+
String dbPath = context.getDatabasePath("tursoDatabaseFile").getAbsolutePath().replace("tursoDatabaseFile", "");
|
|
44
|
+
constants.put("ANDROID_DATABASE_PATH", dbPath);
|
|
45
|
+
|
|
46
|
+
String filesPath = context.getFilesDir().getAbsolutePath();
|
|
47
|
+
constants.put("ANDROID_FILES_PATH", filesPath);
|
|
48
|
+
|
|
49
|
+
File externalFilesDir = context.getExternalFilesDir(null);
|
|
50
|
+
constants.put("ANDROID_EXTERNAL_FILES_PATH",
|
|
51
|
+
externalFilesDir != null ? externalFilesDir.getAbsolutePath() : null);
|
|
52
|
+
|
|
53
|
+
// populate Android and IOS constants to simplify JS code (e.g.
|
|
54
|
+
// IOS_DOCUMENT_PATH ?? ANDROID_DATABASE_PATH)
|
|
55
|
+
constants.put("IOS_DOCUMENT_PATH", null);
|
|
56
|
+
constants.put("IOS_LIBRARY_PATH", null);
|
|
57
|
+
|
|
58
|
+
return constants;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
62
|
+
public boolean install() {
|
|
63
|
+
try {
|
|
64
|
+
ReactApplicationContext context = getReactApplicationContext();
|
|
65
|
+
// Install native module
|
|
66
|
+
TursoBridge.getInstance().install(context);
|
|
67
|
+
return true;
|
|
68
|
+
} catch (Exception e) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Override
|
|
74
|
+
public void invalidate() {
|
|
75
|
+
super.invalidate();
|
|
76
|
+
TursoBridge.getInstance().invalidate();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private native void installNativeJsi(long jsiRuntimePtr, Object callInvokerHolder, String dbPath);
|
|
80
|
+
|
|
81
|
+
private native void clearStateNativeJsi();
|
|
82
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.turso.sync.reactnative;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.Collections;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
14
|
+
// Entry point for React Native's module registration system
|
|
15
|
+
public class TursoPackage implements ReactPackage {
|
|
16
|
+
@NonNull
|
|
17
|
+
@Override
|
|
18
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
19
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
20
|
+
modules.add(new TursoModule(reactContext));
|
|
21
|
+
return modules;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@NonNull
|
|
25
|
+
@Override
|
|
26
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
27
|
+
return Collections.emptyList();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#include "TursoConnectionHostObject.h"
|
|
2
|
+
#include "TursoStatementHostObject.h"
|
|
3
|
+
|
|
4
|
+
extern "C" {
|
|
5
|
+
#include <turso.h>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
namespace turso {
|
|
9
|
+
|
|
10
|
+
TursoConnectionHostObject::~TursoConnectionHostObject() {
|
|
11
|
+
if (conn_) {
|
|
12
|
+
turso_connection_deinit(conn_);
|
|
13
|
+
conn_ = nullptr;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
void TursoConnectionHostObject::throwError(jsi::Runtime &rt, const char *error) {
|
|
18
|
+
throw jsi::JSError(rt, error ? error : "Unknown error");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
jsi::Value TursoConnectionHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
|
|
22
|
+
auto propName = name.utf8(rt);
|
|
23
|
+
|
|
24
|
+
if (propName == "prepareSingle") {
|
|
25
|
+
return jsi::Function::createFromHostFunction(
|
|
26
|
+
rt, name, 1,
|
|
27
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
28
|
+
return this->prepareSingle(rt, args, count);
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (propName == "prepareFirst") {
|
|
34
|
+
return jsi::Function::createFromHostFunction(
|
|
35
|
+
rt, name, 1,
|
|
36
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
37
|
+
return this->prepareFirst(rt, args, count);
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (propName == "lastInsertRowid") {
|
|
43
|
+
return jsi::Function::createFromHostFunction(
|
|
44
|
+
rt, name, 0,
|
|
45
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t) -> jsi::Value {
|
|
46
|
+
return this->lastInsertRowid(rt);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (propName == "getAutocommit") {
|
|
52
|
+
return jsi::Function::createFromHostFunction(
|
|
53
|
+
rt, name, 0,
|
|
54
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t) -> jsi::Value {
|
|
55
|
+
return this->getAutocommit(rt);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (propName == "setBusyTimeout") {
|
|
61
|
+
return jsi::Function::createFromHostFunction(
|
|
62
|
+
rt, name, 1,
|
|
63
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
64
|
+
return this->setBusyTimeout(rt, args, count);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (propName == "close") {
|
|
70
|
+
return jsi::Function::createFromHostFunction(
|
|
71
|
+
rt, name, 0,
|
|
72
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *, size_t) -> jsi::Value {
|
|
73
|
+
return this->close(rt);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return jsi::Value::undefined();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void TursoConnectionHostObject::set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi::Value &value) {
|
|
82
|
+
// Read-only object
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
std::vector<jsi::PropNameID> TursoConnectionHostObject::getPropertyNames(jsi::Runtime &rt) {
|
|
86
|
+
std::vector<jsi::PropNameID> props;
|
|
87
|
+
props.emplace_back(jsi::PropNameID::forAscii(rt, "prepareSingle"));
|
|
88
|
+
props.emplace_back(jsi::PropNameID::forAscii(rt, "prepareFirst"));
|
|
89
|
+
props.emplace_back(jsi::PropNameID::forAscii(rt, "lastInsertRowid"));
|
|
90
|
+
props.emplace_back(jsi::PropNameID::forAscii(rt, "getAutocommit"));
|
|
91
|
+
props.emplace_back(jsi::PropNameID::forAscii(rt, "setBusyTimeout"));
|
|
92
|
+
props.emplace_back(jsi::PropNameID::forAscii(rt, "close"));
|
|
93
|
+
return props;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 1:1 C API mapping - NO logic, just calls through to C API
|
|
97
|
+
jsi::Value TursoConnectionHostObject::prepareSingle(jsi::Runtime &rt, const jsi::Value *args, size_t count) {
|
|
98
|
+
if (count < 1 || !args[0].isString()) {
|
|
99
|
+
throw jsi::JSError(rt, "prepareSingle: expected string argument");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
std::string sql = args[0].asString(rt).utf8(rt);
|
|
103
|
+
turso_statement_t* statement = nullptr;
|
|
104
|
+
const char* error = nullptr;
|
|
105
|
+
|
|
106
|
+
turso_status_code_t status = turso_connection_prepare_single(conn_, sql.c_str(), &statement, &error);
|
|
107
|
+
|
|
108
|
+
if (status != TURSO_OK) {
|
|
109
|
+
throwError(rt, error);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Wrap statement in TursoStatementHostObject
|
|
113
|
+
auto statementObj = std::make_shared<TursoStatementHostObject>(statement);
|
|
114
|
+
return jsi::Object::createFromHostObject(rt, statementObj);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
jsi::Value TursoConnectionHostObject::prepareFirst(jsi::Runtime &rt, const jsi::Value *args, size_t count) {
|
|
118
|
+
if (count < 1 || !args[0].isString()) {
|
|
119
|
+
throw jsi::JSError(rt, "prepareFirst: expected string argument");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
std::string sql = args[0].asString(rt).utf8(rt);
|
|
123
|
+
turso_statement_t* statement = nullptr;
|
|
124
|
+
size_t tail_idx = 0;
|
|
125
|
+
const char* error = nullptr;
|
|
126
|
+
|
|
127
|
+
turso_status_code_t status = turso_connection_prepare_first(conn_, sql.c_str(), &statement, &tail_idx, &error);
|
|
128
|
+
|
|
129
|
+
if (status != TURSO_OK) {
|
|
130
|
+
throwError(rt, error);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// If statement is null, return null (no valid statement parsed)
|
|
134
|
+
if (!statement) {
|
|
135
|
+
return jsi::Value::null();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Return object with statement and tail_idx
|
|
139
|
+
jsi::Object result(rt);
|
|
140
|
+
auto statementObj = std::make_shared<TursoStatementHostObject>(statement);
|
|
141
|
+
result.setProperty(rt, "statement", jsi::Object::createFromHostObject(rt, statementObj));
|
|
142
|
+
result.setProperty(rt, "tailIdx", jsi::Value(static_cast<double>(tail_idx)));
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
jsi::Value TursoConnectionHostObject::lastInsertRowid(jsi::Runtime &rt) {
|
|
148
|
+
int64_t rowid = turso_connection_last_insert_rowid(conn_);
|
|
149
|
+
return jsi::Value(static_cast<double>(rowid));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
jsi::Value TursoConnectionHostObject::getAutocommit(jsi::Runtime &rt) {
|
|
153
|
+
bool autocommit = turso_connection_get_autocommit(conn_);
|
|
154
|
+
return jsi::Value(autocommit);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
jsi::Value TursoConnectionHostObject::setBusyTimeout(jsi::Runtime &rt, const jsi::Value *args, size_t count) {
|
|
158
|
+
if (count < 1 || !args[0].isNumber()) {
|
|
159
|
+
throw jsi::JSError(rt, "setBusyTimeout: expected number argument");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
int64_t timeout_ms = static_cast<int64_t>(args[0].asNumber());
|
|
163
|
+
turso_connection_set_busy_timeout_ms(conn_, timeout_ms);
|
|
164
|
+
|
|
165
|
+
return jsi::Value::undefined();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
jsi::Value TursoConnectionHostObject::close(jsi::Runtime &rt) {
|
|
169
|
+
const char* error = nullptr;
|
|
170
|
+
turso_status_code_t status = turso_connection_close(conn_, &error);
|
|
171
|
+
|
|
172
|
+
if (status != TURSO_OK) {
|
|
173
|
+
throwError(rt, error);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return jsi::Value::undefined();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
} // namespace turso
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <jsi/jsi.h>
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
// Forward declarations for Turso C API types
|
|
8
|
+
extern "C" {
|
|
9
|
+
struct turso_connection;
|
|
10
|
+
struct turso_statement;
|
|
11
|
+
typedef struct turso_connection turso_connection_t;
|
|
12
|
+
typedef struct turso_statement turso_statement_t;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
namespace turso {
|
|
16
|
+
|
|
17
|
+
using namespace facebook;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* TursoConnectionHostObject wraps turso_connection_t* (core SDK-KIT type for database connection).
|
|
21
|
+
* This is a THIN wrapper - 1:1 mapping of SDK-KIT C API with NO logic.
|
|
22
|
+
* All logic belongs in TypeScript or Rust, not here.
|
|
23
|
+
*/
|
|
24
|
+
class TursoConnectionHostObject : public jsi::HostObject {
|
|
25
|
+
public:
|
|
26
|
+
TursoConnectionHostObject(turso_connection_t* conn) : conn_(conn) {}
|
|
27
|
+
~TursoConnectionHostObject();
|
|
28
|
+
|
|
29
|
+
// JSI HostObject interface
|
|
30
|
+
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &name) override;
|
|
31
|
+
void set(jsi::Runtime &rt, const jsi::PropNameID &name, const jsi::Value &value) override;
|
|
32
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
|
|
33
|
+
|
|
34
|
+
// Direct access to wrapped pointer (for internal use)
|
|
35
|
+
turso_connection_t* getConnection() const { return conn_; }
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
turso_connection_t* conn_ = nullptr;
|
|
39
|
+
|
|
40
|
+
// Helper to throw JS errors
|
|
41
|
+
void throwError(jsi::Runtime &rt, const char *error);
|
|
42
|
+
|
|
43
|
+
// 1:1 C API mapping methods (NO logic - just calls through to C API)
|
|
44
|
+
jsi::Value prepareSingle(jsi::Runtime &rt, const jsi::Value *args, size_t count);
|
|
45
|
+
jsi::Value prepareFirst(jsi::Runtime &rt, const jsi::Value *args, size_t count);
|
|
46
|
+
jsi::Value lastInsertRowid(jsi::Runtime &rt);
|
|
47
|
+
jsi::Value getAutocommit(jsi::Runtime &rt);
|
|
48
|
+
jsi::Value setBusyTimeout(jsi::Runtime &rt, const jsi::Value *args, size_t count);
|
|
49
|
+
jsi::Value close(jsi::Runtime &rt);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
} // namespace turso
|