Skip to content

fix: hermes crash rettime#2705

Open
JavanPoirier wants to merge 4 commits intomswjs:mainfrom
JavanPoirier:fix/hermes-crash-rettime
Open

fix: hermes crash rettime#2705
JavanPoirier wants to merge 4 commits intomswjs:mainfrom
JavanPoirier:fix/hermes-crash-rettime

Conversation

@JavanPoirier
Copy link
Copy Markdown

@JavanPoirier JavanPoirier commented Apr 11, 2026

v2.13.2 breaks RN mocking. Maybe freeze the rettime dep, as it is susceptible to breaking changes?

On React Native with Hermes, polyfilled Event doesn't define stopPropagation on its prototype. MSW's WebSocketHandler wraps it with new Proxy(event.stopPropagation, ...), which throws TypeError: new Proxy target must be an Object when stopPropagation is undefined. This is the same class of bug that was fixed in [email protected].

Changes

  • src/core/handlers/WebSocketHandler.ts — Replace new Proxy(event.stopPropagation, { apply }) with a plain function wrapper using optional chaining:
// Before — crashes when stopPropagation is undefined
event.stopPropagation = new Proxy(event.stopPropagation, {
  apply: (target, thisArg, args) => { ... },
})

// After — safe on Hermes
const originalStopPropagation = event.stopPropagation
event.stopPropagation = function (...args) {
  Reflect.get(event, KOnStopPropagation)?.call(handler)
  return originalStopPropagation?.apply(this, args)
}
  • test/native/hermes-event-polyfill.native.test.ts — Regression test that stubs a minimal Hermes-like Event/MessageEvent (no stopPropagation on prototype) and verifies setupServer() from msw/native doesn't crash. Runs in the existing test:native vitest suite with no new dependencies.

  • src/core/handlers/WebSocketHandler.test.ts — Unit tests for createStopPropagationListener covering both the Hermes case (undefined stopPropagation) and standard Events.

Copilot AI added 2 commits April 11, 2026 09:56
[email protected] used `new Proxy(event.stopPropagation, ...)` which throws
on React Native with Hermes because the polyfilled Event does not define
stopPropagation as a function. [email protected] replaced the Proxy approach
with a simple function wrapper, resolving the crash.

Agent-Logs-Url: https://github.com/JavanPoirier/msw/sessions/30b4db7a-5277-4e4b-9b2a-08fe9e43c2b2
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 332bee5a-7cc4-44b3-a540-ccf41df452f2

📥 Commits

Reviewing files that changed from the base of the PR and between 55ea7c7 and 75fadb6.

📒 Files selected for processing (4)
  • src/core/experimental/define-network.ts
  • src/core/experimental/sources/network-source.ts
  • src/core/handlers/WebSocketHandler.test.ts
  • src/core/handlers/WebSocketHandler.ts
✅ Files skipped from review due to trivial changes (1)
  • src/core/handlers/WebSocketHandler.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/experimental/define-network.ts
  • src/core/experimental/sources/network-source.ts

📝 Walkthrough

Walkthrough

Bumps runtime dependency rettime; exports createStopPropagationListener and replaces Proxy-based stopPropagation patch with a captured-original function that invokes handler hook then original; adds Hermes-like event polyfill test; minor typing tweaks in experimental network sources.

Changes

Cohort / File(s) Summary
Dependency
package.json
Updated runtime dependency rettime from ^0.10.1 to ^0.11.7.
WebSocket handler
src/core/handlers/WebSocketHandler.ts
Made createStopPropagationListener exported (@internal). Replaced Proxy wrapper with captured originalStopPropagation reassignment; use reflective optional call for stopImmediatePropagation when propagation ownership differs; retained one-time patch guard and propagation ownership checks.
Handler tests
src/core/handlers/WebSocketHandler.test.ts
Imported createStopPropagationListener and added tests for Hermes-like missing stopPropagation and for standard Event behavior.
Hermes regression test
test/native/hermes-event-polyfill.native.test.ts
Added test that stubs global Event/MessageEvent to mimic Hermes (no stopPropagation) and ensures setupServer() does not throw.
Experimental network
src/core/experimental/define-network.ts, src/core/experimental/sources/network-source.ts
Typing adjustments: define-network uses AnyNetworkFrame for frame parameter and treats wildcard events as any; network-source.on listener type changed from Emitter.ListenerType to Emitter.Listener.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped into the eventful glen,
Reattached a thread, then hopped again.
No Proxy snags, a gentler song,
Hermes-safe — the tests belong.
Tiny cheers and carrot cake! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: hermes crash rettime' is highly related to the main objective of the PR, which fixes a crash on React Native with Hermes by replacing the Proxy-based wrapper in WebSocketHandler and updating the rettime dependency.
Description check ✅ Passed The description clearly explains the problem (Hermes Event polyfill lacks stopPropagation), the solution (replace Proxy with plain function wrapper), and lists all the files changed with their purposes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@JavanPoirier JavanPoirier changed the title Fix/hermes crash rettime fix: hermes crash rettime Apr 11, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/handlers/WebSocketHandler.ts (1)

