Skip to content

fix(storage): decode data URLs with custom content types - #9258

Open
OskarEichler wants to merge 1 commit into
invertase:mainfrom
OskarEichler:codex/storage-decode-data-url-with-content-type
Open

fix(storage): decode data URLs with custom content types#9258
OskarEichler wants to merge 1 commit into
invertase:mainfrom
OskarEichler:codex/storage-decode-data-url-with-content-type

Conversation

@OskarEichler

Copy link
Copy Markdown
Contributor

Compatibility

  • No public API or type changes.
  • No breaking changes.
  • Observable correction: data URL uploads with an explicit metadata contentType are now decoded before reaching the native base64-only upload path. Previously the complete data URL and unsupported data_url format were forwarded.

What changed

  • Always replace a validated data URL with its base64 payload and base64 format.
  • Continue inferring contentType only when callers do not provide one.
  • Add a focused unit regression and native Storage emulator coverage.

Verification

  • Regression test fails on upstream main and passes with this change.
  • Full Jest: 104 suites, 1,480 tests, 31 snapshots.
  • iOS App + Storage E2E: 141 passing, 7 pending.
  • Android App + Storage E2E: 143 passing, 5 pending.
  • Android assembleDebug, assembleAndroidTest, and lintDebug.
  • lerna:prepare, JS lint, dependency-cruiser, Storage TypeScript, repository/consumer TypeScript, API reference, compare-types, Prettier, and diff checks.

@mikehardy mikehardy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @OskarEichler 👋

Thanks for the PR — the real-problem analysis looks right to me, and I like the unit coverage. I ran local Jest plus the android/ios/macos e2e suite here. Android and iOS are green on the new case, but macOS failed, so I'm requesting changes until we understand that path.

macOS e2e failure from our run:

storage() -> StorageTask
  StorageTask modular
    putString()
      uploads a data_url with an explicit content type:
AssertionError: expected 8 to equal 5
  at packages/storage/e2e/StorageTask.e2e.js:185:51

The test uploads data:text/plain;base64,aGVsbG8= with { contentType: 'text/custom' } and expects metadata.size === 5 (decoded hello). macOS reported 8, which looks like the undecoded base64 payload length rather than the decoded bytes.

We have a lot of trouble on the firebase-js-sdk / HermesVM target when it comes to base64 and Web APIs in general — atob, btoa, Blob, FileReader, data URLs, and friends have all been edge-case magnets for us. I think this may be a similar edge case on macOS rather than a simple one-line fix being wrong on mobile.

Could you dig into why macOS still uploads the undecoded payload when an explicit contentType is set, and whether the fix needs a Hermes-specific path (or a different assertion strategy on macOS)? Happy to pair on it if useful.

Cheers

@mikehardy mikehardy added the Workflow: Needs Review Pending feedback or review from a maintainer. label Aug 31, 2026
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.78%. Comparing base (81beeca) to head (c8f5e51).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #9258      +/-   ##
============================================
+ Coverage     68.50%   68.78%   +0.29%     
  Complexity     1915     1915              
============================================
  Files           522      482      -40     
  Lines         38116    35606    -2510     
  Branches       5189     5033     -156     
============================================
- Hits          26108    24489    -1619     
+ Misses        10222     9317     -905     
- Partials       1786     1800      +14     
Flag Coverage Δ
android-native 63.76% <ø> (ø)
e2e-ts-android 54.00% <100.00%> (ø)
e2e-ts-ios 53.50% <100.00%> (ø)
e2e-ts-macos ?
ios-native 68.78% <ø> (ø)
ios-ruby 100.00% <ø> (ø)
jest 46.47% <100.00%> (+0.21%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@OskarEichler

Copy link
Copy Markdown
Contributor Author

I traced the macOS path. The PR’s _updateString result is correct (aGVsbG8=, format base64), and the macOS/web bridge then decodes it in RNFBStorageModule.ts before passing a Uint8Array to firebase-js-sdk uploadBytesResumable. The remaining 8-byte upload is the existing Hermes Blob / firebase-js-sdk binary-upload interaction already isolated in #9006 (@mikehardy/storage-upload-bytes), not the data-URL/content-type branch. That branch replaces this path with uploadBytes plus the Hermes Blob fallback and should make the same assertion report 5. I do not think changing the macOS assertion to 8 is correct because it would codify the corrupted upload; the clean path is to keep this regression and re-run/rebase it after #9006 lands (or stack #9006 underneath it for verification).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Attention Workflow: Needs Review Pending feedback or review from a maintainer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants