chore: sync upstream PR #8478 - fix(http): support binary request bodies - #87
Conversation
Co-authored-by: jcesarmobile <jcesarmobile@gmail.com>
…erred conflicts)
|
Git applied the upstream-preferred strategy to resolve this sync. Please review the branch carefully before merging. |
Beta npm buildMaintainers can publish one Capacitor Plus workspace package from this PR to npm for fast testing. Comment Examples: /publish-beta core
/publish-beta cli
/publish-beta @capacitor-plus/coreIf exactly one workspace package changed, Packages:
The workflow will:
Security note: beta publish is only enabled for branches inside this repository. |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 51 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe changes restore official Capacitor package metadata, extend binary HTTP request and response handling across platforms, add double configuration accessors, adjust Android safe-area behavior, update CLI runtime behavior, and refresh 8.3.x changelogs. ChangesCapacitor platform updates
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Fetch
participant nativeBridge as native-bridge
participant CapacitorHttp
participant Response
Fetch->>nativeBridge: serialize Blob or ArrayBuffer as binary
nativeBridge->>CapacitorHttp: send base64 request dataType binary
CapacitorHttp-->>nativeBridge: return arraybuffer response
nativeBridge->>Response: decode base64 into ArrayBuffer
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift (1)
174-179: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMalformed base64 silently drops the request body instead of failing.
Data(base64Encoded: stringData)returnsnilfor invalid base64 rather than throwing, andsetRequestBodyassigns that directly torequest.httpBody. A malformed/truncated base64 payload for a"file"/"binary"body would silently send the request with no body rather than surfacing an error. This pattern pre-dates this PR for"file", but is now reachable more often since"binary"applies to the more common Blob/ArrayBuffer/TypedArray inputs.🛡️ Proposed fix: throw on decode failure instead of returning nil
if dataType == "file" || dataType == "binary" { guard let stringData = body as? String else { throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed as string") } - return Data(base64Encoded: stringData) + guard let decoded = Data(base64Encoded: stringData) else { + throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be decoded as base64 for dataType [ \(dataType!) ]") + } + return decoded } else if dataType == "formData" {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift` around lines 174 - 179, Update getRequestData for "file" and "binary" payloads to validate the result of Data(base64Encoded: stringData) and throw CapacitorUrlRequestError.serializationError when decoding fails, rather than returning nil and allowing setRequestBody to send a request without a body.android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java (1)
191-211: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winCheck
bodyTypebeforeContent-Type
bodyType == "file"/"binary"should take precedence here. A Blob/ArrayBuffer/TypedArray request withContent-Type: application/jsonwill hit the JSON branch and send the base64 payload as text instead of decoded bytes. Match the iOS ordering by handling binary bodies first.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java` around lines 191 - 211, The setRequestBody method currently checks Content-Type before bodyType, causing file or binary payloads with application/json to be serialized as text. Reorder the branches so bodyType values "file" or "binary" are handled first via decodeBase64RequestBody, then retain the existing Content-Type validation and JSON handling for non-binary bodies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`:
- Around line 167-178: The safe-area CSS injections in SystemBars must respect
insetHandlingEnabled. Update the injection paths around
calcSafeAreaInsets/injectSafeAreaCSS, including the additional locations at the
referenced paths, to guard all three calls with the flag so insetsHandling:
"disable" neither injects nor updates safe-area values.
In `@android/package.json`:
- Line 2: Update the package-scope matching and core-package resolution in
sync-peer-dependencies so the renamed `@capacitor/android` package is recognized
alongside the existing package scope. Ensure release preparation derives a
defined corePkg for the new scope and preserves synchronization for any
remaining `@capacitor-plus` packages.
In `@CHANGELOG.md`:
- Around line 6-30: Revert the hand-authored changes to the generated root
CHANGELOG.md, including the added 8.3.4 and 8.3.3 entries, and leave changelog
updates to the prescribed CI/CD release process.
In `@core/CHANGELOG.md`:
- Around line 6-16: Revert the manually added 8.3.x entries in core/CHANGELOG.md
lines 6-16 and regenerate the core changelog through CI/CD. Apply the same
change to ios/CHANGELOG.md lines 6-16; both changelogs must remain CI/CD-managed
artifacts and must not be edited directly.
In `@core/native-bridge.ts`:
- Around line 96-127: Resolve the unreachable Uint8Array handling in the
body-encoding branch: move the Uint8Array check before ArrayBuffer.isView(body)
if raw Uint8Array bodies must honor contentType; otherwise remove the dead
Uint8Array path. Apply the same change to core/native-bridge.ts,
android/capacitor/src/main/assets/native-bridge.js, and
ios/Capacitor/Capacitor/assets/native-bridge.js, regenerating the platform
assets when removing the path.
---
Outside diff comments:
In
`@android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java`:
- Around line 191-211: The setRequestBody method currently checks Content-Type
before bodyType, causing file or binary payloads with application/json to be
serialized as text. Reorder the branches so bodyType values "file" or "binary"
are handled first via decodeBase64RequestBody, then retain the existing
Content-Type validation and JSON handling for non-binary bodies.
In `@ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift`:
- Around line 174-179: Update getRequestData for "file" and "binary" payloads to
validate the result of Data(base64Encoded: stringData) and throw
CapacitorUrlRequestError.serializationError when decoding fails, rather than
returning nil and allowing setRequestBody to send a request without a body.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b50700e9-b368-4cd7-95b4-feae7b095223
📒 Files selected for processing (24)
CHANGELOG.mdandroid/CHANGELOG.mdandroid/capacitor/src/main/assets/native-bridge.jsandroid/capacitor/src/main/java/com/getcapacitor/PluginConfig.javaandroid/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.javaandroid/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.javaandroid/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.javaandroid/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.javaandroid/package.jsoncli/CHANGELOG.mdcli/package.jsoncli/src/ios/update.tscli/src/ipc.tscore/CHANGELOG.mdcore/http.mdcore/native-bridge.tscore/package.jsoncore/src/core-plugins.tscore/src/tests/bridge.spec.tsios/CHANGELOG.mdios/Capacitor/Capacitor/PluginConfig.swiftios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swiftios/Capacitor/Capacitor/assets/native-bridge.jsios/package.json
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
| WindowInsetsCompat insets; | ||
|
|
||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { | ||
| View v = (View) this.getBridge().getWebView().getParent(); | ||
| WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v); | ||
| if (insets != null) { | ||
| Insets safeAreaInsets = calcSafeAreaInsets(insets); | ||
| injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); | ||
| } | ||
| insets = ViewCompat.getRootWindowInsets(v); | ||
| } else { | ||
| insets = WindowInsetsCompat.CONSUMED; | ||
| } | ||
|
|
||
| if (insets != null) { | ||
| Insets safeAreaInsets = calcSafeAreaInsets(insets); | ||
| injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Preserve insetsHandling: "disable" behavior.
The unconditional injections bypass insetHandlingEnabled, which is set to false when insetsHandling is "disable" at Lines 97-100. Disabled mode will therefore still inject or update --safe-area-inset-* values. Restore the flag check consistently across all three paths.
Also applies to: 194-195, 227-228
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`
around lines 167 - 178, The safe-area CSS injections in SystemBars must respect
insetHandlingEnabled. Update the injection paths around
calcSafeAreaInsets/injectSafeAreaCSS, including the additional locations at the
referenced paths, to guard all three calls with the flag so insetsHandling:
"disable" neither injects nor updates safe-area values.
| ## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) | ||
|
|
||
| **Note:** Version bump only for package @capacitor/core | ||
|
|
||
| ### Features | ||
| ## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) | ||
|
|
||
| * add new workflows for testing and version bumping; update README files for all packages ([a01c2a8](https://github.com/Cap-go/capacitor-plus/commit/a01c2a842c363c2aa58e5681210fa62fac8e5de5)) | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| ## [8.0.1](https://github.com/Cap-go/capacitor-plus/compare/3.3.4...8.0.1) (2025-12-16) | ||
|
|
||
| ### Bug Fixes | ||
|
|
||
| - 204 http response ([#6266](https://github.com/Cap-go/capacitor-plus/issues/6266)) ([771f6ce](https://github.com/Cap-go/capacitor-plus/commit/771f6ce1f35159848db218a42dc4f56b5106f750)) | ||
| - add http method to prototype.open ([#6740](https://github.com/Cap-go/capacitor-plus/issues/6740)) ([1fd2d87](https://github.com/Cap-go/capacitor-plus/commit/1fd2d8762ff2341a8fe20eec9e774c6a29576e88)) | ||
| - **android:** make local urls use unpatched fetch ([#6953](https://github.com/Cap-go/capacitor-plus/issues/6953)) ([e50e56c](https://github.com/Cap-go/capacitor-plus/commit/e50e56c5231f230497d1bd420e02e2e065c38f86)) | ||
| - **cli:** signing type option issue ([#6716](https://github.com/Cap-go/capacitor-plus/issues/6716)) ([ee0f745](https://github.com/Cap-go/capacitor-plus/commit/ee0f7457e458ca4bb4eb74f67552ac2ace76016b)) | ||
| - **cookies:** hide httpOnly cookies from client ([0cc927e](https://github.com/Cap-go/capacitor-plus/commit/0cc927ef5f0f7076a6d486d666d78483f1d71c54)) | ||
| - **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/Cap-go/capacitor-plus/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc)) | ||
| - **cookies:** Use Set-Cookie headers to persist cookies ([57f8b39](https://github.com/Cap-go/capacitor-plus/commit/57f8b39d7f4c5ee0e5e5cb316913e9450a81d22b)) | ||
| - copy url from nativeResponse to response in native-bridge.ts ([#6397](https://github.com/Cap-go/capacitor-plus/issues/6397)) ([e81a2ff](https://github.com/Cap-go/capacitor-plus/commit/e81a2ff42ddd446f3004ab5af6e74209f7ff076a)) | ||
| - **core:** Exception object was not set on Cap ([#5917](https://github.com/Cap-go/capacitor-plus/issues/5917)) ([9ca27a4](https://github.com/Cap-go/capacitor-plus/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e)) | ||
| - **core:** make 'content-type' header count for XMLHttpRequest patch ([#7161](https://github.com/Cap-go/capacitor-plus/issues/7161)) ([26d7f68](https://github.com/Cap-go/capacitor-plus/commit/26d7f688284914c6ef795564ba424119efc32a1c)) | ||
| - **core:** Make cordova bridge use Promise instead of setTimeout ([#5586](https://github.com/Cap-go/capacitor-plus/issues/5586)) ([f35d96b](https://github.com/Cap-go/capacitor-plus/commit/f35d96b185f5890600a64b78e6bf939c336cbb2d)) | ||
| - **core:** Prevent error when hasListeners is empty ([#7975](https://github.com/Cap-go/capacitor-plus/issues/7975)) ([a4a0942](https://github.com/Cap-go/capacitor-plus/commit/a4a0942eddba068e078bd782bb093ed1ecff9e00)) | ||
| - **core:** use getPlatform instead of platform in cordova.js ([#7902](https://github.com/Cap-go/capacitor-plus/issues/7902)) ([277db7b](https://github.com/Cap-go/capacitor-plus/commit/277db7b48caaf870eefdf701ea99332c4338d7ed)) | ||
| - handle fetch headers that are Headers objects ([#6320](https://github.com/Cap-go/capacitor-plus/issues/6320)) ([cb00e49](https://github.com/Cap-go/capacitor-plus/commit/cb00e4952acca8e877555f30b2190f6685d25934)) | ||
| - http content headers not sent when using axios ([#8039](https://github.com/Cap-go/capacitor-plus/issues/8039)) ([67cac40](https://github.com/Cap-go/capacitor-plus/commit/67cac40660b3e8cc78d1d228b7c6915450948ef1)) | ||
| - **http:** add support for Request objects in fetch ([24b3cc1](https://github.com/Cap-go/capacitor-plus/commit/24b3cc113e3d8aae5d85dbf2d25bec0c35136477)) | ||
| - **http:** Add URLSearchParams support ([#7374](https://github.com/Cap-go/capacitor-plus/issues/7374)) ([9367ecc](https://github.com/Cap-go/capacitor-plus/commit/9367ecc56a0c78249dccdf95dca5006422144289)) | ||
| - **http:** boundary not added for Request objects ([#7897](https://github.com/Cap-go/capacitor-plus/issues/7897)) ([bdaa6f3](https://github.com/Cap-go/capacitor-plus/commit/bdaa6f3c38c33f3a021ac61f2de89101a5b66cff)) | ||
| - **http:** change proxy url generation ([#7354](https://github.com/Cap-go/capacitor-plus/issues/7354)) ([318c316](https://github.com/Cap-go/capacitor-plus/commit/318c316847c5b059fb88b46d4acd31e1ced477e5)) | ||
| - **http:** don't override readyState for non POST requests ([#7488](https://github.com/Cap-go/capacitor-plus/issues/7488)) ([30c13a8](https://github.com/Cap-go/capacitor-plus/commit/30c13a865e7710e6dc5f0ee014e951d52d030795)) | ||
| - **http:** don't throw errors when content-type is null on response ([#6627](https://github.com/Cap-go/capacitor-plus/issues/6627)) ([538821f](https://github.com/Cap-go/capacitor-plus/commit/538821f267aa3b79548fed6aaea8880ff949ffdd)) | ||
| - **http:** fire events in correct order when using xhr ([5ed3617](https://github.com/Cap-go/capacitor-plus/commit/5ed361787596bb5949f6ae5e366495f296352bf3)) | ||
| - **http:** fix local http requests on native platforms ([c4e040a](https://github.com/Cap-go/capacitor-plus/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f)) | ||
| - **http:** handle proxy urls with port ([#7273](https://github.com/Cap-go/capacitor-plus/issues/7273)) ([514409a](https://github.com/Cap-go/capacitor-plus/commit/514409aeb93ad65be105bbe2da8d2cd86ff159b0)) | ||
| - **http:** handle UInt8Array on body ([#7546](https://github.com/Cap-go/capacitor-plus/issues/7546)) ([cfb9ce1](https://github.com/Cap-go/capacitor-plus/commit/cfb9ce175615f69fe86b61af6d51ec2044d147cd)) | ||
| - **http:** inherit object properties on window.XMLHttpRequest ([91c11d0](https://github.com/Cap-go/capacitor-plus/commit/91c11d06f773c45a10f6f2d52f672ae6f189b162)) | ||
| - **http:** keep original URL properties on proxy ([#7329](https://github.com/Cap-go/capacitor-plus/issues/7329)) ([cbb6407](https://github.com/Cap-go/capacitor-plus/commit/cbb6407225b42a0d9db4f335a9766f119501021d)) | ||
| - **http:** Make proxy work with Request objects ([#7348](https://github.com/Cap-go/capacitor-plus/issues/7348)) ([7e68725](https://github.com/Cap-go/capacitor-plus/commit/7e6872573df03ab5cdc10a1a27db3e9fe81a141d)) | ||
| - **http:** parse readablestream data on fetch request objects ([#6919](https://github.com/Cap-go/capacitor-plus/issues/6919)) ([80ec3b7](https://github.com/Cap-go/capacitor-plus/commit/80ec3b73db18b7b6841bf90ed50a67389946ab87)) | ||
| - **http:** pass original url as query param on the proxy url ([#7527](https://github.com/Cap-go/capacitor-plus/issues/7527)) ([1da06e6](https://github.com/Cap-go/capacitor-plus/commit/1da06e66cb9cfbf5a5cc48ba6c23cdbe18bc8fc0)) | ||
| - **http:** prevent POST request from being proxied ([#7395](https://github.com/Cap-go/capacitor-plus/issues/7395)) ([7b8c352](https://github.com/Cap-go/capacitor-plus/commit/7b8c3523decd5610dcf09e926640bf35b382d61d)) | ||
| - **http:** return valid response for relative url xhr requests ([bde6569](https://github.com/Cap-go/capacitor-plus/commit/bde65696218f97a8328041f137457f46e5eb766a)) | ||
| - **http:** route get requests through custom handler ([#6818](https://github.com/Cap-go/capacitor-plus/issues/6818)) ([b853d06](https://github.com/Cap-go/capacitor-plus/commit/b853d065055b5a819949551be58b62d40b52e37c)) | ||
| - **http:** set formdata boundary and body when content-type not explicitly set ([0c2ccd9](https://github.com/Cap-go/capacitor-plus/commit/0c2ccd910a92ce3deaa67eb1819a4faa39c6af6e)) | ||
| - **http:** set port for proxy url ([#7341](https://github.com/Cap-go/capacitor-plus/issues/7341)) ([a3059dc](https://github.com/Cap-go/capacitor-plus/commit/a3059dca4a7746d9fb7102a7d41f4da80e2f48b4)) | ||
| - **ios:** Correctly Attach Headers to Request ([#6303](https://github.com/Cap-go/capacitor-plus/issues/6303)) ([a3f875c](https://github.com/Cap-go/capacitor-plus/commit/a3f875cf42e111fde07d6e87643264b19ed77573)) | ||
| - **ios:** crash when http headers contain numbers ([#6251](https://github.com/Cap-go/capacitor-plus/issues/6251)) ([028c556](https://github.com/Cap-go/capacitor-plus/commit/028c556a50b41ee99fe8f4f1aa2f42d3fd57f92d)) | ||
| - make Plugin.resolve act consistently ([#8225](https://github.com/Cap-go/capacitor-plus/issues/8225)) ([06aeb9e](https://github.com/Cap-go/capacitor-plus/commit/06aeb9e85d162d6be9d96820edcb2008cd74da84)) | ||
| - vue 3 log warning causes error on iOS ([#6993](https://github.com/Cap-go/capacitor-plus/issues/6993)) ([87271e2](https://github.com/Cap-go/capacitor-plus/commit/87271e2671013ad35d13b22f2e96d4fe8f4eeaf0)) | ||
| - **web:** Implement `retainUntilConsumed` on notifyListeners ([#7127](https://github.com/Cap-go/capacitor-plus/issues/7127)) ([526292e](https://github.com/Cap-go/capacitor-plus/commit/526292eb273ee7d8fa9a9912ce3b59e9a104c19e)) | ||
| **Note:** Version bump only for package @capacitor/core | ||
|
|
||
| ### Features | ||
| ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) | ||
|
|
||
| - **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/Cap-go/capacitor-plus/issues/5719)) ([174172b](https://github.com/Cap-go/capacitor-plus/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625)) | ||
| - **android:** Improving SystemBars inset handling ([#8268](https://github.com/Cap-go/capacitor-plus/issues/8268)) ([81ae30a](https://github.com/Cap-go/capacitor-plus/commit/81ae30a503797e417dd125b06262dabc4696c88a)) | ||
| - **android:** Use addWebMessageListener where available ([#5427](https://github.com/Cap-go/capacitor-plus/issues/5427)) ([c2dfe80](https://github.com/Cap-go/capacitor-plus/commit/c2dfe808446717412b35e82713d123b7a052f264)) | ||
| - Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/Cap-go/capacitor-plus/commit/d4047cfa947676777f400389a8d65defae140b45)) | ||
| - **cookies:** add get cookies plugin method ([ba1e770](https://github.com/Cap-go/capacitor-plus/commit/ba1e7702a3338714aee24388c0afea39706c9341)) | ||
| - export buildRequestInit function so we can use for downloadFile ([95b0575](https://github.com/Cap-go/capacitor-plus/commit/95b0575e3fbc1b1408aa69b61c58e18bf8882cea)) | ||
| - **http:** Apply overrideUserAgent to requests ([#7906](https://github.com/Cap-go/capacitor-plus/issues/7906)) ([52482c9](https://github.com/Cap-go/capacitor-plus/commit/52482c9d3c575b737054b41f9d1730c70cc5f471)) | ||
| - **http:** support for FormData requests ([#6708](https://github.com/Cap-go/capacitor-plus/issues/6708)) ([849c564](https://github.com/Cap-go/capacitor-plus/commit/849c56458205bea3b078b1ee19807d7fd84c47b1)) | ||
| - Implement automated upstream sync and review process for Capacitor+ ([54a5dfb](https://github.com/Cap-go/capacitor-plus/commit/54a5dfbd1f81ca4144e13b3051bf592d3aaf1f4a)) | ||
| - System Bars Plugin ([#8180](https://github.com/Cap-go/capacitor-plus/issues/8180)) ([a32216a](https://github.com/Cap-go/capacitor-plus/commit/a32216ac0607172a3a9c7ae5cdbfc598769294a6)) | ||
| - Upgrade to Typescript 5.x ([#6433](https://github.com/Cap-go/capacitor-plus/issues/6433)) ([88d0ded](https://github.com/Cap-go/capacitor-plus/commit/88d0ded9e7356531ffc4563b9b81a0f3f069484b)) | ||
| - **webview:** add setServerAssetPath method ([881235b](https://github.com/Cap-go/capacitor-plus/commit/881235b14de23ef988746bfb89a5a0fc3c8d8466)) | ||
| **Note:** Version bump only for package @capacitor/core |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Regenerate both changelogs through CI/CD.
The changes in both files violate the repository rule that changelogs are CI/CD-managed artifacts.
core/CHANGELOG.md#L6-L16: revert the direct 8.3.x edits and regenerate the core changelog.ios/CHANGELOG.md#L6-L16: revert the direct 8.3.x edits and regenerate the iOS changelog.
As per coding guidelines, CHANGELOG.md must not be manually edited because it is managed automatically by CI/CD.
📍 Affects 2 files
core/CHANGELOG.md#L6-L16(this comment)ios/CHANGELOG.md#L6-L16
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@core/CHANGELOG.md` around lines 6 - 16, Revert the manually added 8.3.x
entries in core/CHANGELOG.md lines 6-16 and regenerate the core changelog
through CI/CD. Apply the same change to ios/CHANGELOG.md lines 6-16; both
changelogs must remain CI/CD-managed artifacts and must not be edited directly.
Source: Coding guidelines
| if (body instanceof File) { | ||
| const fileData = await readFileAsBase64(body); | ||
| return { | ||
| data: fileData, | ||
| type: 'file', | ||
| headers: { 'Content-Type': body.type }, | ||
| }; | ||
| } else if (body instanceof Blob) { | ||
| return { | ||
| data: await readBlobAsBase64(body), | ||
| type: 'binary', | ||
| headers: { 'Content-Type': contentType || body.type || 'application/octet-stream' }, | ||
| }; | ||
| } else if (body instanceof ArrayBuffer) { | ||
| return { | ||
| data: readArrayBufferAsBase64(body), | ||
| type: 'binary', | ||
| headers: { 'Content-Type': contentType || 'application/octet-stream' }, | ||
| }; | ||
| } else if (ArrayBuffer.isView(body)) { | ||
| return { | ||
| data: readDataViewAsBase64(body), | ||
| type: 'binary', | ||
| headers: { 'Content-Type': contentType || 'application/octet-stream' }, | ||
| }; | ||
| } | ||
|
|
||
| if ((typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) || body instanceof Uint8Array) { | ||
| let encodedData: Uint8Array; | ||
| if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) { | ||
| const reader = body.getReader(); | ||
| const chunks: any[] = []; | ||
| const chunks: Uint8Array[] = []; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the source around convertBody and the later response/body handling.
sed -n '1,220p' core/native-bridge.ts
printf '\n--- JS asset (android) ---\n'
sed -n '1,220p' android/capacitor/src/main/assets/native-bridge.js
printf '\n--- JS asset (ios) ---\n'
sed -n '1,220p' ios/Capacitor/Capacitor/assets/native-bridge.jsRepository: Cap-go/capacitor-plus
Length of output: 24063
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for downstream classification logic and any tests or references to Uint8Array handling.
rg -n "Uint8Array|ArrayBuffer\.isView|ReadableStream|type: 'binary'|formData|json|text|image|responseType" core/native-bridge.ts android/capacitor/src/main/assets/native-bridge.js ios/Capacitor/Capacitor/assets/native-bridge.jsRepository: Cap-go/capacitor-plus
Length of output: 8273
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for p in [
Path("core/native-bridge.ts"),
Path("android/capacitor/src/main/assets/native-bridge.js"),
Path("ios/Capacitor/Capacitor/assets/native-bridge.js"),
]:
text = p.read_text()
# Print the exact convertBody block and the later request sending block if present.
for marker in ["const convertBody", "responseType: 'arraybuffer'", "ArrayBuffer.isView(body)", "body instanceof Uint8Array"]:
idx = text.find(marker)
if idx != -1:
start = max(0, text.rfind("\n", 0, idx-300))
end = min(len(text), idx+900)
print(f"\n### {p} :: {marker}\n")
print(text[start:end])
PYRepository: Cap-go/capacitor-plus
Length of output: 15826
Uint8Array never reaches the content-type-aware branch ArrayBuffer.isView(body) already matches it, so the later body instanceof Uint8Array path is dead. If raw Uint8Array bodies should still be classified by contentType, move that check above the view branch; otherwise remove it and regenerate the Android/iOS bridge assets.
📍 Affects 3 files
core/native-bridge.ts#L96-L127(this comment)android/capacitor/src/main/assets/native-bridge.js#L94-L126ios/Capacitor/Capacitor/assets/native-bridge.js#L94-L126
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@core/native-bridge.ts` around lines 96 - 127, Resolve the unreachable
Uint8Array handling in the body-encoding branch: move the Uint8Array check
before ArrayBuffer.isView(body) if raw Uint8Array bodies must honor contentType;
otherwise remove the dead Uint8Array path. Apply the same change to
core/native-bridge.ts, android/capacitor/src/main/assets/native-bridge.js, and
ios/Capacitor/Capacitor/assets/native-bridge.js, regenerating the platform
assets when removing the path.
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
(cherry picked from commit 5bfdb1d) Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
6 issues found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java">
<violation number="1" location="android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java:210">
P2: When a binary `Blob` or buffer uses an `application/json` content type, the JSON branch runs before this new `binary` branch and sends the base64 string verbatim. Handle `file`/`binary` bodies before the JSON branch so the request always writes the decoded bytes.</violation>
</file>
<file name="ios/Capacitor/Capacitor/assets/native-bridge.js">
<violation number="1" location="ios/Capacitor/Capacitor/assets/native-bridge.js:124">
P3: The new `ArrayBuffer.isView` return makes the `Uint8Array` path below unreachable. Remove the redundant branch and keep this block dedicated to `ReadableStream` handling.</violation>
</file>
<file name="android/capacitor/src/main/assets/native-bridge.js">
<violation number="1" location="android/capacitor/src/main/assets/native-bridge.js:117">
P3: This new view branch makes the existing Uint8Array branch unreachable. Remove the redundant Uint8Array arm and dead non-stream path, leaving the subsequent conversion only for `ReadableStream`.</violation>
<violation number="2" location="android/capacitor/src/main/assets/native-bridge.js:596">
P1: When Android returns a non-JSON 4xx/5xx body through its error stream, `base64StringToArrayBuffer` decodes plain text as base64 and throws. Preserve error-stream text or make the native error path return base64 before decoding it here.</violation>
</file>
<file name="core/src/tests/bridge.spec.ts">
<violation number="1" location="core/src/tests/bridge.spec.ts:286">
P3: The MockHeaders mock normalizes header keys with `toLocaleLowerCase()`, which is locale-dependent and can produce unexpected results in some locales (e.g. Turkish where `'I'`.toLocaleLowerCase() is 'ı'), whereas the real implementation in native-bridge.ts uses `toLowerCase()`. Use `toLowerCase()` so the mock's header semantics match production behavior.</violation>
</file>
<file name="core/native-bridge.ts">
<violation number="1" location="core/native-bridge.ts:651">
P1: Non-JSON HTTP error responses are now corrupted (or throw). With `responseType: 'arraybuffer'`, the Android native `readData` returns base64 only in the success/non-JSON branch; when the server returns an error status (errorStream != null), `HttpRequestHandler.readData` returns `readStreamAsString(errorStream)` — a raw text/HTML string, ignoring responseType. The interceptor then passes that raw string to `base64StringToArrayBuffer`, so a 404/500 body like `text/html` (contains chars outside the base64 alphabet) makes `atob` throw `InvalidCharacterError` and the fetch rejects; a plain-text error body is base64-decoded into garbage bytes, so `response.text()`/`arrayBuffer()` are wrong. Before this change the raw string was assigned directly to `data`. Only decode when the native layer actually returned base64 (non-error, non-JSON), i.e. gate on status >= 400 or on a response flag.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| ? null | ||
| : (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json')) | ||
| ? JSON.stringify(nativeResponse.data) | ||
| : base64StringToArrayBuffer(nativeResponse.data); |
There was a problem hiding this comment.
P1: When Android returns a non-JSON 4xx/5xx body through its error stream, base64StringToArrayBuffer decodes plain text as base64 and throws. Preserve error-stream text or make the native error path return base64 before decoding it here.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At android/capacitor/src/main/assets/native-bridge.js, line 596:
<comment>When Android returns a non-JSON 4xx/5xx body through its error stream, `base64StringToArrayBuffer` decodes plain text as base64 and throws. Preserve error-stream text or make the native error path return base64 before decoding it here.</comment>
<file context>
@@ -535,15 +585,15 @@ var nativeBridge = (function (exports) {
+ ? null
+ : (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json'))
+ ? JSON.stringify(nativeResponse.data)
+ : base64StringToArrayBuffer(nativeResponse.data);
// intercept & parse response before returning
const response = new Response(data, {
</file context>
| ? null | ||
| : contentType?.startsWith('application/json') | ||
| ? JSON.stringify(nativeResponse.data) | ||
| : base64StringToArrayBuffer(nativeResponse.data); |
There was a problem hiding this comment.
P1: Non-JSON HTTP error responses are now corrupted (or throw). With responseType: 'arraybuffer', the Android native readData returns base64 only in the success/non-JSON branch; when the server returns an error status (errorStream != null), HttpRequestHandler.readData returns readStreamAsString(errorStream) — a raw text/HTML string, ignoring responseType. The interceptor then passes that raw string to base64StringToArrayBuffer, so a 404/500 body like text/html (contains chars outside the base64 alphabet) makes atob throw InvalidCharacterError and the fetch rejects; a plain-text error body is base64-decoded into garbage bytes, so response.text()/arrayBuffer() are wrong. Before this change the raw string was assigned directly to data. Only decode when the native layer actually returned base64 (non-error, non-JSON), i.e. gate on status >= 400 or on a response flag.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At core/native-bridge.ts, line 651:
<comment>Non-JSON HTTP error responses are now corrupted (or throw). With `responseType: 'arraybuffer'`, the Android native `readData` returns base64 only in the success/non-JSON branch; when the server returns an error status (errorStream != null), `HttpRequestHandler.readData` returns `readStreamAsString(errorStream)` — a raw text/HTML string, ignoring responseType. The interceptor then passes that raw string to `base64StringToArrayBuffer`, so a 404/500 body like `text/html` (contains chars outside the base64 alphabet) makes `atob` throw `InvalidCharacterError` and the fetch rejects; a plain-text error body is base64-decoded into garbage bytes, so `response.text()`/`arrayBuffer()` are wrong. Before this change the raw string was assigned directly to `data`. Only decode when the native layer actually returned base64 (non-error, non-JSON), i.e. gate on status >= 400 or on a response flag.</comment>
<file context>
@@ -577,17 +638,17 @@ const initBridge = (w: any): void => {
+ ? null
+ : contentType?.startsWith('application/json')
+ ? JSON.stringify(nativeResponse.data)
+ : base64StringToArrayBuffer(nativeResponse.data);
// intercept & parse response before returning
</file context>
| } | ||
| os.flush(); | ||
| } | ||
| } else if (bodyType != null && (bodyType.equals("file") || bodyType.equals("binary"))) { |
There was a problem hiding this comment.
P2: When a binary Blob or buffer uses an application/json content type, the JSON branch runs before this new binary branch and sends the base64 string verbatim. Handle file/binary bodies before the JSON branch so the request always writes the decoded bytes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java, line 210:
<comment>When a binary `Blob` or buffer uses an `application/json` content type, the JSON branch runs before this new `binary` branch and sends the base64 string verbatim. Handle `file`/`binary` bodies before the JSON branch so the request always writes the decoded bytes.</comment>
<file context>
@@ -209,13 +207,8 @@ public void setRequestBody(PluginCall call, JSValue body, String bodyType) throw
- }
- os.flush();
- }
+ } else if (bodyType != null && (bodyType.equals("file") || bodyType.equals("binary"))) {
+ this.writeRequestBody(decodeBase64RequestBody(body.toString()));
} else if (contentType.contains("application/x-www-form-urlencoded")) {
</file context>
| headers: { 'Content-Type': contentType || 'application/octet-stream' }, | ||
| }; | ||
| } | ||
| if ((typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) || body instanceof Uint8Array) { |
There was a problem hiding this comment.
P3: The new ArrayBuffer.isView return makes the Uint8Array path below unreachable. Remove the redundant branch and keep this block dedicated to ReadableStream handling.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/Capacitor/Capacitor/assets/native-bridge.js, line 124:
<comment>The new `ArrayBuffer.isView` return makes the `Uint8Array` path below unreachable. Remove the redundant branch and keep this block dedicated to `ReadableStream` handling.</comment>
<file context>
@@ -65,9 +92,38 @@ var nativeBridge = (function (exports) {
+ headers: { 'Content-Type': contentType || 'application/octet-stream' },
+ };
+ }
+ if ((typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) || body instanceof Uint8Array) {
let encodedData;
- if (body instanceof ReadableStream) {
</file context>
| headers: { 'Content-Type': contentType || 'application/octet-stream' }, | ||
| }; | ||
| } | ||
| else if (ArrayBuffer.isView(body)) { |
There was a problem hiding this comment.
P3: This new view branch makes the existing Uint8Array branch unreachable. Remove the redundant Uint8Array arm and dead non-stream path, leaving the subsequent conversion only for ReadableStream.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At android/capacitor/src/main/assets/native-bridge.js, line 117:
<comment>This new view branch makes the existing Uint8Array branch unreachable. Remove the redundant Uint8Array arm and dead non-stream path, leaving the subsequent conversion only for `ReadableStream`.</comment>
<file context>
@@ -65,9 +92,38 @@ var nativeBridge = (function (exports) {
+ headers: { 'Content-Type': contentType || 'application/octet-stream' },
+ };
+ }
+ else if (ArrayBuffer.isView(body)) {
+ return {
+ data: readDataViewAsBase64(body),
</file context>
| return; | ||
| } | ||
|
|
||
| Object.entries(init as Record<string, string>).forEach(([key, value]) => { |
There was a problem hiding this comment.
P3: The MockHeaders mock normalizes header keys with toLocaleLowerCase(), which is locale-dependent and can produce unexpected results in some locales (e.g. Turkish where 'I'.toLocaleLowerCase() is 'ı'), whereas the real implementation in native-bridge.ts uses toLowerCase(). Use toLowerCase() so the mock's header semantics match production behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At core/src/tests/bridge.spec.ts, line 286:
<comment>The MockHeaders mock normalizes header keys with `toLocaleLowerCase()`, which is locale-dependent and can produce unexpected results in some locales (e.g. Turkish where `'I'`.toLocaleLowerCase() is 'ı'), whereas the real implementation in native-bridge.ts uses `toLowerCase()`. Use `toLowerCase()` so the mock's header semantics match production behavior.</comment>
<file context>
@@ -136,11 +266,88 @@ describe('bridge', () => {
+ return;
+ }
+
+ Object.entries(init as Record<string, string>).forEach(([key, value]) => {
+ this.headers[key.toLocaleLowerCase()] = value;
+ });
</file context>


Merge Conflict Review Required
The sync of upstream PR ionic-team#8478 from @iamanaws encountered merge conflicts.
Original PR: ionic-team#8478
What happened
Synced from upstream by Capacitor+ Bot
Summary by CodeRabbit
New Features
Bug Fixes
fetch.Documentation