Skip to content

feat(network-activity-plugin): Override and mock network responses#116

Merged
V3RON merged 10 commits intocallstackincubator:mainfrom
IanLuan:feat/overrides
Oct 23, 2025
Merged

feat(network-activity-plugin): Override and mock network responses#116
V3RON merged 10 commits intocallstackincubator:mainfrom
IanLuan:feat/overrides

Conversation

@IanLuan
Copy link
Copy Markdown
Contributor

@IanLuan IanLuan commented Oct 1, 2025

Override and mock network responses

This feature is inspired by Chrome DevTools overrides. It allows developers to edit the response and see how their app behaves, without changing the backend. Currently, it’s possible to override the response body and status.

With local overrides, you can unblock your workflow by prototyping and testing changes and fixes without waiting for the backend, third-parties, or APIs to support them.

Demo:

Override.network.compressed.mp4

How it works

The DevTools and the app both store the overrides. Every time a response is overridden, the DevTools send it to the app. The overrides list is persisted in the DevTools’ local storage, so it survives app reloads. On the DevTools side, it’s stored in Zustand store; in the app, it’s stored in a registry. If a request has an override, the response body and status are monkey-patched during XMLHttpRequest interception. On load, the DevTools send all overrides stored in local storage to the app, keeping the data in sync.


Note

Adds editable, persisted HTTP response overrides (status/body) in DevTools that sync to the app and are applied via XHR interception at runtime.

  • Core/Protocol:
    • Add RequestOverride type and set-overrides event in shared/client.
  • React Native (runtime):
    • New overrides-registry to store URL→override mappings.
    • Extend XHRInterceptor with setOverrideCallback and invoke before listeners.
    • network-inspector uses overrides to monkey‑patch XMLHttpRequest response/status and responseType based on Content-Type.
    • useNetworkActivityDevTools listens for set-overrides to update registry.
  • DevTools (store + wiring):
    • Zustand store now persists overrides (Map) to localStorage and exposes addOverride/clearOverride actions; sends set-overrides to app.
    • InspectorView sends current overrides on setup and when overrides change.
  • UI:
    • Add CodeEditor and OverrideResponse components for editing overrides.
    • ResponseTab integrates override workflow (edit/save/clear) and action slot via updated Section.
    • RequestList and SidePanel indicate when a request/response has an override.

Written by Cursor Bugbot for commit da64858. This will update automatically on new commits. Configure here.

@vercel
Copy link
Copy Markdown

vercel bot commented Oct 1, 2025

@IanLuan is attempting to deploy a commit to the Callstack Team on Vercel.

A member of the Team first needs to authorize it.

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's split the implementation into two components:
a) one for showing up real responses
b) one for showing up overrides

When user clicks 'override', the second component will be shown.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved override logic into a new component – 79695a6 . Let me know if you think I should change anything.

}),
{
name: 'rozenite-network-activity-storage',
storage: createJSONStorage(() => localStorage, {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's add some sort of version control system, so if we change the state in the future, we will have the option to purge the local state and replace it with the new one. A simple STORE_VERSION constant and a __version property should be sufficient. If there is a mismatch, we should ignore the persisted state.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the STORE_VERSION constant and applied it to the version attribute of zustand persist - 438db9b.

return;
}

request.addEventListener('readystatechange', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't handleRequestOverride already called in readystatechange? Should we verify that readyState has a specific value?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, I moved the overrideCallback invocation outside of event listener. It's on the same level as sendCallback (da64858)

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

request.status = override.status;
}
});
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Inefficient Handling and Incorrect JSON Parsing

The handleRequestOverride function's readystatechange listener applies overrides on every state change, causing inefficient, repeated property assignments and potential timing issues. Additionally, when responseType is 'json', request.response is incorrectly assigned a string instead of a parsed JSON object, which violates the XMLHttpRequest specification.

Fix in Cursor Fix in Web

value={editedStatus}
onChange={(e) => {
setEditedStatus(parseInt(e.target.value));
}}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Override Component Fails with Invalid Status Codes

The OverrideResponse component saves NaN as the override status if the status code input is empty or contains non-numeric characters. This occurs because parseInt returns NaN in these cases, which can lead to unexpected behavior when applying the network override.

Fix in Cursor Fix in Web

@V3RON V3RON merged commit 27bca40 into callstackincubator:main Oct 23, 2025
1 of 2 checks passed
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.

2 participants