diff --git a/.changeset/little-bikes-sort.md b/.changeset/little-bikes-sort.md new file mode 100644 index 00000000..ab016903 --- /dev/null +++ b/.changeset/little-bikes-sort.md @@ -0,0 +1,5 @@ +--- +'@journeyapps/powersync-sdk-common': patch +--- + +Fixed missing `transactionId` value in crud transaction response. diff --git a/.changeset/sharp-lamps-perform.md b/.changeset/sharp-lamps-perform.md new file mode 100644 index 00000000..02c2a1c2 --- /dev/null +++ b/.changeset/sharp-lamps-perform.md @@ -0,0 +1,5 @@ +--- +'@journeyapps/powersync-sdk-react-native': patch +--- + +Fixed issue where SDK would fail to reconnect after disconnecting. diff --git a/demos/react-native-supabase-todolist/ios/Podfile.lock b/demos/react-native-supabase-todolist/ios/Podfile.lock index bda0fb28..ca265039 100644 --- a/demos/react-native-supabase-todolist/ios/Podfile.lock +++ b/demos/react-native-supabase-todolist/ios/Podfile.lock @@ -1353,7 +1353,7 @@ SPEC CHECKSUMS: ExpoSecureStore: c84ae37d1c36f38524d289c67c3a2e3fc56f1108 EXSplashScreen: 39244885abfb1b12765aae89edb90f8c88db6bbd FBLazyVector: fbc4957d9aa695250b55d879c1d86f79d7e69ab4 - FBReactNativeSpec: 330cf4c6cf09f4085799e5e5fa0e1adc22972cf2 + FBReactNativeSpec: 1b6e109181e519dc05c5f0a0546cb8c60cd9a4bb fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 hermes-engine: b361c9ef5ef3cda53f66e195599b47e1f84ffa35 @@ -1399,7 +1399,7 @@ SPEC CHECKSUMS: React-RCTText: 73006e95ca359595c2510c1c0114027c85a6ddd3 React-RCTVibration: 599f427f9cbdd9c4bf38959ca020e8fef0717211 React-rendererdebug: f2946e0a1c3b906e71555a7c4a39aa6a6c0e639b - React-rncore: cf40dd2b824f5443532aa504dfb812319da9ff0a + React-rncore: c165a5d77c7785ae19fa5599fea04cc71a9c9ea4 React-runtimeexecutor: 2d1f64f58193f00a3ad71d3f89c2bfbfe11cf5a5 React-runtimescheduler: df8945a656356ff10f58f65a70820478bfcf33ad React-utils: f5bc61e7ea3325c0732ae2d755f4441940163b85 @@ -1410,9 +1410,9 @@ SPEC CHECKSUMS: RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2 RNVectorIcons: 210f910e834e3485af40693ad4615c1ec22fc02b SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - Yoga: 13c8ef87792450193e117976337b8527b49e8c03 + Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 PODFILE CHECKSUM: 91f1b09fe73837e9fdaecdd06e4916926352d556 -COCOAPODS: 1.14.3 +COCOAPODS: 1.13.0 diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 497c489f..6ef615bf 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -14,7 +14,7 @@ import { import { CrudBatch } from './sync/bucket/CrudBatch'; import { CrudTransaction } from './sync/bucket/CrudTransaction'; import { BucketStorageAdapter, PSInternalTable } from './sync/bucket/BucketStorageAdapter'; -import { CrudEntry } from './sync/bucket/CrudEntry'; +import { CrudEntry, CrudEntryJSON } from './sync/bucket/CrudEntry'; import { mutexRunExclusive } from '../utils/mutex'; import { BaseObserver } from '../utils/BaseObserver'; import { EventIterator } from 'event-iterator'; @@ -394,22 +394,24 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver { return await this.readTransaction(async (tx) => { - const first = await tx.execute(`SELECT id, tx_id, data FROM ${PSInternalTable.CRUD} ORDER BY id ASC LIMIT 1`); + const first = await tx.getOptional( + `SELECT id, tx_id, data FROM ${PSInternalTable.CRUD} ORDER BY id ASC LIMIT 1` + ); - if (!first.rows.length) { + if (!first) { return null; } - const txId: number | undefined = first['tx_id']; + const txId = first.tx_id; let all: CrudEntry[] = []; if (!txId) { - all = [CrudEntry.fromRow(first.rows.item(0))]; + all = [CrudEntry.fromRow(first)]; } else { - const result = await tx.execute( + const result = await tx.getAll( `SELECT id, tx_id, data FROM ${PSInternalTable.CRUD} WHERE tx_id = ? ORDER BY id ASC`, [txId] ); - all = result.rows._array.map((row) => CrudEntry.fromRow(row)); + all = result.map((row) => CrudEntry.fromRow(row)); } const last = all[all.length - 1]; diff --git a/packages/powersync-sdk-react-native/src/sync/stream/ReactNativeRemote.ts b/packages/powersync-sdk-react-native/src/sync/stream/ReactNativeRemote.ts index 4947a4c8..1aeb626e 100644 --- a/packages/powersync-sdk-react-native/src/sync/stream/ReactNativeRemote.ts +++ b/packages/powersync-sdk-react-native/src/sync/stream/ReactNativeRemote.ts @@ -100,6 +100,40 @@ export class ReactNativeRemote extends AbstractRemote { throw error; } - return res.body; + /** + * The can-ndjson-stream does not handle aborted streams well on web. + * This will intercept the readable stream and close the stream if + * aborted. + * TODO this function is duplicated in the Web SDK. + * The common SDK is a bit oblivious to `ReadableStream` classes. + * This should be improved when moving to Websockets + */ + const reader = res.body.getReader(); + const outputStream = new ReadableStream({ + start(controller) { + return processStream(); + + async function processStream(): Promise { + if (signal?.aborted) { + controller.close(); + } + try { + const { done, value } = await reader.read(); + // When no more data needs to be consumed, close the stream + if (done) { + controller.close(); + return; + } + // Enqueue the next data chunk into our target stream + controller.enqueue(value); + return processStream(); + } catch (ex) { + controller.close(); + } + } + } + }); + + return new Response(outputStream).body; } }