chore: sync upstream PR #8511 - fix(http): make HttpResponse data type generic#85
chore: sync upstream PR #8511 - fix(http): make HttpResponse data type generic#85riderx wants to merge 23 commits into
Conversation
Co-authored-by: jcesarmobile <jcesarmobile@gmail.com>
Co-authored-by: Joey Pender <joey.pender@outsystems.com> Co-authored-by: Pedro Bilro <pedro.gustavo.bilro@outsystems.com> Co-authored-by: Mark Anderson <mark.anderson@outsystems.com>
…#8476) Co-authored-by: jcesarmobile <jcesarmobile@gmail.com>
…am#8492) Co-authored-by: Mark Anderson <mark.anderson@outsystems.com>
…m-preferred fallback)
|
Claude Code could not resolve this upstream sync completely. 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. |
📝 WalkthroughWalkthroughChangesCLI features and runtime handling
Android runtime behavior
Public APIs and release packaging
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant SwiftPMGenerator
participant PackageSwift
participant SymlinkDirectory
CLI->>SwiftPMGenerator: read packageOptions
SwiftPMGenerator->>SymlinkDirectory: create optional plugin symlink
SwiftPMGenerator->>PackageSwift: generate dependency and alias entries
CLI->>PackageSwift: patch incompatible Capacitor version
🚥 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: 13
🤖 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 @.github/workflows/test.yml:
- Line 33: Restore the timeout-minutes values in all six affected jobs of the
test workflow to 10, preserving the release-triggered workflow contract
documented by the caller. Update each occurrence at the referenced job
definitions and do not leave any at 30 minutes.
In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`:
- Line 58: Normalize configuredInsetsHandling case-insensitively before
comparing it with INSETS_HANDLING_CSS and INSETS_HANDLING_DISABLE, matching the
existing style handling in SystemBars. Update the logic around the
insetsHandling configuration and its comparisons so values such as “CSS” and
“Disable” select their intended modes while preserving the existing fallback
warning for unsupported values.
In `@android/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.java`:
- Around line 22-72: Extend SystemBarsTest beyond the current show-only coverage
by parameterizing invokeSetHidden to accept the hide flag and adding assertions
for hide=true that verify controller.hide calls for system, status, and
navigation bars. Add initSystemBars tests covering valid css and disable
insetsHandling values, plus an unknown value that verifies warning logging and
fallback to css.
In `@android/CHANGELOG.md`:
- Around line 10-12: Update the generated changelog heading hierarchy around the
8.4.0 release heading so its “Bug Fixes” subsection uses the immediate child
heading level instead of skipping level 2. Preserve the existing release heading
and apply the same consistent hierarchy to generated changelog sections.
In `@CHANGELOG.md`:
- Around line 13-15: Update the 8.4.0 release heading in the changelog to level
2, or consistently change its subsection headings to level 2, so the heading
hierarchy does not skip a level and satisfies MD001.
- Around line 6-12: Remove the manual changes to the root CHANGELOG.md,
including the 8.4.1 section, and regenerate the changelog through the
repository’s release tooling so CI/CD remains the source of truth.
In `@cli/CHANGELOG.md`:
- Around line 13-15: Update the generated changelog heading hierarchy around the
8.4.0 release heading so its “Bug Fixes” subsection uses the next sequential
heading level instead of skipping level 2. Preserve consistent heading levels
for equivalent release sections throughout cli/CHANGELOG.md.
In `@cli/src/declarations.ts`:
- Around line 809-821: Add `@since` documentation tags to the public
PackageOptions interface and its symlink and moduleAliases members, using the
version that introduced each API and matching the existing documentation style
in declarations.ts.
In `@cli/src/ios/update.ts`:
- Around line 62-85: Move the awaited getCapacitorPackageVersion call out of the
validSPMPackages.map callback and compute iosPlatformVersion once before
Promise.all, then reuse it for majorCapVersion in each plugin iteration while
preserving the existing package update behavior.
- Around line 62-85: Update the per-plugin callback in the Promise.all map
around readFile and writeFile so each plugin’s Package.swift update is handled
independently. Catch failures within the callback, log a warning identifying the
affected plugin and error, and allow the remaining validSPMPackages updates to
continue.
In `@cli/src/util/spm.ts`:
- Around line 136-142: Update the symlink destination construction in the
plugin-processing flow to key the folder by the unique plugin.id rather than
plugin.name. Use the same ID-based folder consistently for symlinkFolder,
relPath, and the ensureSymlink destination, while preserving the existing
symlink behavior.
In `@core/src/core-plugins.ts`:
- Around line 265-269: Thread a generic type parameter through WebPlugin.request
so its Promise<HttpResponse> return type preserves the requested response data
type instead of defaulting to any. Update the method signature and
implementation at WebPlugin.request, including any required request/callback
typing, while preserving existing behavior and allowing callers to infer or
specify the response payload type.
In `@ios/CHANGELOG.md`:
- Around line 10-12: Update the generated changelog heading hierarchy around the
8.4.0 release entry so the “Features” heading does not skip level 2 beneath the
top-level release heading. Apply the consistent heading structure used by the
changelog generator for all release sections.
🪄 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: a4ea00d0-a852-4fb7-b31f-f0bb91019e24
📒 Files selected for processing (26)
.github/workflows/test.ymlCHANGELOG.mdandroid/CHANGELOG.mdandroid/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/util/JSONUtils.javaandroid/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.javaandroid/package.jsoncli/CHANGELOG.mdcli/package.jsoncli/src/declarations.tscli/src/ios/update.tscli/src/ipc.tscli/src/tasks/run.tscli/src/telemetry.tscli/src/util/spm.tscli/test/telemetry.spec.tscore/CHANGELOG.mdcore/http.mdcore/package.jsoncore/src/core-plugins.tsios/CHANGELOG.mdios/Capacitor/Capacitor/PluginConfig.swiftios/package.jsonlerna.json
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
| runs-on: ubuntu-latest | ||
| # Keep this job capped at 10 minutes; never raise it unless explicitly asked. | ||
| timeout-minutes: 10 | ||
| timeout-minutes: 30 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Preserve the 10-minute timeout contract for release-triggered tests.
.github/workflows/bump_version.yml explicitly states that called workflow jobs are capped at 10 minutes and must not be raised. These six changes allow those jobs to run for 30 minutes. Revert them to 10 minutes, or update the caller and documented policy with explicit approval.
Also applies to: 47-47, 63-63, 84-84, 105-105, 133-133
🤖 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 @.github/workflows/test.yml at line 33, Restore the timeout-minutes values in
all six affected jobs of the test workflow to 10, preserving the
release-triggered workflow contract documented by the caller. Update each
occurrence at the referenced job definitions and do not leave any at 30 minutes.
| """; | ||
|
|
||
| private boolean insetHandlingEnabled = true; | ||
| private String insetsHandling = INSETS_HANDLING_CSS; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Case-sensitive comparison for insetsHandling inconsistent with style handling.
style is normalized with .toUpperCase(Locale.US) before comparison (line 96), but configuredInsetsHandling is compared as-is against INSETS_HANDLING_CSS/INSETS_HANDLING_DISABLE. A user setting "CSS" or "Disable" will silently fall back to css mode with a warning log, which is likely unintended given the case-insensitive precedent set for style.
🔧 Proposed fix
- String configuredInsetsHandling = getConfig().getString("insetsHandling", INSETS_HANDLING_CSS);
+ String configuredInsetsHandling = getConfig().getString("insetsHandling", INSETS_HANDLING_CSS).toLowerCase(Locale.US);Also applies to: 99-108
🤖 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` at
line 58, Normalize configuredInsetsHandling case-insensitively before comparing
it with INSETS_HANDLING_CSS and INSETS_HANDLING_DISABLE, matching the existing
style handling in SystemBars. Update the logic around the insetsHandling
configuration and its comparisons so values such as “CSS” and “Disable” select
their intended modes while preserving the existing fallback warning for
unsupported values.
| @Test | ||
| public void showWithEmptyBarShowsSystemBars() throws Exception { | ||
| WindowInsetsControllerCompat controller = invokeSetHidden(""); | ||
|
|
||
| verify(controller).show(WindowInsetsCompat.Type.systemBars()); | ||
| verify(controller, never()).show(WindowInsetsCompat.Type.statusBars()); | ||
| verify(controller, never()).show(WindowInsetsCompat.Type.navigationBars()); | ||
| } | ||
|
|
||
| @Test | ||
| public void showWithStatusBarShowsOnlyStatusBars() throws Exception { | ||
| WindowInsetsControllerCompat controller = invokeSetHidden("StatusBar"); | ||
|
|
||
| verify(controller).show(WindowInsetsCompat.Type.statusBars()); | ||
| verify(controller, never()).show(WindowInsetsCompat.Type.systemBars()); | ||
| verify(controller, never()).show(WindowInsetsCompat.Type.navigationBars()); | ||
| } | ||
|
|
||
| @Test | ||
| public void showWithNavigationBarShowsOnlyNavigationBars() throws Exception { | ||
| WindowInsetsControllerCompat controller = invokeSetHidden("NavigationBar"); | ||
|
|
||
| verify(controller).show(WindowInsetsCompat.Type.navigationBars()); | ||
| verify(controller, never()).show(WindowInsetsCompat.Type.systemBars()); | ||
| verify(controller, never()).show(WindowInsetsCompat.Type.statusBars()); | ||
| } | ||
|
|
||
| private WindowInsetsControllerCompat invokeSetHidden(String bar) throws Exception { | ||
| SystemBars plugin = new SystemBars(); | ||
| Bridge bridge = mock(Bridge.class); | ||
| AppCompatActivity activity = mock(AppCompatActivity.class); | ||
| Window window = mock(Window.class); | ||
| View decorView = mock(View.class); | ||
| WindowInsetsControllerCompat controller = mock(WindowInsetsControllerCompat.class); | ||
|
|
||
| when(bridge.getActivity()).thenReturn(activity); | ||
| when(activity.getWindow()).thenReturn(window); | ||
| when(window.getDecorView()).thenReturn(decorView); | ||
|
|
||
| plugin.setBridge(bridge); | ||
|
|
||
| try (MockedStatic<WindowCompat> windowCompat = mockStatic(WindowCompat.class)) { | ||
| windowCompat.when(() -> WindowCompat.getInsetsController(window, decorView)).thenReturn(controller); | ||
|
|
||
| Method setHidden = SystemBars.class.getDeclaredMethod("setHidden", boolean.class, String.class); | ||
| setHidden.setAccessible(true); | ||
| setHidden.invoke(plugin, false, bar); | ||
| } | ||
|
|
||
| return controller; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Test coverage gap: only hide=false (show) is exercised; hide path and insetsHandling validation are untested.
All three tests invoke setHidden with hide=false only, leaving the hide=true branch (lines 307-315 in SystemBars.java) unverified. Additionally, the new config-validation/fallback logic in initSystemBars() (unknown value → warning + fallback to "css") has no test coverage in this new test class.
Consider parameterizing invokeSetHidden to also test hide=true, and adding a test exercising initSystemBars() with valid (css, disable) and invalid insetsHandling config values.
🤖 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/test/java/com/getcapacitor/plugin/SystemBarsTest.java`
around lines 22 - 72, Extend SystemBarsTest beyond the current show-only
coverage by parameterizing invokeSetHidden to accept the hide flag and adding
assertions for hide=true that verify controller.hide calls for system, status,
and navigation bars. Add initSystemBars tests covering valid css and disable
insetsHandling values, plus an unknown value that verifies warning logging and
fallback to css.
| # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) | ||
|
|
||
| ### Bug Fixes |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the heading hierarchy.
The # 8.4.0 heading followed by ### Bug Fixes skips level 2 and triggers MD001. Use consistent heading levels in the generated changelog.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 12-12: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 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/CHANGELOG.md` around lines 10 - 12, Update the generated changelog
heading hierarchy around the 8.4.0 release heading so its “Bug Fixes” subsection
uses the immediate child heading level instead of skipping level 2. Preserve the
existing release heading and apply the same consistent hierarchy to generated
changelog sections.
Source: Linters/SAST tools
| ## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) | ||
|
|
||
| ### Bug Fixes | ||
|
|
||
| - **cli:** make SPM dependency patch work on prereleases ([#8508](https://github.com/ionic-team/capacitor/issues/8508)) ([6048e90](https://github.com/ionic-team/capacitor/commit/6048e90171afa0229a3c25b52a23c377c6bb804c)) | ||
| - **cli:** patch Capacitor SPM dependency version in plugins ([#8492](https://github.com/ionic-team/capacitor/issues/8492)) ([28bb2c6](https://github.com/ionic-team/capacitor/commit/28bb2c687069dfdd6aa7abc866004a1c6388d103)) | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Do not manually edit the root changelog.
CHANGELOG.md is managed by CI/CD per the repository instructions. Regenerate this section through the release tooling rather than resolving it manually in this PR.
🤖 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 `@CHANGELOG.md` around lines 6 - 12, Remove the manual changes to the root
CHANGELOG.md, including the 8.4.1 section, and regenerate the changelog through
the repository’s release tooling so CI/CD remains the source of truth.
Source: Coding guidelines
|
|
||
| export interface PackageOptions { | ||
| /** | ||
| * Create a symlink to the plugin folder instead of pointing to the plugin path. | ||
| * Useful when plugin names conflict. | ||
| */ | ||
| symlink?: boolean; | ||
| /** | ||
| * Useful to avoid target name conflicts in dependencies | ||
| * [see](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/modulealiasing/) | ||
| */ | ||
| moduleAliases?: { [target: string]: string }; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Add @since tags to the new PackageOptions interface for consistency.
Every other public config field/interface in this file documents a @since version (e.g., packageOptions itself at Line 566 has @since 8.4.0), but the new PackageOptions interface and its symlink/moduleAliases members have none. This is likely rendered on the public docs site, so missing tags create an inconsistent/incomplete API reference.
📝 Proposed fix
export interface PackageOptions {
/**
* Create a symlink to the plugin folder instead of pointing to the plugin path.
* Useful when plugin names conflict.
+ *
+ * `@since` 8.4.0
*/
symlink?: boolean;
/**
* Useful to avoid target name conflicts in dependencies
* [see](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/modulealiasing/)
+ *
+ * `@since` 8.4.0
*/
moduleAliases?: { [target: string]: string };
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface PackageOptions { | |
| /** | |
| * Create a symlink to the plugin folder instead of pointing to the plugin path. | |
| * Useful when plugin names conflict. | |
| */ | |
| symlink?: boolean; | |
| /** | |
| * Useful to avoid target name conflicts in dependencies | |
| * [see](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/modulealiasing/) | |
| */ | |
| moduleAliases?: { [target: string]: string }; | |
| } | |
| export interface PackageOptions { | |
| /** | |
| * Create a symlink to the plugin folder instead of pointing to the plugin path. | |
| * Useful when plugin names conflict. | |
| * | |
| * `@since` 8.4.0 | |
| */ | |
| symlink?: boolean; | |
| /** | |
| * Useful to avoid target name conflicts in dependencies | |
| * [see](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/modulealiasing/) | |
| * | |
| * `@since` 8.4.0 | |
| */ | |
| moduleAliases?: { [target: string]: string }; | |
| } |
🤖 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 `@cli/src/declarations.ts` around lines 809 - 821, Add `@since` documentation
tags to the public PackageOptions interface and its symlink and moduleAliases
members, using the version that introduced each API and matching the existing
documentation style in declarations.ts.
| await Promise.all( | ||
| validSPMPackages.map(async (plugin) => { | ||
| const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); | ||
| const packageSwiftPath = join(plugin.rootPath, 'Package.swift'); | ||
| let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); | ||
| const regex = new RegExp( | ||
| 'url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"', | ||
| ); | ||
| const version = content.match(regex)?.[1]; | ||
| const majorCapVersion = major(iosPlatformVersion); | ||
| if (version && major(version) != majorCapVersion) { | ||
| const preCapVersion = prerelease(iosPlatformVersion); | ||
| const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`; | ||
| content = setAllStringIn( | ||
| content, | ||
| `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, | ||
| `)`, | ||
| ` from: "${forceVersion}"`, | ||
| ); | ||
| await writeFile(packageSwiftPath, content); | ||
| logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); | ||
| } | ||
| }), | ||
| ); |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
Hoist getCapacitorPackageVersion out of the per-plugin loop.
iosPlatformVersion/majorCapVersion don't depend on plugin, but getCapacitorPackageVersion(config, config.ios.name) is awaited once per entry in validSPMPackages. Compute it once before Promise.all.
♻️ Proposed fix
const validSPMPackages = await checkPluginsForPackageSwift(config, plugins);
+ const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name);
+ const majorCapVersion = major(iosPlatformVersion);
await Promise.all(
validSPMPackages.map(async (plugin) => {
- const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name);
const packageSwiftPath = join(plugin.rootPath, 'Package.swift');
let content = await readFile(packageSwiftPath, { encoding: 'utf-8' });
const regex = new RegExp(
'url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"',
);
const version = content.match(regex)?.[1];
- const majorCapVersion = major(iosPlatformVersion);
if (version && major(version) != majorCapVersion) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await Promise.all( | |
| validSPMPackages.map(async (plugin) => { | |
| const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); | |
| const packageSwiftPath = join(plugin.rootPath, 'Package.swift'); | |
| let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); | |
| const regex = new RegExp( | |
| 'url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"', | |
| ); | |
| const version = content.match(regex)?.[1]; | |
| const majorCapVersion = major(iosPlatformVersion); | |
| if (version && major(version) != majorCapVersion) { | |
| const preCapVersion = prerelease(iosPlatformVersion); | |
| const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`; | |
| content = setAllStringIn( | |
| content, | |
| `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, | |
| `)`, | |
| ` from: "${forceVersion}"`, | |
| ); | |
| await writeFile(packageSwiftPath, content); | |
| logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); | |
| } | |
| }), | |
| ); | |
| const validSPMPackages = await checkPluginsForPackageSwift(config, plugins); | |
| const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); | |
| const majorCapVersion = major(iosPlatformVersion); | |
| await Promise.all( | |
| validSPMPackages.map(async (plugin) => { | |
| const packageSwiftPath = join(plugin.rootPath, 'Package.swift'); | |
| let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); | |
| const regex = new RegExp( | |
| 'url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"', | |
| ); | |
| const version = content.match(regex)?.[1]; | |
| if (version && major(version) != majorCapVersion) { | |
| const preCapVersion = prerelease(iosPlatformVersion); | |
| const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`; | |
| content = setAllStringIn( | |
| content, | |
| `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, | |
| `)`, | |
| ` from: "${forceVersion}"`, | |
| ); | |
| await writeFile(packageSwiftPath, content); | |
| logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); | |
| } | |
| }), | |
| ); |
🤖 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 `@cli/src/ios/update.ts` around lines 62 - 85, Move the awaited
getCapacitorPackageVersion call out of the validSPMPackages.map callback and
compute iosPlatformVersion once before Promise.all, then reuse it for
majorCapVersion in each plugin iteration while preserving the existing package
update behavior.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -A5 'rootPath' cli/src/util/plugins.ts
rg -n -B2 -A15 'function checkPluginsForPackageSwift' cli/srcRepository: Cap-go/capacitor-plus
Length of output: 1390
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== update.ts =="
sed -n '1,180p' cli/src/ios/update.ts
echo
echo "== spm.ts =="
sed -n '1,220p' cli/src/util/spm.ts
echo
echo "== rootPath search =="
rg -n "rootPath" cli/src
echo
echo "== Package.swift mutation search =="
rg -n "Package.swift|writeFile\\(packageSwiftPath|readFile\\(packageSwiftPath" cli/srcRepository: Cap-go/capacitor-plus
Length of output: 19913
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== update.ts (focused) =="
nl -ba cli/src/ios/update.ts | sed -n '1,170p'
echo
echo "== spm.ts =="
nl -ba cli/src/util/spm.ts | sed -n '1,220p'
echo
echo "== rootPath + Package.swift references =="
rg -n "rootPath|Package\\.swift|writeFile\\(packageSwiftPath|readFile\\(packageSwiftPath" cli/srcRepository: Cap-go/capacitor-plus
Length of output: 225
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== plugin.ts (rootPath resolution) =="
sed -n '1,140p' cli/src/plugin.ts
echo
echo "== spm.ts (Package.swift existence check) =="
sed -n '280,320p' cli/src/util/spm.ts
echo
echo "== updateIOS / updatePluginFiles callers =="
rg -n "updateIOS\\(|updatePluginFiles\\(" cli/srcRepository: Cap-go/capacitor-plus
Length of output: 5078
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' cli/src/util/node.tsRepository: Cap-go/capacitor-plus
Length of output: 1920
Handle each plugin Package.swift update independently. A single readFile/writeFile failure inside Promise.all(...map(...)) rejects the whole SPM update, so one bad plugin blocks every other plugin. Catch per plugin, log a warning, and continue.
🤖 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 `@cli/src/ios/update.ts` around lines 62 - 85, Update the per-plugin callback
in the Promise.all map around readFile and writeFile so each plugin’s
Package.swift update is handled independently. Catch failures within the
callback, log a warning identifying the affected plugin and error, and allow the
remaining validSPMPackages updates to continue.
| const options = packageOptions[plugin.id]; | ||
| const symlink = options?.symlink; | ||
| const symlinkFolder = join('symlinks', plugin.name); | ||
| const relPath = symlink ? symlinkFolder : relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); | ||
| if (symlink) { | ||
| await ensureSymlink(plugin.rootPath, resolve(config.ios.nativeProjectDirAbs, 'CapApp-SPM', symlinkFolder)); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -A10 'ensureSymlink' node_modules/fs-extra/lib/ensure/symlink.js 2>/dev/null || echo "fs-extra not vendored in sandbox"Repository: Cap-go/capacitor-plus
Length of output: 192
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== spm.ts around the reported lines ==\n'
sed -n '1,240p' cli/src/util/spm.ts | cat -n
printf '\n== search for PackageOptions and symlink docs/usages ==\n'
rg -n -A4 -B4 'PackageOptions|symlink' cli/src . -g '!**/node_modules/**' | sed -n '1,220p'
printf '\n== locate other symlink destination patterns ==\n'
rg -n "join\\('symlinks'|symlinks/" cli/src . -g '!**/node_modules/**' | sed -n '1,220p'Repository: Cap-go/capacitor-plus
Length of output: 20857
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== Plugin type definition ==\n'
sed -n '1,220p' cli/src/plugin.ts | cat -n
printf '\n== usages of plugin.id and plugin.name around path generation ==\n'
rg -n -A2 -B2 'plugin\.id|plugin\.name' cli/src/util/spm.ts cli/src/plugin.ts cli/src/declarations.ts | sed -n '1,220p'Repository: Cap-go/capacitor-plus
Length of output: 10123
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('cli/src/util/spm.ts')
text = p.read_text()
for needle in ["symlinkFolder = join('symlinks', plugin.name)", "packageOptions[plugin.id]", "join('symlinks', plugin.id)"]:
print(needle, 'FOUND' if needle in text else 'MISSING')
PYRepository: Cap-go/capacitor-plus
Length of output: 279
🏁 Script executed (no clone):
#!/bin/bash
set -euo pipefail
node <<'JS'
const fs = require('fs/promises');
const path = require('path');
const os = require('os');
(async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'symlink-collision-'));
const src1 = path.join(dir, 'plugin-a');
const src2 = path.join(dir, 'plugin-b');
const link = path.join(dir, 'symlinks', 'same-name');
await fs.mkdir(src1, { recursive: true });
await fs.mkdir(src2, { recursive: true });
await fs.mkdir(path.dirname(link), { recursive: true });
await fs.symlink(src1, link, 'dir');
try {
await fs.symlink(src2, link, 'dir');
console.log('second symlink: unexpectedly succeeded');
} catch (e) {
console.log(`second symlink error: ${e.code}`);
}
const target = await fs.readlink(link);
console.log(`final link target: ${target}`);
})();
JS
Length of output: 212
Key symlink destinations by plugin ID
PackageOptions.symlink is meant to handle plugin-name conflicts, but symlinks/<plugin.name> still collides for plugins with the same name. That makes the symlinked SPM package path non-unique and can break installs for one of the plugins.
🐛 Proposed fix
const options = packageOptions[plugin.id];
const symlink = options?.symlink;
- const symlinkFolder = join('symlinks', plugin.name);
+ const symlinkFolder = join('symlinks', plugin.id);
const relPath = symlink ? symlinkFolder : relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const options = packageOptions[plugin.id]; | |
| const symlink = options?.symlink; | |
| const symlinkFolder = join('symlinks', plugin.name); | |
| const relPath = symlink ? symlinkFolder : relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); | |
| if (symlink) { | |
| await ensureSymlink(plugin.rootPath, resolve(config.ios.nativeProjectDirAbs, 'CapApp-SPM', symlinkFolder)); | |
| } | |
| const options = packageOptions[plugin.id]; | |
| const symlink = options?.symlink; | |
| const symlinkFolder = join('symlinks', plugin.id); | |
| const relPath = symlink ? symlinkFolder : relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); | |
| if (symlink) { | |
| await ensureSymlink(plugin.rootPath, resolve(config.ios.nativeProjectDirAbs, 'CapApp-SPM', symlinkFolder)); | |
| } |
🤖 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 `@cli/src/util/spm.ts` around lines 136 - 142, Update the symlink destination
construction in the plugin-processing flow to key the folder by the unique
plugin.id rather than plugin.name. Use the same ID-based folder consistently for
symlinkFolder, relPath, and the ensureSymlink destination, while preserving the
existing symlink behavior.
| export interface HttpResponse<T = any> { | ||
| /** | ||
| * Additional data received with the Http response. | ||
| */ | ||
| data: any; | ||
| data: T; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Consider threading the generic through WebPlugin.request.
HttpResponse is converted to a generic interface HttpResponse<T = any>, and the data property changes from the untyped any to T, but the request implementation at line 404 still declares Promise<HttpResponse> (implicitly HttpResponse<any>), so callers get no compile-time benefit from the new generic unless they manually cast the result, as done in the updated docs (core/http.md).
♻️ Optional generic passthrough
- async request(options: HttpOptions): Promise<HttpResponse> {
+ async request<T = any>(options: HttpOptions): Promise<HttpResponse<T>> {Also applies to: 404-448
🤖 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/src/core-plugins.ts` around lines 265 - 269, Thread a generic type
parameter through WebPlugin.request so its Promise<HttpResponse> return type
preserves the requested response data type instead of defaulting to any. Update
the method signature and implementation at WebPlugin.request, including any
required request/callback typing, while preserving existing behavior and
allowing callers to infer or specify the response payload type.
| # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) | ||
|
|
||
| ### Features |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the heading hierarchy.
The # 8.4.0 heading followed by ### Features skips level 2 and triggers MD001. Use consistent heading levels in the generated changelog.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 12-12: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 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/CHANGELOG.md` around lines 10 - 12, Update the generated changelog
heading hierarchy around the 8.4.0 release entry so the “Features” heading does
not skip level 2 beneath the top-level release heading. Apply the consistent
heading structure used by the changelog generator for all release sections.
Source: Linters/SAST tools


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