fix: peer-depend expo-blob and @bugsplat/react, develop against SDK 57 - #23
Open
bobbyg603 wants to merge 2 commits into
Open
fix: peer-depend expo-blob and @bugsplat/react, develop against SDK 57#23bobbyg603 wants to merge 2 commits into
bobbyg603 wants to merge 2 commits into
Conversation
expo-blob ships native code that is versioned in lockstep with the Expo SDK, so the app — not this library — has to own which copy is installed. Declaring it as a dependency at ^55.0.13 pins consumers to the SDK 55 build, and the caret range can never reach 57.x. On SDK 57 that combination crashes at startup. expo-blob 55's BlobModule references expo.modules.kotlin.types.AnyTypeProvider, which no longer exists in expo-modules-core 57, so native module registration throws NoClassDefFoundError before any JS runs. The build itself succeeds, which makes it look like a runtime bug rather than a dependency one. Moving it to peerDependencies lets each app install the expo-blob build matching its SDK, and keeps a single copy in the tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates @bugsplat/expo’s dependency model to avoid Expo SDK mismatches by making expo-blob a peer dependency, ensuring consuming apps install the SDK-matching native module.
Changes:
- Move
expo-blobfromdependenciestopeerDependencies(with a localdevDependenciesentry for development/testing). - Update installation instructions to explicitly install
expo-blobviaexpo install. - Document why
expo-blobmust be peer-installed to match the project’s Expo SDK.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| README.md | Updates install command and explains why expo-blob is a peer dependency. |
| package.json | Moves expo-blob to peerDependencies and adds it to devDependencies for local development. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
39
to
43
| "devDependencies": { | ||
| "@types/react": "~19.1.1", | ||
| "expo": "~55.0.8", | ||
| "expo-blob": "~55.0.13", | ||
| "expo-module-scripts": "^55.0.2", |
The package was developed and tested against SDK 55 only, which is how an SDK 57 incompatibility shipped unnoticed in the first place. Move the devDependencies and the example app to SDK 57 (expo 57.0.11, react-native 0.86.2, react 19.2.3) so local builds exercise the same core the library is published against. expo-module-scripts has no 57 line yet, so it goes to the latest available, 56.0.3. @bugsplat/react becomes a peer dependency as well. It exports React components and owns the BugSplat client singleton, so a consumer that installs it directly alongside this package would otherwise end up with two copies and two clients. Peering it keeps exactly one in the tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
expo-blobis declared as a dependency at^55.0.13. It ships native code versioned in lockstep with the Expo SDK, and the caret range can never reach 57.x — so every consumer is pinned to the SDK 55 build no matter which SDK they're on.On SDK 57 that crashes the app at startup:
AnyTypeProviderdoesn't exist anywhere inexpo-modules-core@57. The Gradle build succeeds and the APK installs cleanly — it dies during native module registration, before any JS runs, which makes it read as a runtime bug rather than a dependency one. Minification is off, so this isn't R8 stripping.Found while upgrading an app to Expo SDK 57.
0.7.0is affected identically.Changes
1.
expo-blob→ peer dependency. Matches howexpo,react, andreact-nativeare already declared. The app installs the copy matching its SDK, and there's exactly one in the tree.2.
@bugsplat/react→ peer dependency. It exports React components (ErrorBoundary,withErrorBoundary) and owns the BugSplat client singleton. A consumer who installs it directly alongside this package gets two copies, two clients, and two React contexts. Peering it (at^2.1.1, since we control that release line) keeps one.3. Develop against SDK 57. devDependencies and the example app move to
expo 57.0.11,react-native 0.86.2,react/react-dom 19.2.3,jest-expo 57.0.3,expo-blob 57.0.1. The package was previously developed and tested against SDK 55 only — which is precisely why this shipped unnoticed.expo-module-scriptshas no 57 line published yet, so it goes to the latest available (56.0.3).4. README install line becomes
npx expo install @bugsplat/expo expo-blob @bugsplat/react, with a note thatexpo-blobis not in Expo's bundled native modules manifest (see below), soexpo installwill not pin it for you.No source changes were needed:
src/BugsplatExpoModule.tsusesrequireOptionalNativeModulefromexpo, which is stable across 55→57.This is an install-breaking change for consumers (they must now install
expo-bloband@bugsplat/react), so it wants a minor bump rather than a patch.Not done here
package-lock.jsonat the root and inexample/still describe the SDK 55 tree. Runnpm installin both before merging. I was working on a machine with ~3 GB free and did not want a half-written lockfile.expo-modules-core@57— it built and packaged fine in the consuming app where this was found. Only theexpo-blobdependency was stale. iOS is unverified.Notes — we still need to test whether expo-blob is actually required
I deliberately did not remove
expo-blob, even though a first read suggests theimport 'expo-blob'inBugsplatExpo.tsmay be doing nothing. Bobby is fairly confident it's load-bearing for attachment paths — componentStack, log files, screenshots, and other attachments — and that needs real device testing before anything is removed. Peer-dep'ing it unblocks SDK 57 without changing any runtime behavior, which keeps that question separate.What the code reads like today, for whoever picks this up:
The bare import does not appear to patch
globalThis.Blob.expo-blob's JS isexport * from './ExpoBlob'→export class Blob extends NativeBlobModule.Blob. NoglobalThisassignment insrc/orbuild/in either 55.0.13 or 57.0.1.Module()namedExpoBlob— no JSI global installation.expo/src/winter/runtime.native.tsinstallsTextDecoder,URL,URLSearchParams,DOMException,structuredClone, andfetch— notBlob. Andexpo/src/winter/fetch/createBlob.tsstill carriesTODO(kudo,20260706): remove this when we install expo-blob as globalThis.Blob.Which means binary attachments may already be broken on native.
bugsplatcore does:If
globalThis.Blobis React Native's Blob,BlobManager.createFromPartsthrows outright:A
Uint8Arrayis anArrayBufferView, so any binary attachment — log file, screenshot — would hit that throw. String attachments are fine:new Blob([componentStack])produces an RN Blob, andexpo/fetch'sconvertFormDataserializes it via theFileReader.readAsArrayBufferfallback inblobToArrayBufferAsync.Note that
src/__tests__/BugsplatExpo.expoGo.test.ts:262covers exactly this case ({ filename: 'log.txt', data: new Uint8Array([1]) }) but runs under a Node/jsdomBlob, so it would pass while a device fails.Suggested follow-up, on a real device (not Expo Go, not a JS-only path):
Uint8Array— the case most likely to throw.Run each with and without
expo-blobinstalled. If binary attachments fail in both cases, the import isn't providing the protection it looks like it should, and the real fix is to construct attachments withexpo-blob'sBlobexplicitly (or install it asglobalThis.Blob) rather than relying on a global nothing patches. If they pass only with it installed, then something does wire up the global and the README claim around line 245 stands — worth pinning down which, either way.I could not check whether SDK 55's winter runtime installed
Blobas a global (nonode_modulesin the clone I looked at), so it's possible this worked when it was written and silently stopped.Keeping this in sync going forward
expo-blobis not in Expo's bundled native modules manifest — neither the localexpo/bundledNativeModules.jsonnor the API (https://api.expo.dev/v2/sdks/57.0.0/native-modules, 123 packages, no blob entry). Sonpx expo install expo-blobinstalls whatever npm'slatestis, which today is57.0.1and would be wrong for an SDK 55 or 56 app. Until Expo adds it, the README has to tell people which line to install, andnpx expo-doctorwon't catch a mismatch either.🤖 Generated with Claude Code