diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index a7179a5..ff622f3 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -17,7 +17,7 @@ cd bdk-ffi/ git fetch --all git checkout cd .. -just apply-submodule-patches +just submodule-apply-patch just clean just build-tarball cd tests/ @@ -30,15 +30,18 @@ just pod-install just run-ios ``` -- [ ] Create the tag -- [ ] Build the tarball -- [ ] Sign the artifact -- [ ] Create the release on GitHub -- [ ] Let people know! +- [ ] Bump the version in `package.json` and merge it +- [ ] Create and push the signed tag, pointing at the commit with the bumped + version (the workflow fails if the tag and `package.json` disagree). + This triggers the release workflow, which builds the binaries, creates + the GitHub Release with checksums, and publishes to npm ```shell git tag v1.1.0 --sign --edit git push upstream v1.1.0 ``` +- [ ] Verify the release workflow succeeded (GitHub Release assets + npm) +- [ ] Let people know! + - [ ] Bump the `next` version on `master` diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index a613332..e96ee66 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,36 +1,18 @@ name: Setup -description: Setup Node.js and install dependencies +description: Setup Node.js, pnpm, and install dependencies runs: using: composite steps: + - name: Setup pnpm + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - - - name: Restore dependencies - id: yarn-cache - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - path: | - **/node_modules - .yarn/install-state.gz - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} - restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - ${{ runner.os }}-yarn- + cache: pnpm - name: Install dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - run: yarn install --immutable + run: pnpm install --frozen-lockfile --ignore-scripts shell: bash - - - name: Cache dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - path: | - **/node_modules - .yarn/install-state.gz - key: ${{ steps.yarn-cache.outputs.cache-primary-key }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aab59d3..133bff7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,14 +2,18 @@ name: CI on: push: branches: - - main + - master pull_request: branches: - - main + - master merge_group: types: - checks_requested +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: lint: runs-on: ubuntu-latest @@ -21,133 +25,75 @@ jobs: uses: ./.github/actions/setup - name: Lint files - run: yarn lint - - - name: Typecheck files - run: yarn typecheck + run: pnpm lint - test: + build-android: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive - name: Setup uses: ./.github/actions/setup - - name: Run unit tests - run: yarn test --maxWorkers=2 --coverage - - build-library: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Install just + run: sudo apt-get update && sudo apt-get install -y just - - name: Setup - uses: ./.github/actions/setup + - name: Apply bdk-ffi patches + run: just submodule-apply-patch - - name: Build package - run: yarn prepare + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: 1.91.1 + target: aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android - build-android: - runs-on: ubuntu-latest - env: - TURBO_CACHE_DIR: .turbo/android - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Cache Rust build + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + workspaces: bdk-ffi/bdk-ffi - - name: Setup - uses: ./.github/actions/setup + - name: Install cargo-ndk + run: cargo install cargo-ndk - - name: Cache turborepo for Android - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - path: ${{ env.TURBO_CACHE_DIR }} - key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turborepo-android- - - - name: Check turborepo cache for Android - run: | - TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status") - - if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then - echo "turbo_cache_hit=1" >> $GITHUB_ENV - fi - - - name: Install JDK - if: env.turbo_cache_hit != 1 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - distribution: 'zulu' - java-version: '17' + - name: Build Android binaries and generate bindings + run: pnpm ubrn:android --config ubrn.config.yaml - - name: Finalize Android SDK - if: env.turbo_cache_hit != 1 - run: | - /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" + - name: Typecheck files + run: pnpm typecheck - - name: Cache Gradle - if: env.turbo_cache_hit != 1 - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - path: | - ~/.gradle/wrapper - ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Build example for Android - env: - JAVA_OPTS: "-XX:MaxHeapSize=6g" - run: | - yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" + - name: Build package + run: pnpm prepare build-ios: runs-on: macos-latest - env: - XCODE_VERSION: 16.2 - TURBO_CACHE_DIR: .turbo/ios steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive - name: Setup uses: ./.github/actions/setup - - name: Cache turborepo for iOS - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + - name: Install just + run: brew install just + + - name: Apply bdk-ffi patches + run: just submodule-apply-patch + + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 with: - path: ${{ env.TURBO_CACHE_DIR }} - key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turborepo-ios- - - - name: Check turborepo cache for iOS - run: | - TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status") - - if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then - echo "turbo_cache_hit=1" >> $GITHUB_ENV - fi - - - name: Use appropriate Xcode version - if: env.turbo_cache_hit != 1 - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 + toolchain: 1.91.1 + target: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios + + - name: Cache Rust build + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: - xcode-version: ${{ env.XCODE_VERSION }} - - - name: Install cocoapods - if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' - run: | - cd example - bundle install - bundle exec pod repo update --verbose - bundle exec pod install --project-directory=ios - - - name: Build example for iOS - run: | - yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" + workspaces: bdk-ffi/bdk-ffi + + - name: Build iOS binaries and generate bindings + run: pnpm ubrn:ios --config ubrn.config.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b1f6431 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,227 @@ +name: Release +# Builds the prebuilt native binaries, attaches them to a GitHub Release, +# and publishes the package to npm. Triggered by pushing a version tag: +# +# git tag v1.1.0 --sign --edit +# git push upstream v1.1.0 +# +# npm publishing uses trusted publishing (OIDC): the repository must be +# configured as a trusted publisher for the package on npmjs.com. No npm +# token secret is required. +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + check-version: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Verify tag matches package.json version + id: version + run: | + VERSION=$(node -p "require('./package.json').version") + if [ "$GITHUB_REF_NAME" != "v$VERSION" ]; then + echo "Tag $GITHUB_REF_NAME does not match package.json version $VERSION" >&2 + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + build-android: + runs-on: ubuntu-latest + needs: check-version + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + + - name: Setup + uses: ./.github/actions/setup + + - name: Install just + run: sudo apt-get update && sudo apt-get install -y just + + - name: Apply bdk-ffi patches + run: just submodule-apply-patch + + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: 1.91.1 + target: aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android + + - name: Cache Rust build + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + workspaces: bdk-ffi/bdk-ffi + + - name: Install cargo-ndk + run: cargo install cargo-ndk + + - name: Build Android binaries and generate bindings + run: pnpm ubrn:android --config ubrn.config.yaml + + - name: Package Android artifacts + run: zip -r android-artifacts.zip android/src/main/jniLibs cpp src/generated + + - name: Upload Android artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: android-artifacts + path: android-artifacts.zip + if-no-files-found: error + + build-ios: + runs-on: macos-latest + needs: check-version + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + + - name: Setup + uses: ./.github/actions/setup + + - name: Install just + run: brew install just + + - name: Apply bdk-ffi patches + run: just submodule-apply-patch + + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: 1.91.1 + target: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios + + - name: Cache Rust build + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + workspaces: bdk-ffi/bdk-ffi + + - name: Build iOS binaries and generate bindings + run: pnpm ubrn:ios --config ubrn.config.yaml + + - name: Package iOS artifacts + run: zip -r ios-artifacts.zip BdkRnFramework.xcframework cpp src/generated + + - name: Upload iOS artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ios-artifacts + path: ios-artifacts.zip + if-no-files-found: error + + release: + runs-on: ubuntu-latest + needs: [check-version, build-android, build-ios] + permissions: + contents: write + id-token: write + attestations: write + outputs: + android_checksum: ${{ steps.checksums.outputs.android }} + ios_checksum: ${{ steps.checksums.outputs.ios }} + steps: + - name: Download artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: '*-artifacts' + merge-multiple: true + + - name: Compute checksums + id: checksums + run: | + ANDROID_CHECKSUM=$(sha256sum android-artifacts.zip | cut -d' ' -f1) + IOS_CHECKSUM=$(sha256sum ios-artifacts.zip | cut -d' ' -f1) + echo "android=$ANDROID_CHECKSUM" >> "$GITHUB_OUTPUT" + echo "ios=$IOS_CHECKSUM" >> "$GITHUB_OUTPUT" + jq -n --arg android "$ANDROID_CHECKSUM" --arg ios "$IOS_CHECKSUM" \ + '{android: $android, ios: $ios}' > checksums.json + + - name: Attest build provenance + uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 + with: + subject-path: | + android-artifacts.zip + ios-artifacts.zip + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + TAG="$GITHUB_REF_NAME" + # Releases are immutable: re-uploading assets would break checksum + # verification for versions already published to npm. + if gh release view "$TAG" > /dev/null 2>&1; then + echo "Release $TAG already exists; refusing to modify it." >&2 + exit 1 + fi + { + echo "## Checksums (SHA-256)" + echo "" + echo '```' + echo "android-artifacts.zip ${{ steps.checksums.outputs.android }}" + echo "ios-artifacts.zip ${{ steps.checksums.outputs.ios }}" + echo '```' + } > notes.md + gh release create "$TAG" \ + android-artifacts.zip ios-artifacts.zip checksums.json \ + --title "$TAG" \ + --notes-file notes.md \ + --verify-tag + + publish-npm: + runs-on: ubuntu-latest + needs: [check-version, release] + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Update npm for trusted publishing + run: npm install -g npm@latest + + - name: Download artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: '*-artifacts' + merge-multiple: true + + - name: Extract generated bindings and binaries + run: | + unzip -o -q android-artifacts.zip + unzip -o -q ios-artifacts.zip + + - name: Build package + run: pnpm prepare + + - name: Inject artifact checksums + run: | + jq --arg android "${{ needs.release.outputs.android_checksum }}" \ + --arg ios "${{ needs.release.outputs.ios_checksum }}" \ + '.checksums = {android: $android, ios: $ios} | del(.scripts.prepare)' \ + package.json > package.json.tmp + mv package.json.tmp package.json + + - name: Remove binaries from the npm tarball + run: rm -rf android/src/main/jniLibs BdkRnFramework.xcframework ./*.zip + + - name: Publish to npm + run: npm publish --access public --ignore-scripts diff --git a/.gitignore b/.gitignore index 9b79adb..d6b1478 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,12 @@ nitrogen/ target/ libbdkffi.a +# Prebuilt native artifacts (built by CI, downloaded by scripts/postinstall.js) +android/src/main/jniLibs/ +android-artifacts.zip +ios-artifacts.zip +checksums.json + # Other temp/ diff --git a/README.md b/README.md index 5272480..46e2a41 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,32 @@ The code in this repository is mostly comprised of: The core Rust code that is exposed to the React Native language bindings actually resides in the [bdk-ffi](https://github.com/bitcoindevkit/bdk-ffi) repository. This repo pulls it in as a submodule. +## Installing + +1. Install the package + +```shell +npm install bdk-rn +# or in an Expo project +npx expo install bdk-rn +``` + +2. In an Expo project, make sure the plugin is in `app.json`, and run `prebuild`. + +```json +{ + "expo": { + "plugins": ["bdk-rn"] + } +} +``` + +```bash +npx expo prebuild +``` + +Warning: If you are using pnpm v10+, run pnpm approve-builds and select `bdk-rn` to allow the postinstall script to download the prebuilt native binaries. + ## Exploring the Example Apps To take a look at the API exposed in this library, you can run our example applications. [Read the docs on this here](https://bitcoindevkit.github.io/bdk-rn/example-apps/), and [find our example apps here](https://github.com/thunderbiscuit/bdk-rn-example-apps). @@ -26,7 +52,7 @@ You can easily build the library from source for quick development and iteration - Rust toolchain (stable 1.91.1) - `just` CLI tool: https://github.com/casey/just - `cargo-ndk` (`cargo install cargo-ndk`) -- For iOS: CocoaPods >= 1.13: ()`brew install cocoapods`) +- For iOS: CocoaPods >= 1.13 (`brew install cocoapods`) ### Build Instructions @@ -36,11 +62,14 @@ git clone git@github.com:bitcoindevkit/bdk-rn.git cd bdk-rn # Install compilation targets -rustup target add aarch64-linux-android aarch64-apple-ios aarch64-apple-ios-sim +rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android \ + aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios + +# Initialize the bdk-ffi submodule and apply the async-sync patches +just submodule-init +just submodule-apply-patch -# Build the library and create tarball (includes both Android and iOS) -just rename-library -# Build both platforms and creates bdk-rn-VERSION.tgz +# Build both platforms and create the bdk-rn-VERSION.tgz tarball just build-tarball ``` @@ -52,10 +81,7 @@ You can use the following workflow to run the tests locally on an Android emulat ```shell # Build and package the library -just build-android - -# Create the tarball bdk-rn--next.tgz -npm pack +just build-tarball-android # Install dependencies in the tests cd tests @@ -73,7 +99,7 @@ npm run android ### Test Development Workflow 1. Make changes to `bdk-ffi` or the library code -2. Build and package: `just build-android && npm pack` +2. Build and package: `just build-tarball-android` 3. Update tests: `cd tests && npm install` 4. Add or modify tests in the tests 5. Run the app and verify test results in logcat or in the emulator diff --git a/app.plugin.js b/app.plugin.js new file mode 100644 index 0000000..7590f2e --- /dev/null +++ b/app.plugin.js @@ -0,0 +1 @@ +module.exports = require('./plugin'); diff --git a/docs/build.md b/docs/build.md index 4b5092d..1690db6 100644 --- a/docs/build.md +++ b/docs/build.md @@ -5,7 +5,7 @@ You can easily build the library from source for quick development and iteration - Rust toolchain (stable 1.91.1) - `just` CLI tool: https://github.com/casey/just - `cargo-ndk` (`cargo install cargo-ndk`) -- For iOS: CocoaPods >= 1.13: ()`brew install cocoapods`) +- For iOS: CocoaPods >= 1.13 (`brew install cocoapods`) ### Build Instructions @@ -15,7 +15,11 @@ git clone git@github.com:bitcoindevkit/bdk-rn.git cd bdk-rn # Install compilation targets -rustup target add aarch64-linux-android aarch64-apple-ios aarch64-apple-ios-sim +rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android \ + aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios + +# Initialize the bdk-ffi submodule +just submodule-init # Apply the patch to the submodule (required for async Electrum and Esplora methods) just submodule-apply-patch @@ -55,7 +59,7 @@ just test-android ## Test Development Workflow 1. Make changes to `bdk-ffi` or the library code -2. Build and package: `just build-android && npm pack` +2. Build and package: `just build-tarball-android` 3. Update tests: `cd tests && npm install` 4. Add or modify tests in the tests 5. Run the app and verify test results in logcat or in the emulator diff --git a/package.json b/package.json index 6dfd5ea..47776e5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "cpp", "*.podspec", "BdkRnFramework.xcframework", - "react-native.config.js", + "app.plugin.js", + "plugin", + "scripts", "!ios/build", "!android/build", "!android/gradle", @@ -42,8 +44,13 @@ "lint": "eslint \"**/*.{js,ts,tsx}\"", "clean": "del-cli android/build lib", "prepare": "bob build", + "postinstall": "node scripts/postinstall.js", "release": "release-it --only-version" }, + "checksums": { + "android": "", + "ios": "" + }, "keywords": [ "react-native", "ios", @@ -64,6 +71,7 @@ }, "devDependencies": { "@commitlint/config-conventional": "^19.6.0", + "@expo/config-plugins": "^54.0.5", "@eslint/compat": "^1.2.7", "@eslint/eslintrc": "^3.3.0", "@eslint/js": "^9.22.0", @@ -175,6 +183,6 @@ "version": "0.52.0" }, "dependencies": { - "uniffi-bindgen-react-native": "git+https://github.com/jhugman/uniffi-bindgen-react-native.git#0.30.0-1" + "uniffi-bindgen-react-native": "0.30.0-1" } } diff --git a/plugin/index.js b/plugin/index.js new file mode 100644 index 0000000..361972f --- /dev/null +++ b/plugin/index.js @@ -0,0 +1,70 @@ +/** + * Expo config plugin for bdk-rn. + * + * bdk-rn ships as a TurboModule with prebuilt native binaries (downloaded by + * the package's postinstall script), so regular React Native autolinking — + * which `expo prebuild` runs automatically — does most of the work. This + * plugin only validates and enforces the requirements the library cannot + * autolink its way around: + * + * - The React Native New Architecture must be enabled (bdk-rn is a + * JSI/TurboModule-only library with no old-architecture fallback). + * - `android.useAndroidX=true` must be set in gradle.properties. + * + * Usage in app.json / app.config.js: + * + * { "expo": { "plugins": ["bdk-rn"] } } + * + * Note: bdk-rn contains custom native code, so it does not work in Expo Go. + * Use a development build (`expo run:android` / `expo run:ios`) or EAS Build. + */ + +'use strict'; + +const pkg = require('../package.json'); + +let plugins; +try { + plugins = require('@expo/config-plugins'); +} catch (error) { + throw new Error( + `bdk-rn: unable to load '@expo/config-plugins'. The bdk-rn config plugin ` + + `must be used inside an Expo project. (${error.message})` + ); +} + +const { createRunOncePlugin, withGradleProperties } = plugins; + +function setGradleProperty(properties, key, value) { + const result = properties.filter( + (item) => !(item.type === 'property' && item.key === key) + ); + result.push({ type: 'property', key, value }); + return result; +} + +const withBdkRn = (config) => { + if (config.newArchEnabled === false) { + throw new Error( + 'bdk-rn requires the React Native New Architecture, but "newArchEnabled" ' + + 'is set to false in your app config. Remove it (it defaults to enabled ' + + 'since Expo SDK 52) or set it to true.' + ); + } + + return withGradleProperties(config, (gradleConfig) => { + gradleConfig.modResults = setGradleProperty( + gradleConfig.modResults, + 'newArchEnabled', + 'true' + ); + gradleConfig.modResults = setGradleProperty( + gradleConfig.modResults, + 'android.useAndroidX', + 'true' + ); + return gradleConfig; + }); +}; + +module.exports = createRunOncePlugin(withBdkRn, pkg.name, pkg.version); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a077be..3fb74bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: uniffi-bindgen-react-native: - specifier: git+https://github.com/jhugman/uniffi-bindgen-react-native.git#0.30.0-1 - version: https://codeload.github.com/jhugman/uniffi-bindgen-react-native/tar.gz/21702c20a277a622776505744b67d83fc0f48c8a + specifier: 0.30.0-1 + version: 0.30.0-1 devDependencies: '@commitlint/config-conventional': specifier: ^19.6.0 @@ -24,6 +24,9 @@ importers: '@eslint/js': specifier: ^9.22.0 version: 9.39.4 + '@expo/config-plugins': + specifier: ^54.0.5 + version: 54.0.5 '@react-native-community/cli': specifier: 15.0.0-alpha.2 version: 15.0.0-alpha.2(typescript@5.9.3) @@ -90,6 +93,9 @@ packages: '@ark/util@0.56.0': resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==} + '@babel/code-frame@7.10.4': + resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -200,6 +206,10 @@ packages: resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} + '@babel/highlight@7.25.9': + resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.29.3': resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} engines: {node: '>=6.0.0'} @@ -921,6 +931,21 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@expo/config-plugins@54.0.5': + resolution: {integrity: sha512-aWQ3sViNRoQWw6So4A2qhWCt24CuGBK6MrRHI1AG+V6/NQAjIZCHaSvcXK2gXKpmisRhTSUWaKPIgLJQFB+AeQ==} + + '@expo/config-types@54.0.10': + resolution: {integrity: sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==} + + '@expo/json-file@10.0.16': + resolution: {integrity: sha512-fcVkWEj+hLuP2yt5W0aw6LmDRqSPWDLUSxOMcmFeV+algmIF59sQVKCwB9btjQLd4V6x9N0pISkQEkBubUHrCw==} + + '@expo/plist@0.4.9': + resolution: {integrity: sha512-MPVpmKGfnQEnrCzgxuXcmPP/y/t6AVm+DcSb2Myp21LKWv1N3l8uFxMggesfF4ixAxkRlGmMMx9GyDC9M+XklQ==} + + '@expo/sdk-runtime-versions@1.0.0': + resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -1446,6 +1471,14 @@ packages: resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} + '@xmldom/xmldom@0.8.13': + resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} + engines: {node: '>=10.0.0'} + + '@xmldom/xmldom@0.9.10': + resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} + engines: {node: '>=14.6'} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -1673,6 +1706,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1688,6 +1725,10 @@ packages: before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -1695,12 +1736,23 @@ packages: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} + bplist-creator@0.1.0: + resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} + + bplist-parser@0.3.1: + resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} + engines: {node: '>= 5.10.0'} + brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1774,6 +1826,10 @@ packages: caniuse-lite@1.0.30001792: resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2641,6 +2697,10 @@ packages: resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} engines: {node: '>= 14'} + getenv@2.0.0: + resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} + engines: {node: '>=6'} + git-raw-commits@4.0.0: resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} engines: {node: '>=16'} @@ -2676,6 +2736,10 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -2730,6 +2794,10 @@ packages: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3480,6 +3548,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3635,6 +3707,10 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -3930,6 +4006,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3953,6 +4033,10 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + plist@3.1.1: + resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} + engines: {node: '>=10.4.0'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -4236,6 +4320,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} + engines: {node: '>=11.0.0'} + scheduler@0.25.0: resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} @@ -4323,6 +4411,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-plist@1.3.1: + resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -4342,6 +4433,10 @@ packages: resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} engines: {node: '>=6'} + slugify@1.6.9: + resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==} + engines: {node: '>=8.0.0'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -4414,6 +4509,10 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stream-buffers@2.2.0: + resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} + engines: {node: '>= 0.10.0'} + string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -4504,6 +4603,10 @@ packages: resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -4702,9 +4805,8 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - uniffi-bindgen-react-native@https://codeload.github.com/jhugman/uniffi-bindgen-react-native/tar.gz/21702c20a277a622776505744b67d83fc0f48c8a: - resolution: {tarball: https://codeload.github.com/jhugman/uniffi-bindgen-react-native/tar.gz/21702c20a277a622776505744b67d83fc0f48c8a} - version: 0.30.0-1 + uniffi-bindgen-react-native@0.30.0-1: + resolution: {integrity: sha512-rffyNPTQ2qFx7deaLXYVBhXcy4pDrjtEc28Pc1Rwt532IGEQquT7eNvvLKIgX6G5xODi6Yfni1zFs5u+O2QL1A==} hasBin: true universal-user-agent@6.0.1: @@ -4746,6 +4848,11 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@7.0.3: + resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} @@ -4860,10 +4967,26 @@ packages: utf-8-validate: optional: true + xcode@3.0.1: + resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} + engines: {node: '>=10.0.0'} + xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} + xml2js@0.6.0: + resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -4922,6 +5045,10 @@ snapshots: '@ark/util@0.56.0': {} + '@babel/code-frame@7.10.4': + dependencies: + '@babel/highlight': 7.25.9 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -5084,6 +5211,13 @@ snapshots: '@babel/template': 7.28.6 '@babel/types': 7.29.0 + '@babel/highlight@7.25.9': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/parser@7.29.3': dependencies: '@babel/types': 7.29.0 @@ -5970,6 +6104,40 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@expo/config-plugins@54.0.5': + dependencies: + '@expo/config-types': 54.0.10 + '@expo/json-file': 10.0.16 + '@expo/plist': 0.4.9 + '@expo/sdk-runtime-versions': 1.0.0 + chalk: 4.1.2 + debug: 4.4.3 + getenv: 2.0.0 + glob: 13.0.6 + resolve-from: 5.0.0 + semver: 7.8.0 + slash: 3.0.0 + slugify: 1.6.9 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + + '@expo/config-types@54.0.10': {} + + '@expo/json-file@10.0.16': + dependencies: + '@babel/code-frame': 7.10.4 + json5: 2.2.3 + + '@expo/plist@0.4.9': + dependencies: + '@xmldom/xmldom': 0.8.13 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + + '@expo/sdk-runtime-versions@1.0.0': {} + '@hapi/hoek@9.3.0': {} '@hapi/topo@5.1.0': @@ -6817,6 +6985,10 @@ snapshots: '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 + '@xmldom/xmldom@0.8.13': {} + + '@xmldom/xmldom@0.9.10': {} + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -7113,6 +7285,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.10.29: {} @@ -7121,6 +7295,8 @@ snapshots: before-after-hook@2.2.3: {} + big-integer@1.6.52: {} + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -7131,13 +7307,21 @@ snapshots: dependencies: ansi-align: 3.0.1 camelcase: 8.0.0 - chalk: 5.4.1 + chalk: 5.6.2 cli-boxes: 3.0.0 string-width: 7.2.0 type-fest: 4.41.0 widest-line: 5.0.0 wrap-ansi: 9.0.2 + bplist-creator@0.1.0: + dependencies: + stream-buffers: 2.2.0 + + bplist-parser@0.3.1: + dependencies: + big-integer: 1.6.52 + brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 @@ -7147,6 +7331,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -7220,6 +7408,12 @@ snapshots: caniuse-lite@1.0.30001792: {} + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -8218,6 +8412,8 @@ snapshots: transitivePeerDependencies: - supports-color + getenv@2.0.0: {} + git-raw-commits@4.0.0: dependencies: dargs: 8.1.0 @@ -8266,6 +8462,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -8333,6 +8535,8 @@ snapshots: has-bigints@1.1.0: {} + has-flag@3.0.0: {} + has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -9209,7 +9413,7 @@ snapshots: log-symbols@6.0.0: dependencies: - chalk: 5.4.1 + chalk: 5.6.2 is-unicode-supported: 1.3.0 logkitty@0.7.1: @@ -9224,6 +9428,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.5.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -9475,6 +9681,10 @@ snapshots: mimic-function@5.0.1: {} + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + minimatch@3.1.5: dependencies: brace-expansion: 1.1.14 @@ -9660,7 +9870,7 @@ snapshots: ora@8.1.1: dependencies: - chalk: 5.4.1 + chalk: 5.6.2 cli-cursor: 5.0.0 cli-spinners: 2.9.2 is-interactive: 2.0.0 @@ -9742,7 +9952,7 @@ snapshots: ky: 1.14.3 registry-auth-token: 5.1.1 registry-url: 6.0.1 - semver: 7.6.3 + semver: 7.8.0 parent-module@1.0.1: dependencies: @@ -9793,6 +10003,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.2 + minipass: 7.1.3 + path-type@4.0.0: {} path-type@5.0.0: {} @@ -9807,6 +10022,12 @@ snapshots: dependencies: find-up: 4.1.0 + plist@3.1.1: + dependencies: + '@xmldom/xmldom': 0.9.10 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + possible-typed-array-names@1.1.0: {} prelude-ls@1.2.1: {} @@ -10202,6 +10423,8 @@ snapshots: safer-buffer@2.1.2: {} + sax@1.6.1: {} + scheduler@0.25.0: {} semver@6.3.1: {} @@ -10311,6 +10534,12 @@ snapshots: signal-exit@4.1.0: {} + simple-plist@1.3.1: + dependencies: + bplist-creator: 0.1.0 + bplist-parser: 0.3.1 + plist: 3.1.1 + sisteransi@1.0.5: {} slash@3.0.0: {} @@ -10325,6 +10554,8 @@ snapshots: astral-regex: 1.0.0 is-fullwidth-code-point: 2.0.0 + slugify@1.6.9: {} + smart-buffer@4.2.0: {} socks-proxy-agent@8.0.5: @@ -10393,6 +10624,8 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stream-buffers@2.2.0: {} + string-length@4.0.2: dependencies: char-regex: 1.0.2 @@ -10500,6 +10733,10 @@ snapshots: sudo-prompt@9.2.1: {} + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -10669,7 +10906,7 @@ snapshots: unicorn-magic@0.1.0: {} - uniffi-bindgen-react-native@https://codeload.github.com/jhugman/uniffi-bindgen-react-native/tar.gz/21702c20a277a622776505744b67d83fc0f48c8a: {} + uniffi-bindgen-react-native@0.30.0-1: {} universal-user-agent@6.0.1: {} @@ -10688,14 +10925,14 @@ snapshots: update-notifier@7.3.1: dependencies: boxen: 8.0.1 - chalk: 5.4.1 + chalk: 5.6.2 configstore: 7.1.0 is-in-ci: 1.0.0 is-installed-globally: 1.0.0 is-npm: 6.1.0 latest-version: 9.0.0 pupa: 3.3.0 - semver: 7.6.3 + semver: 7.8.0 xdg-basedir: 5.1.0 uri-js@4.4.1: @@ -10708,6 +10945,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@7.0.3: {} + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -10833,8 +11072,22 @@ snapshots: ws@7.5.10: {} + xcode@3.0.1: + dependencies: + simple-plist: 1.3.1 + uuid: 7.0.3 + xdg-basedir@5.1.0: {} + xml2js@0.6.0: + dependencies: + sax: 1.6.1 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + xmlbuilder@15.1.1: {} + y18n@4.0.3: {} y18n@5.0.8: {} diff --git a/scripts/postinstall.js b/scripts/postinstall.js new file mode 100644 index 0000000..f234afc --- /dev/null +++ b/scripts/postinstall.js @@ -0,0 +1,218 @@ +#!/usr/bin/env node +/** + * Downloads the prebuilt native binaries for bdk-rn from GitHub Releases. + * + * The published npm package does not contain the Rust binaries (Android + * static libs and the iOS xcframework) to keep the tarball small. This + * script runs on `npm install` in the consumer's project, downloads the + * artifacts for the matching release tag, verifies their SHA-256 checksums + * (baked into package.json at publish time by CI), and extracts them into + * the package root where the podspec and CMakeLists expect them. + * + * Environment variables: + * BDK_RN_SKIP_POSTINSTALL - set to any value to skip this script entirely. + * BDK_RN_BINARY_BASE_URL - override the download base URL (mirror). + * Artifacts are fetched from //. + * BDK_RN_ARTIFACTS_DIR - directory containing the artifact zips + * (offline installs / CI caches). Zips are + * copied from there instead of downloaded. + * + * Note: downloads use plain https and do not honor HTTP(S)_PROXY. Behind a + * proxy, pre-fetch the zips and point BDK_RN_ARTIFACTS_DIR at them, or set + * BDK_RN_SKIP_POSTINSTALL and provide the binaries yourself. + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const https = require('https'); +const crypto = require('crypto'); +const { execFileSync } = require('child_process'); + +const PACKAGE_ROOT = path.resolve(__dirname, '..'); +const packageJson = require(path.join(PACKAGE_ROOT, 'package.json')); + +const DEFAULT_BASE_URL = + 'https://github.com/bitcoindevkit/bdk-rn/releases/download'; +const BASE_URL = process.env.BDK_RN_BINARY_BASE_URL || DEFAULT_BASE_URL; +const MAX_REDIRECTS = 5; + +const ARTIFACTS = [ + { + name: 'Android', + zipName: 'android-artifacts.zip', + checksumKey: 'android', + existsPath: path.join(PACKAGE_ROOT, 'android', 'src', 'main', 'jniLibs'), + }, + { + name: 'iOS', + zipName: 'ios-artifacts.zip', + checksumKey: 'ios', + existsPath: path.join(PACKAGE_ROOT, 'BdkRnFramework.xcframework'), + // iOS builds only happen on macOS hosts; skip the download elsewhere. + platformGuard: 'darwin', + }, +]; + +function log(message) { + console.log(`[bdk-rn] ${message}`); +} + +function download(url, destination, redirectCount = 0) { + return new Promise((resolve, reject) => { + if (redirectCount > MAX_REDIRECTS) { + reject(new Error(`Too many redirects while downloading ${url}`)); + return; + } + https + .get(url, (response) => { + if ( + response.statusCode >= 300 && + response.statusCode < 400 && + response.headers.location + ) { + response.resume(); + resolve( + download(response.headers.location, destination, redirectCount + 1) + ); + return; + } + if (response.statusCode !== 200) { + response.resume(); + reject( + new Error(`Download failed (HTTP ${response.statusCode}): ${url}`) + ); + return; + } + const file = fs.createWriteStream(destination); + response.pipe(file); + file.on('finish', () => file.close(resolve)); + file.on('error', (error) => { + fs.rmSync(destination, { force: true }); + reject(error); + }); + }) + .on('error', reject); + }); +} + +function sha256(filePath) { + return new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256'); + const stream = fs.createReadStream(filePath); + stream.on('data', (chunk) => hash.update(chunk)); + stream.on('end', () => resolve(hash.digest('hex'))); + stream.on('error', reject); + }); +} + +async function verifyChecksum(filePath, expected, name) { + const actual = await sha256(filePath); + if (actual !== expected) { + throw new Error( + `Checksum mismatch for ${name}:\n expected ${expected}\n got ${actual}\n` + + 'The downloaded artifact does not match the checksum recorded at publish time. ' + + 'Refusing to install it.' + ); + } +} + +function extractZip(zipPath, destination) { + if (process.platform === 'win32') { + // tar.exe (bsdtar) ships with Windows 10 1803+ and extracts zip archives. + execFileSync('tar', ['-xf', zipPath, '-C', destination], { + stdio: 'inherit', + }); + } else { + execFileSync('unzip', ['-o', '-q', zipPath, '-d', destination], { + stdio: 'inherit', + }); + } +} + +function manualInstructions(releaseTag) { + return ( + `You can install the binaries manually:\n` + + ` 1. Download the artifact zips from ` + + `https://github.com/bitcoindevkit/bdk-rn/releases/tag/${releaseTag}\n` + + ` 2. Verify their SHA-256 checksums against the "checksums" field in ` + + `node_modules/bdk-rn/package.json\n` + + ` 3. Extract them into node_modules/bdk-rn/\n` + + `Or point BDK_RN_ARTIFACTS_DIR at a directory containing the zips and reinstall.\n` + + `To build from source instead, see https://github.com/bitcoindevkit/bdk-rn#readme` + ); +} + +async function main() { + if (process.env.BDK_RN_SKIP_POSTINSTALL) { + log('BDK_RN_SKIP_POSTINSTALL is set, skipping binary download.'); + return; + } + + const checksums = packageJson.checksums || {}; + const releaseTag = packageJson.releaseTag || `v${packageJson.version}`; + + const pending = ARTIFACTS.filter((artifact) => { + if (artifact.platformGuard && process.platform !== artifact.platformGuard) { + return false; + } + return !fs.existsSync(artifact.existsPath); + }); + + if (pending.length === 0) { + log('Native binaries already present, nothing to do.'); + return; + } + + if (pending.every((artifact) => !checksums[artifact.checksumKey])) { + // Development checkout or git install: no checksums are recorded, so + // there is nothing trustworthy to download. Build from source instead. + log( + 'No artifact checksums found in package.json (development install?). ' + + 'Skipping binary download. Build the binaries with ' + + '`just submodule-init && just submodule-apply-patch && just build-tarball`.' + ); + return; + } + + const localDir = process.env.BDK_RN_ARTIFACTS_DIR; + + for (const artifact of pending) { + const expected = checksums[artifact.checksumKey]; + if (!expected) { + log(`No checksum for ${artifact.name} artifact, skipping it.`); + continue; + } + + const zipPath = path.join(PACKAGE_ROOT, artifact.zipName); + try { + if (localDir) { + const localZip = path.join(localDir, artifact.zipName); + log(`Copying ${artifact.name} binaries from ${localZip}`); + fs.copyFileSync(localZip, zipPath); + } else { + const url = `${BASE_URL}/${releaseTag}/${artifact.zipName}`; + log(`Downloading ${artifact.name} binaries from ${url}`); + await download(url, zipPath); + } + + await verifyChecksum(zipPath, expected, artifact.zipName); + log(`Checksum verified for ${artifact.zipName}`); + extractZip(zipPath, PACKAGE_ROOT); + log(`${artifact.name} binaries installed.`); + } catch (error) { + console.error(`[bdk-rn] Failed to install ${artifact.name} binaries.`); + console.error(`[bdk-rn] ${error.message}`); + console.error(`[bdk-rn] ${manualInstructions(releaseTag)}`); + process.exit(1); + } finally { + fs.rmSync(zipPath, { force: true }); + } + } +} + +main().catch((error) => { + console.error(`[bdk-rn] Unexpected error: ${error.message}`); + process.exit(1); +}); diff --git a/tsconfig.json b/tsconfig.json index 3b74bb6..6b63257 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,5 +25,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["example", "tests", "lib", "node_modules"] } diff --git a/ubrn.config.yaml b/ubrn.config.yaml index 8e978ff..72628bd 100644 --- a/ubrn.config.yaml +++ b/ubrn.config.yaml @@ -5,8 +5,12 @@ rust: android: targets: - arm64-v8a + - armeabi-v7a + - x86 + - x86_64 ios: targets: - aarch64-apple-ios - aarch64-apple-ios-sim + - x86_64-apple-ios