No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!263
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix-formdata-detection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes #253 and #256 after a long debugging path through five attempts (PRs #257, #260, #263, plus the iterative commits on this branch). The actual root causes were two distinct Expo SDK 56-specific issues:
Root causes
Upload (#253): Expo SDK 56's
@expo/winterfetch polyfill (the default fetch on every Expo build) doesn't support RN's{ uri, name, type }file-descriptor FormData parts. The upstream comment is explicit:uri is not supported for React Native's FormData.Seeexpo/src/winter/fetch/convertFormData.ts— only string, Blob, or{ bytes }parts are accepted; anything else throws "Unsupported FormDataPart implementation" from the JS layer, before the network ever sees the request.This explains why every prior attempt failed: PRs #257, #260, #263 each chased a different layer (RN's networking module, URL rewriter, Content-Type generation, Headers iteration) — but the winter polyfill rejects the FormData before any of that code runs.
Display (#256): RN's
<Image>on Android has documented inconsistent support forsource.headers. The runtime log proved it definitively:source.headers.Authorizationwas set, but the resulting OkHttp GET hit the server with no Authorization header and returned 401.Fixes
expo-file-system'sFileinstead of the RN file-descriptor.FileextendsBlob, which is exactly what the winter polyfill accepts.expo-image(Expo's enhanced image component, built on Glide on Android / Nuke on iOS) which honorssource.headersreliably.uploadProfilePicture()(bypassing the typed@carol/api-clientand its URL-rewriter middleware) — that ensures RN's native fetch receives an untouched Request and handles the multipart serialization end-to-end. Web continues to work via the same path same-origin.loadAccessToken()eager preload +subscribeAccessToken()pub/sub inlib/auth/storage.tsso the avatar's bearer header is available on the first paint after cold-launch.What this PR added vs reverted
Added:
expo-file-system,expo-imagedeps.lib/profile/pictureFetch.ts— raw fetch upload +buildPictureFormDatanative branch (kept here so theexpo-file-systemimport chain doesn't pollute the node-runtime vitest suite).loadAccessToken()/subscribeAccessToken().Refined:
_bodyFormDatafield (in addition to Content-Type) — kept because it's the right shape for any future non-upload multipart endpoint, even though uploads now bypass the rewriter.Test plan
pnpm -F @carol/client typecheck/lint/test(88 passing).pnpm -F @carol/client export:web(web bundle still produces dist/).Out of scope (filing as follow-ups if you want)
Closes #253.
Closes #256.
📊 Test coverage
Patch coverage: no testable lines changed.
Overall (
app/,lib/,db/, excluding UI per ADR-0019):Soft thresholds per ADR-0019. Coverage is informational and does not block merge.
Root cause finally found: Expo SDK 56's @expo/winter fetch polyfill (the default fetch on Expo) doesn't support RN's `{ uri, name, type }` file-descriptor FormData parts. The error comment in upstream is explicit: "`uri` is not supported for React Native's FormData." See expo/src/winter/fetch/convertFormData.ts — only string / Blob / `{ bytes }` parts are accepted; anything else throws "Unsupported FormDataPart implementation" from the JS side, before the request ever reaches the network. This explains all four prior attempts. They were all chasing the wrong layer — RN's networking module, the URL rewriter, Content-Type generation, Headers iteration — none of which mattered, because the winter polyfill rejects the FormData before any of that code runs. Switch native to `expo-file-system`'s `File`, which extends Blob and is exactly the shape the winter polyfill accepts. Web continues to use the existing fetch-the-blob-URL path. The native FormData builder moves to lib/profile/pictureFetch.ts because expo-file-system's import chain can't load under vitest's node environment (rolldown parse error on Expo's transitives). The node-runtime test suite stays pure with the web-only helper renamed to buildPictureFormDataWeb. Native-branch tests deleted — they asserted the old RN file-descriptor shape that no longer ships, and the new path can only be exercised on a device. Closes #253. Closes #256.fix(client): detect FormData via RN polyfill private field (#253 #256 — third attempt)to fix(client): profile picture upload + display on android (#253, #256)