Skip to content

Fix/network url and body handling - #13

Merged
buivietphi merged 16 commits into
mainfrom
fix/network-url-and-body-handling
Jul 4, 2026
Merged

buivietphi merged 16 commits into
mainfrom
fix/network-url-and-body-handling

Conversation

@buivietphi

@buivietphi buivietphi commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Description

Related Issue

Type of Change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation update
  • Performance improvement
  • CI/CD or build configuration
  • Other (describe below)

Testing

  • Tested on macOS
  • Tested on Windows
  • Flutter analyze passes
  • SDK build passes (if applicable)

Screenshots (if applicable)

Summary by CodeRabbit

  • New Features

    • Added a new top-bar tips pill with quick SDK compatibility information.
    • Improved view modes across logs, network, storage, and state screens with Tree / JSON / Code switching.
    • Added clearer screenshot naming and saved-path feedback.
  • Bug Fixes

    • Improved network request detection, including better handling of URLs, headers, and request bodies.
    • Added support for clearing stale pending requests.
    • Enhanced binary payload handling and JSON rendering for faster, more reliable displays.

phibvcfc added 16 commits July 3, 2026 13:04
…ee, JSON, and platform-specific code support
…ifecycle-based invalidation for JSON highlights
…capture stability, and standardized byte formatting
@buivietphi
buivietphi merged commit c1ac133 into main Jul 4, 2026
1 of 5 checks passed
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a95d7be5-7b4e-431d-acbe-fb3cff23c810

📥 Commits

Reviewing files that changed from the base of the PR and between 96dfbd5 and 37baf8c.

📒 Files selected for processing (40)
  • client_sdks/devconnect-react-native/src/client.ts
  • lib/app.dart
  • lib/components/feedback/lib_update_tips.dart
  • lib/components/misc/service_tag.dart
  • lib/components/viewers/json_viewer.dart
  • lib/core/constants/app_constants.dart
  • lib/core/routes/app_shell.dart
  • lib/core/theme/theme_provider.dart
  • lib/core/utils/log_message_summary.dart
  • lib/core/utils/lru_cache.dart
  • lib/core/utils/network_service_detector.dart
  • lib/core/utils/network_url_utils.dart
  • lib/core/utils/screenshot_filename.dart
  • lib/core/utils/screenshot_utils.dart
  • lib/features/all_events/presentation/pages/all_events_page.dart
  • lib/features/all_events/provider/all_events_provider.dart
  • lib/features/console/presentation/pages/console_page.dart
  • lib/features/error_inspector/presentation/pages/error_inspector_page.dart
  • lib/features/network_inspector/presentation/pages/network_inspector_page.dart
  • lib/features/network_inspector/provider/network_providers.dart
  • lib/features/performance/presentation/pages/memory_leaks_page.dart
  • lib/features/settings/presentation/pages/settings_page.dart
  • lib/features/state_inspector/presentation/pages/state_inspector_page.dart
  • lib/features/storage_viewer/presentation/pages/storage_viewer_page.dart
  • lib/l10n/app_en.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_ja.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_ja.dart
  • lib/l10n/app_localizations_vi.dart
  • lib/l10n/app_localizations_zh.dart
  • lib/l10n/app_vi.arb
  • lib/l10n/app_zh.arb
  • lib/l10n/app_zh_CN.arb
  • lib/l10n/app_zh_TW.arb
  • lib/l10n/untranslated.txt
  • lib/models/network/network_entry.dart
  • lib/server/ws_message_handler.dart

📝 Walkthrough

Walkthrough

This PR spans two platforms: the React Native SDK reworks fetch/XHR header and body interception, while the Flutter app adds backend service detection, stale network-request cleanup, an LRU-based JSON highlight cache with deferred/async rendering widgets, redesigned screenshot naming across detail panels, a new SDK-tips widget, and extensive localization additions.

Changes

React Native SDK

Layer / File(s) Summary
Fetch/XHR interception rework
client_sdks/devconnect-react-native/src/client.ts
Adds header-tracking wrappers for fetch, reworks method/url/body extraction (including Request inputs), and coerces XHR open URL arguments to strings.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Flutter DevConnect App

Layer / File(s) Summary
Core utilities
lib/core/utils/lru_cache.dart, lib/core/utils/network_url_utils.dart, lib/core/utils/network_service_detector.dart, lib/core/utils/screenshot_filename.dart, lib/core/utils/screenshot_utils.dart, lib/core/constants/app_constants.dart, lib/core/utils/log_message_summary.dart
Adds a byte-budgeted LruCache with pinning, URL malformation detection/normalization, service detection from headers/body, screenshot filename builders, an updated captureWidgetAsImage API returning saved path, formatBytes, and a large-payload log summary threshold.
JSON viewer caching & deferred widgets
lib/components/viewers/json_viewer.dart, lib/app.dart
Replaces the highlight cache with LruCache plus a lifecycle observer, and adds ViewModeSwitcher, DeferredBuilder, AsyncJsonParser, and LazyTab; wires cache lifecycle/invalidation into the app root.
Network id dedup and service detection
lib/models/network/network_entry.dart, lib/server/ws_message_handler.dart, lib/features/network_inspector/provider/network_providers.dart
Adds serviceName/serviceAction fields, stable round-trip/one-shot id generation, service detection wiring, and stale-request counting/clearing/merging logic.
Network inspector UI
lib/features/network_inspector/presentation/pages/network_inspector_page.dart, lib/components/misc/service_tag.dart
Adds a "Clear stale" pill, ServiceTag rendering, blob payload detection with dedicated display, lazy tab bodies, and async/deferred body rendering.
All Events page & provider
lib/features/all_events/presentation/pages/all_events_page.dart, lib/features/all_events/provider/all_events_provider.dart
Updates network titles with service actions, renders ServiceTag, redesigns storage detail/screenshot rendering, and reworks body views to use AsyncJsonParser/DeferredBuilder.
Detail panels: view modes & screenshot naming
lib/features/console/presentation/pages/console_page.dart, lib/features/error_inspector/presentation/pages/error_inspector_page.dart, lib/features/state_inspector/presentation/pages/state_inspector_page.dart, lib/features/settings/presentation/pages/settings_page.dart, lib/features/performance/presentation/pages/memory_leaks_page.dart, lib/core/theme/theme_provider.dart
Adds metadataViewModeProvider-driven blocks, LazyTab wrapping, rich screenshot filenames, mode-aware description text, and delegated byte formatting.
Storage viewer redesign
lib/features/storage_viewer/presentation/pages/storage_viewer_page.dart
Converts the detail panel to ConsumerStatefulWidget, adds JSON-shape detection, view-mode switching, and redesigned metadata/header/screenshot rendering.
SDK tips widget
lib/components/feedback/lib_update_tips.dart, lib/core/routes/app_shell.dart
Adds a dismissible SDK-compatibility tips pill with collapsed/expanded states, wired into the app shell title bar.
Localization
lib/l10n/*.arb, lib/l10n/app_localizations*.dart, lib/l10n/untranslated.txt
Adds/updates translation entries for mode descriptions, stale-request clearing, SDK tips, and binary body labeling across all supported locales.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

Poem

A bunny hopped through code both old and new,
Cross-platform bridges, tagged services too 🐰
Blobs get labeled, stale trips swept away,
TextComponents shine in each detail array,
Trees and JSON dance in a lazy-built view!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/network-url-and-body-handling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several performance optimizations and UI enhancements to the DevConnect tool, including lazy tab loading, asynchronous JSON parsing, an LRU cache for syntax highlighting, and a redesigned storage detail panel. It also adds backend service detection for network requests and a 'Clear stale' utility. The code review identified several critical issues: a potential header corruption bug in the React Native client when handling array-based headers, a double-unit formatting bug in the binary body size display, a memory leak from an unclosed Riverpod subscription in the storage viewer, and a Flutter anti-pattern involving state mutation inside a build method. Additionally, the reviewer recommended enhancing service action detection to support parsed JSON maps and replacing hardcoded English strings with existing localization templates.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +389 to +390
const headers = init.headers;
if (headers && typeof headers === 'object' && !(headers instanceof Headers)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The check typeof headers === 'object' evaluates to true for arrays. If headers is passed as an array of key-value pairs (e.g., string[][]), it will incorrectly enter this block and be wrapped in a Proxy designed for a flat record. This will corrupt the headers when they are spread or read.

Adding !Array.isArray(headers) ensures that array-based headers are correctly handled by the fallback block, which is already supported by readFinalHeaders.

Suggested change
const headers = init.headers;
if (headers && typeof headers === 'object' && !(headers instanceof Headers)) {
const headers = init.headers;
if (headers && typeof headers === 'object' && !Array.isArray(headers) && !(headers instanceof Headers)) {

Comment on lines +2651 to +2655
S.of(context).binaryBodySize(
AppConstants.formatBytes(sizeBytes),
sizeBytes,
),
style: TextStyle(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The translation template for binaryBodySize expects a raw number/string representing the size in KB (e.g., "{kb} KB ({bytes} bytes)"). Passing AppConstants.formatBytes(sizeBytes) (which already formats the value with a unit like '4.2 KB' or '512 B') will result in a double unit display (e.g., '4.2 KB KB' or '512 B KB').

Passing (sizeBytes / 1024).toStringAsFixed(1) fixes this formatting bug.

Suggested change
S.of(context).binaryBodySize(
AppConstants.formatBytes(sizeBytes),
sizeBytes,
),
style: TextStyle(
Text(
S.of(context).binaryBodySize(
(sizeBytes / 1024).toStringAsFixed(1),
sizeBytes,
),

Comment on lines +68 to +75
ref.listenManual<String?>(
selectedStorageIdProvider,
(_, _) {
_generation++;
setState(() {});
},
fireImmediately: false,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using ref.listenManual in initState without storing and closing the returned ProviderSubscription in dispose causes a memory leak. If the provider emits a change after this widget is unmounted, the callback will still run, call setState, and throw an exception.

Instead of manually managing the subscription lifecycle, use the standard ref.listen inside the build method. Riverpod will automatically manage and dispose of the subscription when the widget is unmounted.

Suggested change
ref.listenManual<String?>(
selectedStorageIdProvider,
(_, _) {
_generation++;
setState(() {});
},
fireImmediately: false,
);
// Selection changes are handled via ref.listen in build

Comment thread lib/app.dart
Comment on lines +89 to +94
final devices = ref.watch(connectedDevicesProvider);
final ids = devices.map((d) => d.deviceId).toSet();
if (_knownDeviceIds != null && ids.any((id) => !_knownDeviceIds!.contains(id))) {
HighlightCacheLifecycleObserver.instance.clearCache();
}
_knownDeviceIds = ids;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Mutating state variables (like _knownDeviceIds) and triggering side effects (like clearing a cache) directly inside the build method is a Flutter anti-pattern. It can lead to inconsistent state or unnecessary rebuilds.

Instead, use ref.listen to reactively listen to changes in connectedDevicesProvider and trigger the cache invalidation side-effect. This also allows you to completely remove the _knownDeviceIds state variable from the class.

Suggested change
final devices = ref.watch(connectedDevicesProvider);
final ids = devices.map((d) => d.deviceId).toSet();
if (_knownDeviceIds != null && ids.any((id) => !_knownDeviceIds!.contains(id))) {
HighlightCacheLifecycleObserver.instance.clearCache();
}
_knownDeviceIds = ids;
ref.listen(connectedDevicesProvider, (previous, next) {
if (previous != null) {
final prevIds = previous.map((d) => d.deviceId).toSet();
final nextIds = next.map((d) => d.deviceId).toSet();
if (nextIds.any((id) => !prevIds.contains(id))) {
HighlightCacheLifecycleObserver.instance.clearCache();
}
}
});

Comment on lines +61 to +64
if (body is String) {
final sigMatch = RegExp(r'"Action"\s*:\s*"([^"]+)"').firstMatch(body);
if (sigMatch != null) return sigMatch.group(1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The body parameter can be a parsed JSON payload (such as a Map or List) rather than a raw String. If body is a Map, the check body is String will fail, and the action name will not be extracted.

Adding a check for body is Map allows direct lookup of the Action or action keys, improving the reliability of service action detection.

  if (body is Map) {
    final action = body['Action'] ?? body['action'];
    if (action is String) return action;
  }
  if (body is String) {
    final sigMatch = RegExp(r'"Action"\s*:\s*"([^"]+)"').firstMatch(body);
    if (sigMatch != null) return sigMatch.group(1);
  }

Comment on lines +468 to +471
content: Text(
'Cleared $removed stale request${removed == 1 ? '' : 's'} '
'(pending > 10min)',
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This user-facing string is hardcoded. Since the localization key clearStaleSnackbar is already defined in the ARB files, it should be used here to support internationalization.

Suggested change
content: Text(
'Cleared $removed stale request${removed == 1 ? '' : 's'} '
'(pending > 10min)',
),
content: Text(
S.of(context).clearStaleSnackbar(removed),
),

Comment on lines +2166 to +2169
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: TextComponent(
'$type payload ($bytes bytes) — binary, cannot be inspected.\nIdentify the action via the X-Amz-Target header.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This screenshot helper uses hardcoded English strings. It should use the localized strings binaryBody, binaryBodySize, and binaryBodyHint to ensure consistency across all supported locales.

Suggested change
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: TextComponent(
'$type payload ($bytes bytes) — binary, cannot be inspected.\nIdentify the action via the X-Amz-Target header.',
final sizeLabel = (bytes / 1024).toStringAsFixed(1);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: TextComponent(
'${S.of(context).binaryBody(type)} (${S.of(context).binaryBodySize(sizeLabel, bytes)}) — binary, cannot be inspected.\\n${S.of(context).binaryBodyHint}',

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant