@trainstar/synchro-react-native 0.1.1 → 0.3.1

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.
Files changed (62) hide show
  1. package/README.md +103 -24
  2. package/SynchroReactNative.podspec +3 -2
  3. package/android/build.gradle +8 -3
  4. package/android/src/main/kotlin/com/trainstar/synchro/rn/SynchroModule.kt +1105 -238
  5. package/ios/SynchroModule.mm +178 -51
  6. package/ios/SynchroModule.swift +1382 -295
  7. package/lib/module/NativeSynchro.js +5 -0
  8. package/lib/module/NativeSynchro.js.map +1 -1
  9. package/lib/module/SynchroClient.js +637 -65
  10. package/lib/module/SynchroClient.js.map +1 -1
  11. package/lib/module/digest.js +82 -0
  12. package/lib/module/digest.js.map +1 -0
  13. package/lib/module/errors.js +165 -22
  14. package/lib/module/errors.js.map +1 -1
  15. package/lib/module/hooks/useQuery.js +37 -3
  16. package/lib/module/hooks/useQuery.js.map +1 -1
  17. package/lib/module/hooks/useSyncStatus.js +51 -6
  18. package/lib/module/hooks/useSyncStatus.js.map +1 -1
  19. package/lib/module/index.js +2 -1
  20. package/lib/module/index.js.map +1 -1
  21. package/lib/module/inspection.js +121 -0
  22. package/lib/module/inspection.js.map +1 -0
  23. package/lib/module/sqliteValues.js +87 -0
  24. package/lib/module/sqliteValues.js.map +1 -0
  25. package/lib/module/types.js +10 -0
  26. package/lib/module/types.js.map +1 -1
  27. package/lib/typescript/example/e2e/corpus-harness.d.ts +34 -0
  28. package/lib/typescript/example/e2e/corpus-harness.d.ts.map +1 -0
  29. package/lib/typescript/example/src/conformance/runner.d.ts +125 -0
  30. package/lib/typescript/example/src/conformance/runner.d.ts.map +1 -0
  31. package/lib/typescript/example/src/conformance/types.d.ts +71 -0
  32. package/lib/typescript/example/src/conformance/types.d.ts.map +1 -0
  33. package/lib/typescript/src/NativeSynchro.d.ts +59 -17
  34. package/lib/typescript/src/NativeSynchro.d.ts.map +1 -1
  35. package/lib/typescript/src/SynchroClient.d.ts +27 -10
  36. package/lib/typescript/src/SynchroClient.d.ts.map +1 -1
  37. package/lib/typescript/src/digest.d.ts +3 -0
  38. package/lib/typescript/src/digest.d.ts.map +1 -0
  39. package/lib/typescript/src/errors.d.ts +37 -9
  40. package/lib/typescript/src/errors.d.ts.map +1 -1
  41. package/lib/typescript/src/hooks/useQuery.d.ts +2 -2
  42. package/lib/typescript/src/hooks/useQuery.d.ts.map +1 -1
  43. package/lib/typescript/src/hooks/useSyncStatus.d.ts.map +1 -1
  44. package/lib/typescript/src/index.d.ts +4 -3
  45. package/lib/typescript/src/index.d.ts.map +1 -1
  46. package/lib/typescript/src/inspection.d.ts +43 -0
  47. package/lib/typescript/src/inspection.d.ts.map +1 -0
  48. package/lib/typescript/src/sqliteValues.d.ts +8 -0
  49. package/lib/typescript/src/sqliteValues.d.ts.map +1 -0
  50. package/lib/typescript/src/types.d.ts +203 -11
  51. package/lib/typescript/src/types.d.ts.map +1 -1
  52. package/package.json +20 -8
  53. package/src/NativeSynchro.ts +93 -26
  54. package/src/SynchroClient.ts +899 -120
  55. package/src/digest.ts +93 -0
  56. package/src/errors.ts +273 -33
  57. package/src/hooks/useQuery.ts +59 -16
  58. package/src/hooks/useSyncStatus.ts +40 -7
  59. package/src/index.ts +44 -3
  60. package/src/inspection.ts +226 -0
  61. package/src/sqliteValues.ts +105 -0
  62. package/src/types.ts +317 -16
package/README.md CHANGED
@@ -4,19 +4,31 @@ React Native TurboModule bridge for Synchro. The package wraps the native Swift
4
4
 
5
5
  ## Requirements
6
6
 
7
- - React Native `0.83.x`
7
+ - React Native `0.83.x`. Use `0.83.5` or later with Xcode `26.4` or later.
8
8
  - iOS `16.0+`
9
9
  - Android `minSdk 24`
10
- - Node `20+`
10
+ - Node `20.19.4+`
11
11
  - Android development and CI should use JDK `17`
12
12
 
13
13
  ## Installation
14
14
 
15
+ Install the published package only after Synchro `0.3.1` is available. Before that,
16
+ use the local artifact flow in
17
+ [Client Consumption](https://trainstar.github.io/synchro/clients/consumption/).
18
+
15
19
  ```sh
16
- npm install @trainstar/synchro-react-native
20
+ npm install @trainstar/synchro-react-native@0.3.1
21
+ ```
22
+
23
+ Before you run `pod install`, add these published Synchro `0.3.1` dependencies to the
24
+ application `ios/Podfile`:
25
+
26
+ ```ruby
27
+ pod 'Synchro', :git => 'https://github.com/trainstar/synchro.git', :tag => 'v0.3.1'
28
+ pod 'GRDB.swift', :git => 'https://github.com/groue/GRDB.swift.git', :tag => 'v7.0.0'
17
29
  ```
18
30
 
19
- iOS:
31
+ Then install the pods:
20
32
 
21
33
  ```sh
22
34
  cd ios
@@ -25,25 +37,83 @@ pod install
25
37
 
26
38
  Android autolinks through the React Native Gradle plugin. No manual `MainApplication` edits are required.
27
39
 
40
+ The Android library resolves the native SDK from Maven Central.
41
+ Local Maven resolution is explicit in `example/android/build.gradle` for development.
42
+
28
43
  ## Usage
29
44
 
30
45
  ```ts
46
+ import { Platform } from 'react-native';
31
47
  import { SynchroClient } from '@trainstar/synchro-react-native';
32
48
 
33
- const client = new SynchroClient({
34
- dbPath: 'synchro.db',
35
- serverURL: 'https://api.example.com',
36
- authProvider: async () => '<jwt>',
37
- clientID: 'device-1',
38
- appVersion: '1.0.0',
39
- });
49
+ export async function syncQuickstart(
50
+ accessToken: string,
51
+ clientID: string,
52
+ databaseFileName: string
53
+ ): Promise<void> {
54
+ const serverURL = Platform.OS === 'android'
55
+ ? 'http://10.0.2.2:8091'
56
+ : 'http://127.0.0.1:8091';
57
+ const noteID = '00000000-0000-4000-8000-000000000001';
58
+ const client = new SynchroClient({
59
+ dbPath: databaseFileName,
60
+ serverURL,
61
+ authProvider: async () => accessToken,
62
+ clientID,
63
+ appVersion: '0.3.0',
64
+ });
65
+
66
+ try {
67
+ await client.initialize();
68
+ await client.start();
69
+ await client.syncNow();
70
+
71
+ const note = await client.queryOne(
72
+ 'SELECT id, body FROM notes WHERE id = ?',
73
+ [noteID]
74
+ );
75
+ if (note === null || typeof note.id !== 'string' || note.id !== noteID) {
76
+ throw new Error('The first pull did not contain the quickstart note');
77
+ }
78
+
79
+ await client.stop();
80
+ await client.execute(
81
+ 'UPDATE notes SET body = ? WHERE id = ?',
82
+ ['Edited offline', noteID]
83
+ );
84
+ await client.start();
85
+ await client.syncNow();
86
+
87
+ const updated = await client.queryOne(
88
+ 'SELECT body FROM notes WHERE id = ?',
89
+ [noteID]
90
+ );
91
+ if (
92
+ updated === null ||
93
+ typeof updated.body !== 'string' ||
94
+ updated.body !== 'Edited offline'
95
+ ) {
96
+ throw new Error('The synced note did not contain the offline edit');
97
+ }
98
+ } finally {
99
+ await client.close();
100
+ }
101
+ }
102
+ ```
40
103
 
41
- await client.initialize();
42
- await client.start();
104
+ The example uses the local tutorial adapter only. Android emulators reach the
105
+ host at `10.0.2.2`; iOS simulators use `127.0.0.1`. The example app permits
106
+ that cleartext development traffic. Production applications must use HTTPS and
107
+ must not copy the cleartext network configuration.
43
108
 
44
- const rows = await client.query('SELECT * FROM tasks WHERE done = ?', [0]);
45
- await client.execute('UPDATE tasks SET done = ? WHERE id = ?', [1, rows[0].id]);
46
- ```
109
+ `start()` arms the lifecycle. Do not use it as a schema-readiness signal.
110
+ `syncNow()` completes the requested cycle before this example reads the local
111
+ database.
112
+
113
+ SQL bind params are passed as native typed arrays, not as JSON strings. Supported React Native bind values are `null`, `string`, `number`, `boolean`, `{ type: 'int64', value: '...' }`, and `{ type: 'bytes', base64: '...' }`. Positional `null` values are preserved across iOS and Android for direct queries, batch execution, transactions, and query observers.
114
+
115
+ `queryOne()` returns `Row | null`, and each row value is `unknown`. Check for
116
+ both a non-null row and a string `id` before using the value as a note ID.
47
117
 
48
118
  ## Public API
49
119
 
@@ -67,11 +137,18 @@ Events and hooks:
67
137
 
68
138
  - `onStatusChange(callback)`
69
139
  - `onConflict(callback)`
70
- - `onSnapshotRequired(handler)`
71
140
  - `useQuery(client, sql, params?, tables?)`
72
141
  - `useSyncStatus(client)`
73
142
  - `usePendingChanges(client, pollInterval?)`
74
143
 
144
+ `useQuery` compares `int64` and `bytes` parameters by tag type and payload.
145
+ Equivalent tag objects do not restart the query or subscription.
146
+
147
+ `useSyncStatus` subscribes before it reads current native status.
148
+ New events take priority over the initial snapshot.
149
+ Changing clients resets the displayed status until the new client supplies state.
150
+ Snapshot failures reach the React error boundary unless a newer status event has arrived.
151
+
75
152
  ## Transactions
76
153
 
77
154
  `writeTransaction()` and `readTransaction()` mirror the native SDKs through a bridge-held transaction session.
@@ -85,8 +162,8 @@ Contract:
85
162
  ```ts
86
163
  await client.writeTransaction(async (tx) => {
87
164
  await tx.execute(
88
- 'INSERT INTO tasks (id, title, done) VALUES (?, ?, ?)',
89
- ['task-1', 'Ship RN SDK', 0]
165
+ 'UPDATE notes SET body = ? WHERE id = ?',
166
+ ['Edited offline', '00000000-0000-4000-8000-000000000001']
90
167
  );
91
168
  });
92
169
  ```
@@ -100,7 +177,6 @@ Native errors are normalized to typed JS errors, including:
100
177
  - `TableNotSyncedError`
101
178
  - `UpgradeRequiredError`
102
179
  - `SchemaMismatchError`
103
- - `SnapshotRequiredError`
104
180
  - `PushRejectedError`
105
181
  - `NetworkError`
106
182
  - `ServerError`
@@ -110,13 +186,16 @@ Native errors are normalized to typed JS errors, including:
110
186
  - `NotStartedError`
111
187
  - `TransactionTimeoutError`
112
188
 
189
+ The iOS schema bridge rejects malformed JSON with the existing `UNKNOWN` error code.
190
+
113
191
  ## Development
114
192
 
193
+ The published package accepts Node `20.19.4` or later. Repository development
194
+ uses the Node `22.20.0` pin in `.nvmrc`.
195
+
115
196
  ```sh
116
- npm run typecheck
117
- npm run test:unit
118
- npm run prepare
119
- npm run pack:dry-run
197
+ make lint-rn
198
+ make test-rn-unit
120
199
  ```
121
200
 
122
201
  The example app in [`example/`](./example) is the end-to-end harness used for RN bridge verification on iOS and Android.
@@ -11,12 +11,13 @@ Pod::Spec.new do |s|
11
11
  s.authors = package["author"]
12
12
 
13
13
  s.platforms = { :ios => "16.0" }
14
- s.source = { :git => "https://github.com/trainstar/synchro.git", :tag => "#{s.version}" }
14
+ s.source = { :git => "https://github.com/trainstar/synchro.git", :tag => "v#{s.version}" }
15
15
 
16
16
  s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
17
  s.private_header_files = "ios/**/*.h"
18
+ s.compiler_flags = "-Werror=protocol"
18
19
 
19
- s.dependency "Synchro", "~> 0.1"
20
+ s.dependency "Synchro", "= #{s.version}"
20
21
 
21
22
  install_modules_dependencies(s)
22
23
  end
@@ -17,7 +17,6 @@ buildscript {
17
17
  repositories {
18
18
  google()
19
19
  mavenCentral()
20
- mavenLocal()
21
20
  }
22
21
 
23
22
  dependencies {
@@ -33,6 +32,9 @@ apply plugin: "kotlin-android"
33
32
 
34
33
  apply plugin: "com.facebook.react"
35
34
 
35
+ def defaultSynchroVersion = "0.3.1"
36
+ def resolvedSynchroVersion = findProperty("synchroVersion")?.toString() ?: defaultSynchroVersion
37
+
36
38
  android {
37
39
  namespace "com.trainstar.synchro.rn"
38
40
 
@@ -66,16 +68,19 @@ android {
66
68
  compileOptions {
67
69
  sourceCompatibility JavaVersion.VERSION_1_8
68
70
  targetCompatibility JavaVersion.VERSION_1_8
71
+ coreLibraryDesugaringEnabled true
69
72
  }
70
73
  }
71
74
 
72
75
  repositories {
73
- mavenLocal()
76
+ mavenCentral()
74
77
  }
75
78
 
76
79
  dependencies {
80
+ coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
77
81
  implementation "com.facebook.react:react-android"
78
- implementation "com.trainstar:synchro:0.1.0"
82
+ implementation "fit.trainstar:synchro:${resolvedSynchroVersion}"
83
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
79
84
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0"
80
85
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
81
86
  }