From 298fe813cbe1421c6748b721de953acf6573acb8 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Fri, 15 May 2026 22:19:22 -0400 Subject: [PATCH 1/3] fix(deps): upgrade react-native-quick-base64 to v3 for New Architecture v2.x throws `Base64Module.install is not a function` on React Native New Architecture (default in Expo SDK 54+) because the legacy bridge install method is not exposed via NativeModules under bridgeless mode. v3.0.0 ships a Codegen TurboModule and dropped the install side effect. Replace the `require('react-native-quick-base64')` side effect in QuickCrypto's `install()` with explicit calls to v3's `toByteArray` / `fromByteArray`, assigning `global.base64ToArrayBuffer` and `global.base64FromArrayBuffer` directly. Declare those globals in the package so consumers get the types. Bump `@craftzdog/react-native- buffer` to 6.1.2 (v3-compatible) and pin all touched versions per the new code-typescript rule (no caret/tilde ranges). Fixes the `Base64Module.install is not a function` runtime error reported on vanilla Expo SDK 54/55 setups. --- .agents/rules/code-typescript.md | 1 + .github/ISSUE_TEMPLATE/BUILD_ERROR.yml | 2 +- bun.lock | 10 +++--- example/ios/Podfile.lock | 32 +++++++++++++++++-- example/package.json | 2 +- .../react-native-quick-crypto/package.json | 4 +-- .../react-native-quick-crypto/src/index.ts | 19 +++++++++-- 7 files changed, 55 insertions(+), 15 deletions(-) diff --git a/.agents/rules/code-typescript.md b/.agents/rules/code-typescript.md index 6e98131dd..8073d80a7 100644 --- a/.agents/rules/code-typescript.md +++ b/.agents/rules/code-typescript.md @@ -17,6 +17,7 @@ Applies to `*.ts` and `*.tsx`. - Write explicit return types. - Keep functions minimal and modular. - Use Bun for package management; do not use npm/yarn/pnpm. +- Pin dependency versions exactly in `package.json`. No caret (`^`) or tilde (`~`) ranges. Applies to `dependencies`, `devDependencies`, and `peerDependencies`. Use the version `bun.lock` resolved. - For React, minimize `useEffect`; use named effect functions if unavoidable. ## Formatting diff --git a/.github/ISSUE_TEMPLATE/BUILD_ERROR.yml b/.github/ISSUE_TEMPLATE/BUILD_ERROR.yml index eff246e19..509ea1571 100644 --- a/.github/ISSUE_TEMPLATE/BUILD_ERROR.yml +++ b/.github/ISSUE_TEMPLATE/BUILD_ERROR.yml @@ -41,7 +41,7 @@ body: "react-native": "^0.74.3", "react-native-quick-crypto": "^0.7.1", "@craftzdog/react-native-buffer": "^6.0.5", - "react-native-quick-base64": "^2.1.2", + "react-native-quick-base64": "3.0.0", ... }, validations: diff --git a/bun.lock b/bun.lock index 2b7291458..f09b36586 100644 --- a/bun.lock +++ b/bun.lock @@ -48,7 +48,7 @@ "react-native-fast-encoder": "0.3.1", "react-native-mmkv": "4.0.1", "react-native-nitro-modules": "0.33.2", - "react-native-quick-base64": "2.2.2", + "react-native-quick-base64": "3.0.0", "react-native-quick-crypto": "workspace:*", "react-native-safe-area-context": "5.6.2", "react-native-screens": "4.18.0", @@ -88,7 +88,7 @@ "name": "react-native-quick-crypto", "version": "1.1.3", "dependencies": { - "@craftzdog/react-native-buffer": "6.1.0", + "@craftzdog/react-native-buffer": "6.1.2", "events": "3.3.0", "readable-stream": "4.7.0", "safe-buffer": "^5.2.1", @@ -115,7 +115,7 @@ "react": "*", "react-native": "*", "react-native-nitro-modules": ">=0.31.2", - "react-native-quick-base64": ">=2.1.0", + "react-native-quick-base64": "3.0.0", }, "optionalPeers": [ "expo", @@ -391,7 +391,7 @@ "@conventional-changelog/git-client": ["@conventional-changelog/git-client@1.0.1", "", { "dependencies": { "@types/semver": "^7.5.5", "semver": "^7.5.2" }, "peerDependencies": { "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0" }, "optionalPeers": ["conventional-commits-filter", "conventional-commits-parser"] }, "sha512-PJEqBwAleffCMETaVm/fUgHldzBE35JFk3/9LL6NUA5EXa3qednu+UT6M7E5iBu3zIQZCULYIiZ90fBYHt6xUw=="], - "@craftzdog/react-native-buffer": ["@craftzdog/react-native-buffer@6.1.0", "", { "dependencies": { "ieee754": "^1.2.1", "react-native-quick-base64": "^2.0.5" } }, "sha512-lJXdjZ7fTllLbzDrwg/FrJLjQ5sBcAgwcqgAB6OPpXTHdCenEhHZblQpfmBLLe7/S7m0yKXL3kN3jpwOEkpjGg=="], + "@craftzdog/react-native-buffer": ["@craftzdog/react-native-buffer@6.1.2", "", { "dependencies": { "ieee754": "^1.2.1", "react-native-quick-base64": "^3.0.0" } }, "sha512-KV1HitN05FHLLDG7Zb/yftDsa+mKBYBzFMQ0PMldvUicq6vWOtAvz9mDavt7Fzozh+WNqORE+yFDkkdWysZ/SA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], @@ -2101,7 +2101,7 @@ "react-native-nitro-modules": ["react-native-nitro-modules@0.33.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-ZlfOe6abODeHv/eZf8PxeSkrxIUhEKha6jaAAA9oXy7I6VPr7Ff4dUsAq3cyF3kX0L6qt2Dh9nzD2NdSsDwGpA=="], - "react-native-quick-base64": ["react-native-quick-base64@2.2.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-WLHSifHLoamr2kF00Gov0W9ud6CfPshe1rmqWTquVIi9c62qxOaJCFVDrXFZhEBU8B8PvGLVuOlVKH78yhY0Fg=="], + "react-native-quick-base64": ["react-native-quick-base64@3.0.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-gnJhA4a/QIGrptZQJv0hLLne8yyJPkdXtn33LHEzq3nhzsv+jRhrxr07wYwYX+CKCRXmuNkZ3Wo7LapSrFVmQg=="], "react-native-quick-crypto": ["react-native-quick-crypto@workspace:packages/react-native-quick-crypto"], diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ba6911977..a0281fca0 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1869,8 +1869,34 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-quick-base64 (2.2.2): + - react-native-quick-base64 (3.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - react-native-safe-area-context (5.6.2): - boost - DoubleConversion @@ -2815,7 +2841,7 @@ SPEC CHECKSUMS: NitroMmkv: afbc5b2fbf963be567c6c545aa1efcf6a9cec68e NitroModules: 11bba9d065af151eae51e38a6425e04c3b223ff3 OpenSSL-Universal: 9110d21982bb7e8b22a962b6db56a8aa805afde7 - QuickCrypto: 3f1a7a649aa230cafd9b3576bdbc101bd10eb0ca + QuickCrypto: b6fed48151d7fef2b33e8420c660dde2b421c396 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a @@ -2850,7 +2876,7 @@ SPEC CHECKSUMS: React-Mapbuffer: 91e0eab42a6ae7f3e34091a126d70fc53bd3823e React-microtasksnativemodule: 1ead4fe154df3b1ba34b5a9e35ef3c4bdfa72ccb react-native-fast-encoder: f2728ab5e520601ba04df15716722941d941495e - react-native-quick-base64: 6568199bb2ac8e72ecdfdc73a230fbc5c1d3aac4 + react-native-quick-base64: e5eea0a73474108a7b80e33ce2c004a414649da2 react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460 React-NativeModulesApple: eff2eba56030eb0d107b1642b8f853bc36a833ac React-oscompat: b12c633e9c00f1f99467b1e0e0b8038895dae436 diff --git a/example/package.json b/example/package.json index 2a05b8d53..81c055bcb 100644 --- a/example/package.json +++ b/example/package.json @@ -39,7 +39,7 @@ "react-native-fast-encoder": "0.3.1", "react-native-mmkv": "4.0.1", "react-native-nitro-modules": "0.33.2", - "react-native-quick-base64": "2.2.2", + "react-native-quick-base64": "3.0.0", "react-native-quick-crypto": "workspace:*", "react-native-safe-area-context": "5.6.2", "react-native-screens": "4.18.0", diff --git a/packages/react-native-quick-crypto/package.json b/packages/react-native-quick-crypto/package.json index d470916fd..cccdf8ff8 100644 --- a/packages/react-native-quick-crypto/package.json +++ b/packages/react-native-quick-crypto/package.json @@ -106,7 +106,7 @@ "registry": "https://registry.npmjs.org/" }, "dependencies": { - "@craftzdog/react-native-buffer": "6.1.0", + "@craftzdog/react-native-buffer": "6.1.2", "events": "3.3.0", "readable-stream": "4.7.0", "safe-buffer": "^5.2.1", @@ -131,7 +131,7 @@ "react": "*", "react-native": "*", "react-native-nitro-modules": ">=0.31.2", - "react-native-quick-base64": ">=2.1.0", + "react-native-quick-base64": "3.0.0", "expo": ">=48.0.0", "expo-build-properties": "*" }, diff --git a/packages/react-native-quick-crypto/src/index.ts b/packages/react-native-quick-crypto/src/index.ts index 02548308b..725b1e23b 100644 --- a/packages/react-native-quick-crypto/src/index.ts +++ b/packages/react-native-quick-crypto/src/index.ts @@ -1,5 +1,16 @@ // polyfill imports import { Buffer } from '@craftzdog/react-native-buffer'; +import { toByteArray, fromByteArray } from 'react-native-quick-base64'; + +declare global { + // eslint-disable-next-line no-var + var base64ToArrayBuffer: ( + s: string, + removeLinebreaks?: boolean, + ) => ArrayBuffer; + // eslint-disable-next-line no-var + var base64FromArrayBuffer: (b: ArrayBuffer, urlSafe?: boolean) => string; +} // API imports import * as argon2Module from './argon2'; @@ -66,9 +77,11 @@ export const install = () => { // @ts-expect-error subtle isn't fully implemented and Cryptokey is missing global.crypto = QuickCrypto; - // Install base64 globals (base64ToArrayBuffer, base64FromArrayBuffer) - // eslint-disable-next-line @typescript-eslint/no-require-imports - require('react-native-quick-base64'); + // react-native-quick-base64 v3 no longer installs these globals on import. + global.base64ToArrayBuffer = (s, removeLinebreaks) => + toByteArray(s, removeLinebreaks).buffer as ArrayBuffer; + global.base64FromArrayBuffer = (b, urlSafe) => + fromByteArray(new Uint8Array(b), urlSafe); }; // random, cipher, hash use nextTick From 465b45a4a8dce68a0ce9346b745b2ac4685fdfb3 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Fri, 15 May 2026 22:32:40 -0400 Subject: [PATCH 2/3] chore(deps): loosen library ranges and scope pin rule to example Library package.json may use ranges so consumers aren't blocked on exact-version updates. Only the example app needs deterministic pins. - Loosen @craftzdog/react-native-buffer to ^6.1.2 - Loosen react-native-quick-base64 peerDep to >=3.0.0 - Scope exact-pin rule to example app's package.json --- .agents/rules/code-typescript.md | 3 ++- packages/react-native-quick-crypto/package.json | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.agents/rules/code-typescript.md b/.agents/rules/code-typescript.md index 8073d80a7..95c0fff96 100644 --- a/.agents/rules/code-typescript.md +++ b/.agents/rules/code-typescript.md @@ -17,7 +17,8 @@ Applies to `*.ts` and `*.tsx`. - Write explicit return types. - Keep functions minimal and modular. - Use Bun for package management; do not use npm/yarn/pnpm. -- Pin dependency versions exactly in `package.json`. No caret (`^`) or tilde (`~`) ranges. Applies to `dependencies`, `devDependencies`, and `peerDependencies`. Use the version `bun.lock` resolved. +- In the example app's `package.json`, pin dependency versions exactly. No caret (`^`) or tilde (`~`) ranges. Applies to `dependencies` and `devDependencies`. Use the version `bun.lock` resolved. +- Library `package.json` (under `packages/*`) may use ranges (`^`, `>=`) for `dependencies` and `peerDependencies` to allow consumer flexibility. - For React, minimize `useEffect`; use named effect functions if unavoidable. ## Formatting diff --git a/packages/react-native-quick-crypto/package.json b/packages/react-native-quick-crypto/package.json index cccdf8ff8..9efa663bf 100644 --- a/packages/react-native-quick-crypto/package.json +++ b/packages/react-native-quick-crypto/package.json @@ -106,7 +106,7 @@ "registry": "https://registry.npmjs.org/" }, "dependencies": { - "@craftzdog/react-native-buffer": "6.1.2", + "@craftzdog/react-native-buffer": "^6.1.2", "events": "3.3.0", "readable-stream": "4.7.0", "safe-buffer": "^5.2.1", @@ -131,7 +131,7 @@ "react": "*", "react-native": "*", "react-native-nitro-modules": ">=0.31.2", - "react-native-quick-base64": "3.0.0", + "react-native-quick-base64": ">=3.0.0", "expo": ">=48.0.0", "expo-build-properties": "*" }, From a052e4be7a4c80ed9b3ece66a5edc3349a13399d Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Fri, 15 May 2026 22:33:26 -0400 Subject: [PATCH 3/3] chore(deps): update bun.lock for loosened ranges --- bun.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index f09b36586..1dc9354fe 100644 --- a/bun.lock +++ b/bun.lock @@ -88,7 +88,7 @@ "name": "react-native-quick-crypto", "version": "1.1.3", "dependencies": { - "@craftzdog/react-native-buffer": "6.1.2", + "@craftzdog/react-native-buffer": "^6.1.2", "events": "3.3.0", "readable-stream": "4.7.0", "safe-buffer": "^5.2.1", @@ -115,7 +115,7 @@ "react": "*", "react-native": "*", "react-native-nitro-modules": ">=0.31.2", - "react-native-quick-base64": "3.0.0", + "react-native-quick-base64": ">=3.0.0", }, "optionalPeers": [ "expo",