From 111a3066dd257c0ed6ad338c704664e9688e0167 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Thu, 8 May 2025 17:05:15 +0200 Subject: [PATCH 1/6] use expo fetch --- .changeset/many-fishes-camp.md | 5 +++++ .../src/sync/stream/ReactNativeRemote.ts | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/many-fishes-camp.md diff --git a/.changeset/many-fishes-camp.md b/.changeset/many-fishes-camp.md new file mode 100644 index 00000000..cde03e21 --- /dev/null +++ b/.changeset/many-fishes-camp.md @@ -0,0 +1,5 @@ +--- +'@powersync/react-native': minor +--- + +Added ability to use Expo's fetch API if available. diff --git a/packages/react-native/src/sync/stream/ReactNativeRemote.ts b/packages/react-native/src/sync/stream/ReactNativeRemote.ts index 3ed14fb6..1c3bc501 100644 --- a/packages/react-native/src/sync/stream/ReactNativeRemote.ts +++ b/packages/react-native/src/sync/stream/ReactNativeRemote.ts @@ -27,7 +27,24 @@ export const STREAMING_POST_TIMEOUT_MS = 30_000; * a polyfill. */ class ReactNativeFetchProvider extends FetchImplementationProvider { + constructor(public logger: ILogger = DEFAULT_REMOTE_LOGGER) { + super(); + } + getFetch(): FetchImplementation { + /** + * From Expo 52, Expo provides a fetch implementation which supports HTTP streams. + * https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api + */ + try { + const f = require('expo/fetch').fetch; + if (f) { + this.logger.debug('Using Expo fetch implementation'); + return f; + } + } catch (e) { + // If expo is not installed, fallback + } return fetch.bind(globalThis); } } @@ -40,7 +57,7 @@ export class ReactNativeRemote extends AbstractRemote { ) { super(connector, logger, { ...(options ?? {}), - fetchImplementation: options?.fetchImplementation ?? new ReactNativeFetchProvider() + fetchImplementation: options?.fetchImplementation ?? new ReactNativeFetchProvider(logger) }); } From f99ba7495fa2f69c01dbb29abfee2acbd23794c2 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 12 May 2025 12:22:52 +0200 Subject: [PATCH 2/6] fix blob import --- packages/react-native/rollup.config.mjs | 9 ++++++--- .../src/sync/stream/ReactNativeRemote.ts | 13 ------------- packages/react-native/vendor/BlobManager.js | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 16 deletions(-) create mode 100644 packages/react-native/vendor/BlobManager.js diff --git a/packages/react-native/rollup.config.mjs b/packages/react-native/rollup.config.mjs index 8da7993c..9a180747 100644 --- a/packages/react-native/rollup.config.mjs +++ b/packages/react-native/rollup.config.mjs @@ -4,9 +4,9 @@ import inject from '@rollup/plugin-inject'; import json from '@rollup/plugin-json'; import nodeResolve from '@rollup/plugin-node-resolve'; import replace from '@rollup/plugin-replace'; +import terser from '@rollup/plugin-terser'; import path from 'path'; import { fileURLToPath } from 'url'; -import terser from '@rollup/plugin-terser'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -34,7 +34,7 @@ export default (commandLineArgs) => { }), json(), nodeResolve({ - preferBuiltins: false, + preferBuiltins: false }), commonjs({}), inject({ @@ -48,6 +48,10 @@ export default (commandLineArgs) => { alias({ entries: [ { find: 'bson', replacement: path.resolve(__dirname, '../../node_modules/bson/lib/bson.rn.cjs') }, + { + find: 'react-native/Libraries/Blob/BlobManager', + replacement: path.resolve(__dirname, './vendor/BlobManager.js') + } ] }), terser() @@ -59,7 +63,6 @@ export default (commandLineArgs) => { 'node-fetch', 'js-logger', 'react-native', - 'react-native/Libraries/Blob/BlobManager', 'react' ] }; diff --git a/packages/react-native/src/sync/stream/ReactNativeRemote.ts b/packages/react-native/src/sync/stream/ReactNativeRemote.ts index 1c3bc501..b2f505d7 100644 --- a/packages/react-native/src/sync/stream/ReactNativeRemote.ts +++ b/packages/react-native/src/sync/stream/ReactNativeRemote.ts @@ -32,19 +32,6 @@ class ReactNativeFetchProvider extends FetchImplementationProvider { } getFetch(): FetchImplementation { - /** - * From Expo 52, Expo provides a fetch implementation which supports HTTP streams. - * https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api - */ - try { - const f = require('expo/fetch').fetch; - if (f) { - this.logger.debug('Using Expo fetch implementation'); - return f; - } - } catch (e) { - // If expo is not installed, fallback - } return fetch.bind(globalThis); } } diff --git a/packages/react-native/vendor/BlobManager.js b/packages/react-native/vendor/BlobManager.js new file mode 100644 index 00000000..543173cc --- /dev/null +++ b/packages/react-native/vendor/BlobManager.js @@ -0,0 +1,19 @@ +/** + * The current Rollup configuration targets a CommonJs output. + * This translates import statements to require statements. + * React native recently shifted to ESM exports. + * https://github.com/facebook/react-native/pull/48761/files + * This causes requiring this module to return an object + * of the form + * ```javascript + * { + * _esModule: true, + * default: BlobManager + * } + * ``` + * This wrapper provides a small shim to conditionally return the default export of the module. + */ +const BlobManager = require('react-native/Libraries/Blob/BlobManager'); +const interop = (mod) => (mod && mod.__esModule ? mod.default : mod); + +export default interop(BlobManager); From c413fe6358861e2ffc7cd5642cc5d211171af7f8 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 12 May 2025 12:24:10 +0200 Subject: [PATCH 3/6] update changeset --- .changeset/many-fishes-camp.md | 8 ++++++-- packages/react-native/vendor/BlobManager.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.changeset/many-fishes-camp.md b/.changeset/many-fishes-camp.md index cde03e21..ec44b623 100644 --- a/.changeset/many-fishes-camp.md +++ b/.changeset/many-fishes-camp.md @@ -1,5 +1,9 @@ --- -'@powersync/react-native': minor +'@powersync/react-native': patch --- -Added ability to use Expo's fetch API if available. +Fixed issue where CRUD uploads could fail with the error + +``` +Exception: require(_dependencyMap[11], "rea(...)/BlobManager").createFromOptions is not a function (it is undefined) +``` diff --git a/packages/react-native/vendor/BlobManager.js b/packages/react-native/vendor/BlobManager.js index 543173cc..f1a5cc62 100644 --- a/packages/react-native/vendor/BlobManager.js +++ b/packages/react-native/vendor/BlobManager.js @@ -4,7 +4,7 @@ * React native recently shifted to ESM exports. * https://github.com/facebook/react-native/pull/48761/files * This causes requiring this module to return an object - * of the form + * of the form: * ```javascript * { * _esModule: true, From 5b0379f1083a2a99f0c76117a305b90dbda5549f Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 12 May 2025 12:36:04 +0200 Subject: [PATCH 4/6] cleanup logger --- packages/react-native/src/sync/stream/ReactNativeRemote.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/react-native/src/sync/stream/ReactNativeRemote.ts b/packages/react-native/src/sync/stream/ReactNativeRemote.ts index b2f505d7..3ed14fb6 100644 --- a/packages/react-native/src/sync/stream/ReactNativeRemote.ts +++ b/packages/react-native/src/sync/stream/ReactNativeRemote.ts @@ -27,10 +27,6 @@ export const STREAMING_POST_TIMEOUT_MS = 30_000; * a polyfill. */ class ReactNativeFetchProvider extends FetchImplementationProvider { - constructor(public logger: ILogger = DEFAULT_REMOTE_LOGGER) { - super(); - } - getFetch(): FetchImplementation { return fetch.bind(globalThis); } @@ -44,7 +40,7 @@ export class ReactNativeRemote extends AbstractRemote { ) { super(connector, logger, { ...(options ?? {}), - fetchImplementation: options?.fetchImplementation ?? new ReactNativeFetchProvider(logger) + fetchImplementation: options?.fetchImplementation ?? new ReactNativeFetchProvider() }); } From 78eac6e989ea6f63467ace52b793bb9762a381eb Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 12 May 2025 12:44:06 +0200 Subject: [PATCH 5/6] cjs export --- packages/react-native/vendor/BlobManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/vendor/BlobManager.js b/packages/react-native/vendor/BlobManager.js index f1a5cc62..441e87c2 100644 --- a/packages/react-native/vendor/BlobManager.js +++ b/packages/react-native/vendor/BlobManager.js @@ -16,4 +16,4 @@ const BlobManager = require('react-native/Libraries/Blob/BlobManager'); const interop = (mod) => (mod && mod.__esModule ? mod.default : mod); -export default interop(BlobManager); +module.exports = interop(BlobManager); From f8409fdaf09d86c66da17335ec4656e604c075af Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 12 May 2025 12:55:36 +0200 Subject: [PATCH 6/6] cleanup --- packages/react-native/vendor/BlobManager.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/react-native/vendor/BlobManager.js b/packages/react-native/vendor/BlobManager.js index 441e87c2..2fc2660b 100644 --- a/packages/react-native/vendor/BlobManager.js +++ b/packages/react-native/vendor/BlobManager.js @@ -16,4 +16,12 @@ const BlobManager = require('react-native/Libraries/Blob/BlobManager'); const interop = (mod) => (mod && mod.__esModule ? mod.default : mod); -module.exports = interop(BlobManager); +/** + * Using an ESM export here is important. Rollup compiles this to + * ```javascript + * const BlobManager = require('react-native/Libraries/Blob/BlobManager'); + * const interop = (mod) => (mod && mod.__esModule ? mod.default : mod); + * var BlobManager$1 = interop(BlobManager); + * ``` + */ +export default interop(BlobManager);