197-199: ⚠️ Potential issue | 🟠 Major

Guard stopImmediatePropagation for Hermes-like events.

Line 198 can still throw when stopImmediatePropagation is missing (same class of polyfill issue this PR addresses), once propagation has already been marked by another handler.

Safe runtime-compatible fix
     if (propagationStoppedAt && handler.id !== propagationStoppedAt) {
-      event.stopImmediatePropagation()
+      Reflect.get(event, 'stopImmediatePropagation')?.call(event)
       return
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/handlers/WebSocketHandler.ts` around lines 197 - 199, The code calls
event.stopImmediatePropagation() without ensuring it exists, which can throw for
Hermes-like/polyfilled events; update the guard inside the propagation check
(the block using propagationStoppedAt and handler.id) to first verify that
event.stopImmediatePropagation is a function (e.g., typeof
event.stopImmediatePropagation === "function") before calling it so handlers
that mark propagation won't cause runtime errors in environments lacking that
method.
🧹 Nitpick comments (1)
src/core/handlers/WebSocketHandler.test.ts (1)

21-30: Strengthen this assertion to detect patch regressions.

On Line 29, callable event.stopPropagation is true even without patching. Consider also asserting the patched side effect (kPropagationStoppedAt) after invoking event.stopPropagation().

Suggested test tightening
   const listener = createStopPropagationListener(handler)
   listener(event)

-  // stopPropagation should be patched and still callable.
-  expect(() => event.stopPropagation()).not.toThrow()
+  expect(() => event.stopPropagation()).not.toThrow()
+  expect(Reflect.get(event, 'kPropagationStoppedAt')).toBe(handler.id)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/handlers/WebSocketHandler.test.ts` around lines 21 - 30, The test
currently only checks that event.stopPropagation() is callable, which can pass
without the patch; update the test using WebSocketHandler and
createStopPropagationListener so after calling event.stopPropagation() you also
assert the patched side-effect: the event has the kPropagationStoppedAt property
set (e.g., is defined and is a number or true) to verify the patch applied by
createStopPropagationListener; use the same Event instance and symbol name
kPropagationStoppedAt to locate and assert the added property.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/core/handlers/WebSocketHandler.ts`:
- Around line 197-199: The code calls event.stopImmediatePropagation() without
ensuring it exists, which can throw for Hermes-like/polyfilled events; update
the guard inside the propagation check (the block using propagationStoppedAt and
handler.id) to first verify that event.stopImmediatePropagation is a function
(e.g., typeof event.stopImmediatePropagation === "function") before calling it
so handlers that mark propagation won't cause runtime errors in environments
lacking that method.

---

Nitpick comments:
In `@src/core/handlers/WebSocketHandler.test.ts`:
- Around line 21-30: The test currently only checks that event.stopPropagation()
is callable, which can pass without the patch; update the test using
WebSocketHandler and createStopPropagationListener so after calling
event.stopPropagation() you also assert the patched side-effect: the event has
the kPropagationStoppedAt property set (e.g., is defined and is a number or
true) to verify the patch applied by createStopPropagationListener; use the same
Event instance and symbol name kPropagationStoppedAt to locate and assert the
added property.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5ba17112-f2ea-40ec-ba8b-a00949d90ed7

📥 Commits

Reviewing files that changed from the base of the PR and between 33bf349 and cf79863.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • package.json
  • src/core/handlers/WebSocketHandler.test.ts
  • src/core/handlers/WebSocketHandler.ts
  • test/native/hermes-event-polyfill.native.test.ts

@JavanPoirier JavanPoirier force-pushed the fix/hermes-crash-rettime branch from 55ea7c7 to cf79863 Compare April 11, 2026 14:53
@kettanaito
Copy link
Copy Markdown
Member

Hi, @JavanPoirier. Appreciate the detailed explanation of the issue. But I have a serious concern with the proposed solution. You are suggesting we tailor to a broken polyfill. There is nothing wrong relying on Event.prototype.stopPropagation to exist. A proper solution here is to open an issue for that polyfill and make them implement that method or at least create a dummy function like Node.js does if the implementation is inapplicable.

I am open to discussion on this, but at its current state, I cannot accept these suggestions.

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.

3 participants