Skip to content

feat: add event.stopPropagation#1835

Merged
Yradex merged 1 commit intolynx-family:mainfrom
f0rdream:event_propgation
Sep 29, 2025
Merged

feat: add event.stopPropagation#1835
Yradex merged 1 commit intolynx-family:mainfrom
f0rdream:event_propgation

Conversation

@f0rdream
Copy link
Copy Markdown
Collaborator

@f0rdream f0rdream commented Sep 25, 2025

Add event.stopPropagation and event.stopImmediatePropagation in MTS, to help with event propagation control

function App() {
  function handleInnerTap(event: MainThread.TouchEvent) {
    'main thread';
    event.stopPropagation();
    // Or stop immediate propagation with
    // event.stopImmediatePropagation();
  }

  // OuterTap will not be triggered
  return (
    <view main-thread:bindtap={handleOuterTap}>
      <view main-thread:bindtap={handleInnerTap}>
        <text>Hello, world</text>
      </view>
    </view>
  );
}

Summary by CodeRabbit

  • New Features

    • Event objects now support stopPropagation and stopImmediatePropagation and expose combined propagation result.
  • Bug Fixes

    • Removed an obsolete runtime warning related to stopPropagation handling.
  • Documentation

    • Added usage example and changelog guidance; note to update related SDK in lazy-loading setups.
  • Tests

    • Added tests validating propagation methods, event-result wrapping, and SDK gating.

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Sep 25, 2025

🦋 Changeset detected

Latest commit: eaf5401

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@lynx-js/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 25, 2025

📝 Walkthrough

Walkthrough

Adds event propagation support to the worklet runtime: new EventResult bitmasks and EventCtx, SDK-gated injection of stopPropagation/stopImmediatePropagation into event objects, runWorklet API extensions and return-shape changes, new tests, and removal of an SWC stopPropagation warning.

Changes

Cohort / File(s) Summary of changes
Changelog
.changeset/clear-crabs-swim.md
Adds a changeset documenting the new event propagation API with example and upgrade note.
SWC plugin compatibility
packages/react/transform/crates/swc_plugin_compat/lib.rs
Removed the branch that detected e.stopPropagation() and emitted a deprecation/BROKEN warning; other warnings remain.
Worklet runtime — types
packages/react/worklet-runtime/src/bindings/types.ts
Added EventCtx (_eventReturnResult?: number), RunWorkletSource enum (NONE, EVENT, GESTURE), and RunWorkletOptions (source: RunWorkletSource).
Worklet runtime — event utils
packages/react/worklet-runtime/src/eventPropagation.ts
New module: EventResult bitmasks, EventLike detection, isEventObject, and addEventMethodsIfNeeded which injects stopPropagation/stopImmediatePropagation and manipulates event bitmask.
Worklet runtime — runtime integration
packages/react/worklet-runtime/src/workletRuntime.ts
runWorklet/runWorkletImpl now accept options?: RunWorkletOptions, call addEventMethodsIfNeeded, and when event methods are present return { returnValue, eventReturnResult }.
Worklet runtime — tests
packages/react/worklet-runtime/__test__/eventPropagation.test.js, packages/react/worklet-runtime/__test__/workletRuntime.test.js
New tests verifying stopPropagation / stopImmediatePropagation, combined bitmask behavior, SDK gating, and return-value wrapping for event vs non-event payloads.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • hzy
  • Yradex
  • colinaaa

Poem

the rabbit taps a tiny bell, 🐇
I add two stops so handlers quell.
bits set, bits clear — the event replies,
worklets run and return their ties.
a changelog hop beneath blue skies.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely and accurately reflects a core new feature added in this PR: the introduction of an event.stopPropagation method. It is specific, concise, and directly tied to the changeset without extraneous detail, making it clear to a reviewer what primary functionality has been introduced.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Sep 25, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6180e11 and 4176c3a.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (1 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, always generate a changeset and commit the resulting markdown file(s)

Files:

  • .changeset/clear-crabs-swim.md
🧬 Code graph analysis (3)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (2)
  • ClosureValueType (28-39)
  • EventCtx (70-72)
packages/react/worklet-runtime/src/workletRuntime.ts (2)
packages/react/worklet-runtime/src/bindings/types.ts (1)
  • EventCtx (70-72)
packages/react/worklet-runtime/src/eventPropagation.ts (2)
  • isEventObject (16-25)
  • addEventPropagationMethods (31-45)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (2)
packages/react/worklet-runtime/src/api/lynxApi.ts (1)
  • initApiEnv (42-42)
packages/react/worklet-runtime/src/workletRuntime.ts (1)
  • initWorklet (191-191)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: test-rust / Test (Ubuntu)
🔇 Additional comments (5)
packages/react/worklet-runtime/src/bindings/types.ts (1)

70-72: Event context typing looks good.

The lightweight EventCtx carrier is exactly what we need for the propagation bitmask—nice addition.

.changeset/clear-crabs-swim.md (1)

1-25: Changelog entry reads well.

The changeset clearly explains the new propagation APIs and ships with a practical snippet—thanks.

packages/react/worklet-runtime/__test__/eventPropagation.test.js (1)

9-80: Great targeted coverage.

These focused assertions around the propagation bitmask give us confidence the new hooks work end-to-end. Nice.

packages/react/worklet-runtime/__test__/workletRuntime.test.js (1)

340-372: Thanks for verifying the new event surface.

Confirming both helper injection and wrapped return values here is super helpful.

packages/react/worklet-runtime/src/eventPropagation.ts (1)

8-44: Helper module looks solid.

The bitmask helpers and type guard cleanly encapsulate the propagation logic—nicely done.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Sep 25, 2025

CodSpeed Performance Report

Merging #1835 will not alter performance

Comparing f0rdream:event_propgation (eaf5401) with main (a35a245)

Summary

✅ 53 untouched
⏩ 3 skipped1

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci bot commented Sep 25, 2025

Web Explorer

#5585 Bundle Size — 366.09KiB (0%).

eaf5401(current) vs a35a245 main#5578(baseline)

Bundle metrics  Change 1 change
                 Current
#5585
     Baseline
#5578
No change  Initial JS 145.3KiB 145.3KiB
No change  Initial CSS 32KiB 32KiB
Change  Cache Invalidation 0% 47.65%
No change  Chunks 8 8
No change  Assets 8 8
Change  Modules 220(+0.46%) 219
No change  Duplicate Modules 16 16
No change  Duplicate Code 3.36% 3.36%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#5585
     Baseline
#5578
No change  JS 240.07KiB 240.07KiB
No change  Other 94.02KiB 94.02KiB
No change  CSS 32KiB 32KiB

Bundle analysis reportBranch f0rdream:event_propgationProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci bot commented Sep 25, 2025

React Example

#5589 Bundle Size — 237.56KiB (0%).

eaf5401(current) vs a35a245 main#5581(baseline)

Bundle metrics  no changes
                 Current
#5589
     Baseline
#5581
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 166 166
No change  Duplicate Modules 68 68
No change  Duplicate Code 46.82% 46.82%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#5589
     Baseline
#5581
No change  IMG 145.76KiB 145.76KiB
No change  Other 91.8KiB 91.8KiB

Bundle analysis reportBranch f0rdream:event_propgationProject dashboard


Generated by RelativeCIDocumentationReport issue

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 0

🧹 Nitpick comments (7)
packages/react/worklet-runtime/src/bindings/types.ts (1)

70-72: EventCtx is fine; consider documenting and minor naming tidy-up

  • Add a brief JSDoc explaining the bitmask semantics and when it’s populated (via event propagation methods).
  • Optional: drop the quotes around the property name for consistency with other interfaces.
.changeset/clear-crabs-swim.md (1)

5-25: Good changeset; call out SDK requirement and return wrapper behavior

Explicitly document:

  • Minimum Lynx SDK 3.5 for stopPropagation/stopImmediatePropagation.
  • When an event object is the first argument, worklets return { returnValue, __EventReturnResult } (wrapped), otherwise raw value.

Apply this edit for clarity:

 Add `event.stopPropagation` and `event.stopImmediatePropagation` in MTS, to help with event propagation control
+
+- Requires Lynx SDK >= 3.5.
+- When a worklet is invoked with an event object (first argument has `target` and `currentTarget`),
+  its return is wrapped as `{ returnValue, __EventReturnResult }`. Otherwise, the raw return value is used.

As per coding guidelines

packages/react/worklet-runtime/__test__/eventPropagation.test.js (2)

23-25: Use mockRestore instead of mockReset

Restore the original console.warn after tests to avoid leaking spies across suites.

-  afterAll(() => {
-    consoleMock.mockReset();
-  });
+  afterAll(() => {
+    consoleMock.mockRestore();
+  });

27-80: Prefer EventResult constants in assertions

Import and assert against EventResult to avoid magic numbers and keep tests resilient to future bit changes.

-import { initWorklet } from '../src/workletRuntime';
+import { initWorklet } from '../src/workletRuntime';
+import { EventResult } from '../src/eventPropagation';

-  expect(ret).toMatchObject({
-    returnValue: undefined,
-    __EventReturnResult: 1,
-  });
+  expect(ret).toMatchObject({
+    returnValue: undefined,
+    __EventReturnResult: EventResult.kStopPropagationMask,
+  });-  expect(ret).toMatchObject({
-    returnValue: undefined,
-    __EventReturnResult: 2,
-  });
+  expect(ret).toMatchObject({
+    returnValue: undefined,
+    __EventReturnResult: EventResult.kStopImmediatePropagationMask,
+  });-  expect(ret).toMatchObject({
-    returnValue: undefined,
-    __EventReturnResult: 0x1 | 0x2,
-  });
+  expect(ret).toMatchObject({
+    returnValue: undefined,
+    __EventReturnResult: EventResult.kStopPropagationMask | EventResult.kStopImmediatePropagationMask,
+  });
packages/react/worklet-runtime/src/eventPropagation.ts (3)

8-14: Expose a typed alias for the bitmask (optional)

Define and export a type to reflect valid mask values and aid consumers/tests.

 export const EventResult = {
   kDefault: 0x0,
   kStopPropagationMask: 0x1,
   kStopImmediatePropagationMask: 0x2,
 } as const;
+
+export type EventReturnResult = typeof EventResult[keyof typeof EventResult];

17-26: Tighten event detection (optional)

Guard against null and limit to own properties to avoid prototype pollution issues.

-export function isEventObject(ctx: ClosureValueType[]): ctx is [EventLike, ...ClosureValueType[]] {
-  if (!Array.isArray(ctx) || typeof ctx[0] !== 'object') {
+export function isEventObject(ctx: ClosureValueType[]): ctx is [EventLike, ...ClosureValueType[]] {
+  if (!Array.isArray(ctx) || ctx[0] == null || typeof ctx[0] !== 'object') {
     return false;
   }
-  const eventObj = ctx[0] as Record<string, ClosureValueType>;
-  if (eventObj && 'target' in eventObj && 'currentTarget' in eventObj) {
+  const eventObj = ctx[0] as Record<string, ClosureValueType>;
+  if (Object.prototype.hasOwnProperty.call(eventObj, 'target')
+    && Object.prototype.hasOwnProperty.call(eventObj, 'currentTarget')) {
     return true;
   }
   return false;
 }

32-52: Define non-enumerable methods and avoid overwriting preexisting ones

Prevents polluting event payloads during iteration and avoids clobbering existing API if present.

 export function addEventPropagationMethods(ctx: [EventLike, ...ClosureValueType[]], eventCtx: EventCtx): void {
   const eventObj = ctx[0];

-  // Add stopPropagation method
-  eventObj['stopPropagation'] = function() {
+  // Add stopPropagation method
+  if (!('stopPropagation' in eventObj)) {
+    Object.defineProperty(eventObj, 'stopPropagation', {
+      enumerable: false,
+      configurable: true,
+      writable: true,
+      value: function() {
     if (!isSdkVersionGt(3, 4)) {
       throw new Error('stopPropagation requires Lynx sdk version 3.5');
     }
     eventCtx._eventReturnResult = (eventCtx._eventReturnResult ?? EventResult.kDefault)
       | EventResult.kStopPropagationMask;
-  };
+      },
+    });
+  }

   // Add stopImmediatePropagation method
-  eventObj['stopImmediatePropagation'] = function() {
+  if (!('stopImmediatePropagation' in eventObj)) {
+    Object.defineProperty(eventObj, 'stopImmediatePropagation', {
+      enumerable: false,
+      configurable: true,
+      writable: true,
+      value: function() {
     if (!isSdkVersionGt(3, 4)) {
       throw new Error('stopImmediatePropagation requires Lynx sdk version 3.5');
     }
     eventCtx._eventReturnResult = (eventCtx._eventReturnResult ?? EventResult.kDefault)
       | EventResult.kStopImmediatePropagationMask;
-  };
+      },
+    });
+  }
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4176c3a and c868f8c.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (1 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/worklet-runtime/src/workletRuntime.ts
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, always generate a changeset and commit the resulting markdown file(s)

Files:

  • .changeset/clear-crabs-swim.md
🧬 Code graph analysis (2)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (2)
  • ClosureValueType (28-39)
  • EventCtx (70-72)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (1)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (3)
  • consoleMock (12-12)
  • fn (395-411)
  • fn (426-442)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: test-rust / Test (Ubuntu)
🔇 Additional comments (4)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (3)

340-353: LGTM: event methods presence check is correct

Asserts are appropriate for verifying augmentation.


354-372: LGTM: event return is wrapped for event-like input

Covers expected shape when no propagation flags are set.


374-386: LGTM: non-event input returns raw value

Validates the non-wrapped path.

packages/react/worklet-runtime/__test__/eventPropagation.test.js (1)

82-119: LGTM: version-gating error paths are verified

Error messages and gating behavior align with implementation.

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 4

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c868f8c and 5c9a4b1.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (1 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (5 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • .changeset/clear-crabs-swim.md
  • packages/react/worklet-runtime/src/eventPropagation.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (3)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (2)
  • fn (395-411)
  • fn (426-442)
packages/react/worklet-runtime/src/api/lynxApi.ts (1)
  • initApiEnv (42-42)
packages/react/worklet-runtime/src/workletRuntime.ts (1)
  • initWorklet (192-192)
packages/react/worklet-runtime/src/workletRuntime.ts (2)
packages/react/worklet-runtime/src/bindings/types.ts (4)
  • Worklet (41-52)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
  • EventCtx (70-72)
packages/react/worklet-runtime/src/eventPropagation.ts (2)
  • isEventObject (18-29)
  • addEventPropagationMethods (36-56)

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

🧹 Nitpick comments (9)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (5)

24-26: Restore the spy instead of just resetting it.

Use mockRestore() to avoid leaking the spied method across suites.

 afterAll(() => {
-  consoleMock.mockReset();
+  consoleMock.mockRestore();
 });

28-28: Rename test titles to the current field name.

Titles still say “__EventReturnResult”. Align with eventReturnResult.

-it('stopPropagation should have __EventReturnResult be 1', async () => {
+it('stopPropagation should have eventReturnResult be 1', async () => {

-it('stopImmediatePropagation should have __EventReturnResult be 2', async () => {
+it('stopImmediatePropagation should have eventReturnResult be 2', async () => {

-it('call stopPropagation and stopImmediatePropagation should have __EventReturnResult be 3', async () => {
+it('call stopPropagation and stopImmediatePropagation should have eventReturnResult be 3', async () => {

Also applies to: 48-48, 68-68


30-34: Remove no-op bind() calls inside the worklet fns.

_workletMap['1'].bind(this) return value is unused and has no effect.

-      globalThis.lynxWorkletImpl._workletMap['1'].bind(this);
 
       event.stopPropagation();
-      globalThis.lynxWorkletImpl._workletMap['1'].bind(this);
       event.stopImmediatePropagation();

Also applies to: 50-53, 70-74, 95-97, 115-118


4-9: Assert with exported bitmask constants to avoid magic numbers.

Import EventResult and use the flags in expectations.

 import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { initApiEnv } from '../src/api/lynxApi';
 import { RunWorkletSource } from '../src/bindings/types';
 import { initWorklet } from '../src/workletRuntime';
+import { EventResult } from '../src/eventPropagation';
@@
     expect(ret).toMatchObject({
       returnValue: undefined,
-      eventReturnResult: 1,
+      eventReturnResult: EventResult.kStopPropagationMask,
     });
@@
     expect(ret).toMatchObject({
       returnValue: undefined,
-      eventReturnResult: 2,
+      eventReturnResult: EventResult.kStopImmediatePropagationMask,
     });
@@
     expect(ret).toMatchObject({
       returnValue: undefined,
-      eventReturnResult: 0x1 | 0x2,
+      eventReturnResult:
+        EventResult.kStopPropagationMask | EventResult.kStopImmediatePropagationMask,
     });

Also applies to: 42-45, 62-65, 83-86


87-88: Optional: Add a baseline test for “no calls ⇒ 0”.

This will assert default behavior. Requires runtime to coalesce undefined to 0 (see runtime comment).

   });
 
+  it('no propagation calls should have eventReturnResult be 0', async () => {
+    initWorklet();
+    const fn = vi.fn(function(event) {
+      // intentionally do nothing
+    });
+    globalThis.registerWorklet('main-thread', '1', fn);
+    const ret = globalThis.runWorklet(
+      { _wkltId: '1' },
+      [{ target: {}, currentTarget: {} }],
+      { source: RunWorkletSource.EVENT },
+    );
+    expect(ret).toMatchObject({
+      returnValue: undefined,
+      eventReturnResult: 0,
+    });
+  });
packages/react/worklet-runtime/src/workletRuntime.ts (2)

76-81: Compute event detection once and default eventReturnResult to 0.

Prevents inconsistencies if the event object is mutated during handler execution and guarantees a numeric return for event-sourced invocations.

-  const eventCtx: EventCtx = {};
-
-  if (isEventObject(params_, options)) {
-    addEventPropagationMethods(params_, eventCtx);
-  }
+  const eventCtx: EventCtx = {};
+  const isEvent = isEventObject(params_, options);
+  if (isEvent) {
+    // Narrow to `[event, ...rest]` for augmentation; object shape is not relied upon further.
+    addEventPropagationMethods(
+      params_ as [Record<string, ClosureValueType>, ...ClosureValueType[]],
+      eventCtx,
+    );
+  }
   const result = profile('runWorklet', () => worklet(...params_));
 
-  const eventReturnResult = eventCtx._eventReturnResult;
+  const eventReturnResult = eventCtx._eventReturnResult ?? 0;
 
-  // Check if any event object has _eventResult set and include it in return
-  if (isEventObject(params_, options)) {
+  // For event-sourced invocations, always include the propagation bitmask.
+  if (isEvent) {
     return {
       returnValue: result,
-      eventReturnResult: eventReturnResult,
+      eventReturnResult,
     };
   }

Also applies to: 83-91


85-86: Comment drift.

The comment mentions “include if set,” but code now always returns eventReturnResult for event runs. Update comment or condition for clarity.

-  // Check if any event object has _eventResult set and include it in return
+  // For event-sourced invocations, include the propagation bitmask in the return value.
packages/react/worklet-runtime/src/eventPropagation.ts (2)

22-29: Tighten and simplify the type guard.

Minor simplification; keeps semantics identical.

-  if (!Array.isArray(ctx) || typeof ctx[0] !== 'object' || ctx[0] === null) {
-    return false;
-  }
-  if (options && options.source === RunWorkletSource.EVENT) {
-    return true;
-  }
-  return false;
+  return (
+    Array.isArray(ctx) &&
+    typeof ctx[0] === 'object' &&
+    ctx[0] !== null &&
+    options?.source === RunWorkletSource.EVENT
+  );

39-56: Define propagation methods as non-enumerable.

Prevents polluting user iteration over event fields and avoids accidental serialization of helper functions.

-  // Add stopPropagation method
-  eventObj['stopPropagation'] = function() {
-    if (!isSdkVersionGt(3, 4)) {
-      throw new Error('stopPropagation requires Lynx sdk version 3.5');
-    }
-    eventCtx._eventReturnResult = (eventCtx._eventReturnResult ?? EventResult.kDefault)
-      | EventResult.kStopPropagationMask;
-  };
+  // Add stopPropagation method
+  Object.defineProperty(eventObj, 'stopPropagation', {
+    value: function stopPropagation() {
+      if (!isSdkVersionGt(3, 4)) {
+        throw new Error('stopPropagation requires Lynx sdk version 3.5');
+      }
+      eventCtx._eventReturnResult =
+        (eventCtx._eventReturnResult ?? EventResult.kDefault) | EventResult.kStopPropagationMask;
+    },
+    enumerable: false,
+    configurable: true,
+  });
@@
-  // Add stopImmediatePropagation method
-  eventObj['stopImmediatePropagation'] = function() {
-    if (!isSdkVersionGt(3, 4)) {
-      throw new Error('stopImmediatePropagation requires Lynx sdk version 3.5');
-    }
-    eventCtx._eventReturnResult = (eventCtx._eventReturnResult ?? EventResult.kDefault)
-      | EventResult.kStopImmediatePropagationMask;
-  };
+  // Add stopImmediatePropagation method
+  Object.defineProperty(eventObj, 'stopImmediatePropagation', {
+    value: function stopImmediatePropagation() {
+      if (!isSdkVersionGt(3, 4)) {
+        throw new Error('stopImmediatePropagation requires Lynx sdk version 3.5');
+      }
+      eventCtx._eventReturnResult =
+        (eventCtx._eventReturnResult ?? EventResult.kDefault) |
+        EventResult.kStopImmediatePropagationMask;
+    },
+    enumerable: false,
+    configurable: true,
+  });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c9a4b1 and b6e8860.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (2 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (5 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/react/worklet-runtime/test/workletRuntime.test.js
  • packages/react/worklet-runtime/src/bindings/types.ts
  • .changeset/clear-crabs-swim.md
🧰 Additional context used
🧬 Code graph analysis (2)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (3)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
  • EventCtx (70-72)
packages/react/worklet-runtime/src/workletRuntime.ts (2)
packages/react/worklet-runtime/src/bindings/types.ts (4)
  • Worklet (41-52)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
  • EventCtx (70-72)
packages/react/worklet-runtime/src/eventPropagation.ts (2)
  • isEventObject (18-29)
  • addEventPropagationMethods (36-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: zizmor
  • GitHub Check: test-rust / Test (Ubuntu)
🔇 Additional comments (2)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (2)

36-41: Good fix: event-source gating is correctly passed to runWorklet.

Passing { source: RunWorkletSource.EVENT } ensures propagation helpers are attached.


42-45: Assertion key updated to eventReturnResult — LGTM.

Matches the new runtime return shape.

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/react/worklet-runtime/src/workletRuntime.ts (1)

59-63: Propagate RunWorkletOptions through the legacy queue

The _lepusWorkletHash branch still enqueues with delayExecUntilJsReady but drops the new options. When that delayed callback runs, runWorkletImpl receives undefined, so addEventMethodsIfNeeded never tags the event and the stopPropagation bitmask is lost. Any main-thread worklet executed before JS hydration (the whole point of this queue) will therefore ignore the new propagation controls. Please thread the options through the delay path (pass them here and teach delayWorkletEvent.ts to store/forward them) so the queued execution sees RunWorkletSource.EVENT.

-    delayExecUntilJsReady(ctx._lepusWorkletHash, params);
+    delayExecUntilJsReady(ctx._lepusWorkletHash, params, options);
🧹 Nitpick comments (1)
.changeset/clear-crabs-swim.md (1)

18-23: Define handleOuterTap in the snippet

The example references handleOuterTap, but it isn’t defined anywhere in the block. When readers paste this into their project (or docs tooling renders it with type checking), they’ll hit an undefined identifier. Please add a minimal stub (e.g., function handleOuterTap() { 'main thread'; }) to keep the sample self-contained.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6e8860 and 435b449.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (2 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (5 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/worklet-runtime/test/workletRuntime.test.js
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, always generate a changeset and commit the resulting markdown file(s)

Files:

  • .changeset/clear-crabs-swim.md
🧬 Code graph analysis (3)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (3)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
  • EventCtx (70-72)
packages/react/worklet-runtime/src/workletRuntime.ts (2)
packages/react/worklet-runtime/src/bindings/types.ts (3)
  • Worklet (41-52)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
  • addEventMethodsIfNeeded (37-63)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (3)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (1)
  • consoleMock (13-13)
packages/react/worklet-runtime/src/api/lynxApi.ts (1)
  • initApiEnv (42-42)
packages/react/worklet-runtime/src/workletRuntime.ts (1)
  • initWorklet (186-186)

@f0rdream f0rdream force-pushed the event_propgation branch 2 times, most recently from 0c624a4 to e449aae Compare September 28, 2025 08:51
Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 0

🧹 Nitpick comments (7)
.changeset/clear-crabs-swim.md (2)

7-25: Complete the example by defining handleOuterTap; fix minor comment casing.

Add a no-op outer handler so the snippet is self-contained, and use “Outer tap” (lowercase) for readability.

 function App() {
+  function handleOuterTap(event: MainThread.TouchEvent) {
+    'main thread';
+    // outer handler won't run when inner handler stops propagation
+  }
   function handleInnerTap(event: MainThread.TouchEvent) {
     'main thread';
     event.stopPropagation();
     // Or stop immediate propagation with
     // event.stopImmediatePropagation();
   }

-  // OuterTap will not be triggered
+  // Outer tap will not be triggered
   return (
     <view main-thread:bindtap={handleOuterTap}>
       <view main-thread:bindtap={handleInnerTap}>
         <text>Hello, world</text>
       </view>
     </view>
   );
 }

27-27: Specify the minimum version in the note.

Replace “latest” with the first released version containing this feature (e.g., “@lynx-js/react >= X.Y.Z”) once known.

packages/react/worklet-runtime/__test__/eventPropagation.test.js (2)

28-28: Update test titles to match the current return key.

Reflect eventReturnResult in descriptions.

-it('stopPropagation should have __EventReturnResult be 1', async () => {
+it('stopPropagation should have eventReturnResult be 1', async () => {
-it('stopImmediatePropagation should have __EventReturnResult be 2', async () => {
+it('stopImmediatePropagation should have eventReturnResult be 2', async () => {
-it('call stopPropagation and stopImmediatePropagation should have __EventReturnResult be 3', async () => {
+it('calling both should have eventReturnResult be 3', async () => {

Also applies to: 48-48, 68-68


30-34: Remove no-op bind() lines for clarity.

These bind() calls return a function that’s never used; they add noise without affecting behavior.

-    const fn = vi.fn(function(event) {
-      globalThis.lynxWorkletImpl._workletMap['1'].bind(this);
-
-      event.stopPropagation();
-    });
+    const fn = vi.fn(function(event) {
+      event.stopPropagation();
+    });
-    const fn = vi.fn(function(event) {
-      globalThis.lynxWorkletImpl._workletMap['1'].bind(this);
-      event.stopImmediatePropagation();
-    });
+    const fn = vi.fn(function(event) {
+      event.stopImmediatePropagation();
+    });
-    const fn = vi.fn(function(event) {
-      globalThis.lynxWorkletImpl._workletMap['1'].bind(this);
-      event.stopImmediatePropagation();
-      event.stopPropagation();
-    });
+    const fn = vi.fn(function(event) {
+      event.stopImmediatePropagation();
+      event.stopPropagation();
+    });

Also applies to: 50-53, 70-74

packages/react/worklet-runtime/src/workletRuntime.ts (3)

60-63: Consider forwarding options through the legacy delay path.

Even if the legacy _lepusWorkletHash branch isn’t used today, threading options keeps APIs consistent and future-proof.

-    delayExecUntilJsReady(ctx._lepusWorkletHash, params);
+    delayExecUntilJsReady(ctx._lepusWorkletHash, params, options);

Additionally (outside this diff), update delayWorkletEvent.ts to accept and pass options?: RunWorkletOptions through to runWorkletImpl(...).


80-85: Default eventReturnResult to 0 for a stable numeric type.

Prevents undefined when no propagation method is called, simplifying consumers.

     return {
       returnValue: result,
-      eventReturnResult: eventCtx._eventReturnResult,
+      eventReturnResult: eventCtx._eventReturnResult ?? 0,
     };

52-55: Document the return shape change in JSDoc.

Clarify that event invocations return { returnValue, eventReturnResult }, otherwise the raw result.

  * @param params worklet params.
- * @param options run worklet options.
+ * @param options run worklet options. When `options.source === RunWorkletSource.EVENT`,
+ *   the return value is `{ returnValue, eventReturnResult }`; otherwise it is the worklet’s raw return.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c624a4 and e449aae.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (2 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (5 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/react/worklet-runtime/test/workletRuntime.test.js
  • packages/react/worklet-runtime/src/bindings/types.ts
  • packages/react/worklet-runtime/src/eventPropagation.ts
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, always generate a changeset and commit the resulting markdown file(s)

Files:

  • .changeset/clear-crabs-swim.md
🧠 Learnings (2)
📚 Learning: 2025-09-28T08:46:43.167Z
Learnt from: f0rdream
PR: lynx-family/lynx-stack#1835
File: packages/react/worklet-runtime/src/workletRuntime.ts:52-55
Timestamp: 2025-09-28T08:46:43.167Z
Learning: The legacy worklet path with `_lepusWorkletHash` in `packages/react/worklet-runtime/src/workletRuntime.ts` is preserved for compatibility with MTS (Mini-app Threading Service) that doesn't support Initial Frame Rendering. This path will not be touched in current implementations.

Applied to files:

  • packages/react/worklet-runtime/src/workletRuntime.ts
📚 Learning: 2025-08-11T05:57:18.212Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1305
File: packages/testing-library/testing-environment/src/index.ts:255-258
Timestamp: 2025-08-11T05:57:18.212Z
Learning: In the ReactLynx testing environment (`packages/testing-library/testing-environment/src/index.ts`), the dual assignment pattern `target.console.method = console.method = () => {}` is required for rstest compatibility. This is because rstest provides `console` in an IIFE (Immediately Invoked Function Expression), and both the target and global console need to have these methods defined for proper test execution.

Applied to files:

  • packages/react/worklet-runtime/__test__/eventPropagation.test.js
🧬 Code graph analysis (2)
packages/react/worklet-runtime/src/workletRuntime.ts (2)
packages/react/worklet-runtime/src/bindings/types.ts (3)
  • Worklet (41-52)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
  • addEventMethodsIfNeeded (36-56)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (3)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (1)
  • consoleMock (13-13)
packages/react/worklet-runtime/src/api/lynxApi.ts (1)
  • initApiEnv (42-42)
packages/react/worklet-runtime/src/workletRuntime.ts (1)
  • initWorklet (186-186)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: test-rust / clippy
  • GitHub Check: test-rust / Test (Ubuntu)
  • GitHub Check: zizmor
🔇 Additional comments (1)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (1)

24-26: Restore the original console after the suite.

Use mockRestore() to avoid leaking the mocked console into other tests.

   afterAll(() => {
-    consoleMock.mockReset();
+    consoleMock.mockRestore();
   });

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

🧹 Nitpick comments (7)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (1)

341-355: Strengthen assertions for event helpers

Also assert they’re functions (not just defined) to catch shape regressions.

Apply this diff:

-      expect(event.stopPropagation).toBeDefined();
-      expect(event.stopImmediatePropagation).toBeDefined();
+      expect(typeof event.stopPropagation).toBe('function');
+      expect(typeof event.stopImmediatePropagation).toBe('function');
packages/react/worklet-runtime/__test__/eventPropagation.test.js (4)

6-9: Assert with named bitmasks instead of magic numbers

Import EventResult and use its masks in expectations for clarity and durability.

Apply this diff:

 import { initApiEnv } from '../src/api/lynxApi';
 import { RunWorkletSource } from '../src/bindings/types';
 import { initWorklet } from '../src/workletRuntime';
+import { EventResult } from '../src/eventPropagation';

28-46: Rename test to reflect eventReturnResult and use constants

Update the title (old __EventReturnResult wording) and assert via EventResult.kStopPropagationMask.

Apply this diff:

-  it('stopPropagation should have __EventReturnResult be 1', async () => {
+  it('stopPropagation should set eventReturnResult to kStopPropagationMask', async () => {
@@
-    expect(ret).toMatchObject({
-      returnValue: undefined,
-      eventReturnResult: 1,
-    });
+    expect(ret).toMatchObject({
+      returnValue: undefined,
+      eventReturnResult: EventResult.kStopPropagationMask,
+    });

48-66: Likewise for stopImmediatePropagation

Use the named mask and fix the title.

Apply this diff:

-  it('stopImmediatePropagation should have __EventReturnResult be 2', async () => {
+  it('stopImmediatePropagation should set eventReturnResult to kStopImmediatePropagationMask', async () => {
@@
-    expect(ret).toMatchObject({
-      returnValue: undefined,
-      eventReturnResult: 2,
-    });
+    expect(ret).toMatchObject({
+      returnValue: undefined,
+      eventReturnResult: EventResult.kStopImmediatePropagationMask,
+    });

68-88: Combine masks via constants in the “both-called” case

Prefer constants over 0x1 | 0x2 and refresh the title.

Apply this diff:

-  it('call stopPropagation and stopImmediatePropagation should have __EventReturnResult be 3', async () => {
+  it('calling both should OR eventReturnResult masks', async () => {
@@
-    expect(ret).toMatchObject({
-      returnValue: undefined,
-      eventReturnResult: 0x1 | 0x2,
-    });
+    expect(ret).toMatchObject({
+      returnValue: undefined,
+      eventReturnResult:
+        EventResult.kStopPropagationMask | EventResult.kStopImmediatePropagationMask,
+    });
packages/react/worklet-runtime/src/eventPropagation.ts (1)

43-53: Optional: make helper methods non‑enumerable

To align with DOM Event semantics and avoid polluting object iteration, define properties as non‑enumerable.

Apply this diff:

-  eventObj['stopPropagation'] = stopPropagation as unknown as ClosureValueType;
+  Object.defineProperty(eventObj, 'stopPropagation', {
+    value: stopPropagation,
+    enumerable: false,
+  });
@@
-  eventObj['stopImmediatePropagation'] =
-    stopImmediatePropagation as unknown as ClosureValueType;
+  Object.defineProperty(eventObj, 'stopImmediatePropagation', {
+    value: stopImmediatePropagation,
+    enumerable: false,
+  });
packages/react/worklet-runtime/src/workletRuntime.ts (1)

54-64: Legacy lepus path drops options (FYI)

The _lepusWorkletHash branch doesn’t forward options, so EVENT helpers wouldn’t attach if this path were used. If this path truly isn’t touched in current MTS flows, no action needed; otherwise consider threading options through delayExecUntilJsReady.

Based on learnings

-    delayExecUntilJsReady(ctx._lepusWorkletHash, params);
+    // If legacy path is exercised, forward options as well.
+    delayExecUntilJsReady(ctx._lepusWorkletHash, params, options);

Note: would also require updating delayWorkletEvent.ts to carry options to runWorkletImpl.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e449aae and eaf5401.

📒 Files selected for processing (7)
  • .changeset/clear-crabs-swim.md (1 hunks)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs (0 hunks)
  • packages/react/worklet-runtime/__test__/eventPropagation.test.js (1 hunks)
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js (2 hunks)
  • packages/react/worklet-runtime/src/bindings/types.ts (1 hunks)
  • packages/react/worklet-runtime/src/eventPropagation.ts (1 hunks)
  • packages/react/worklet-runtime/src/workletRuntime.ts (5 hunks)
💤 Files with no reviewable changes (1)
  • packages/react/transform/crates/swc_plugin_compat/lib.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react/worklet-runtime/src/bindings/types.ts
  • .changeset/clear-crabs-swim.md
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-28T08:46:43.167Z
Learnt from: f0rdream
PR: lynx-family/lynx-stack#1835
File: packages/react/worklet-runtime/src/workletRuntime.ts:52-55
Timestamp: 2025-09-28T08:46:43.167Z
Learning: The legacy worklet path with `_lepusWorkletHash` in `packages/react/worklet-runtime/src/workletRuntime.ts` is preserved for compatibility with MTS (Mini-app Threading Service) that doesn't support Initial Frame Rendering. This path will not be touched in current implementations.

Applied to files:

  • packages/react/worklet-runtime/src/workletRuntime.ts
  • packages/react/worklet-runtime/__test__/workletRuntime.test.js
📚 Learning: 2025-08-11T05:57:18.212Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1305
File: packages/testing-library/testing-environment/src/index.ts:255-258
Timestamp: 2025-08-11T05:57:18.212Z
Learning: In the ReactLynx testing environment (`packages/testing-library/testing-environment/src/index.ts`), the dual assignment pattern `target.console.method = console.method = () => {}` is required for rstest compatibility. This is because rstest provides `console` in an IIFE (Immediately Invoked Function Expression), and both the target and global console need to have these methods defined for proper test execution.

Applied to files:

  • packages/react/worklet-runtime/__test__/eventPropagation.test.js
🧬 Code graph analysis (3)
packages/react/worklet-runtime/src/workletRuntime.ts (3)
packages/react/worklet-runtime/src/bindings/types.ts (3)
  • Worklet (41-52)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
  • addEventMethodsIfNeeded (36-56)
packages/react/worklet-runtime/src/utils/profile.ts (1)
  • profile (4-20)
packages/react/worklet-runtime/__test__/eventPropagation.test.js (3)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (1)
  • consoleMock (13-13)
packages/react/worklet-runtime/src/api/lynxApi.ts (1)
  • initApiEnv (42-42)
packages/react/worklet-runtime/src/workletRuntime.ts (1)
  • initWorklet (186-186)
packages/react/worklet-runtime/src/eventPropagation.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (3)
  • ClosureValueType (28-39)
  • RunWorkletOptions (80-82)
  • EventCtx (70-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: test-rust / Test (Ubuntu)
  • GitHub Check: zizmor
🔇 Additional comments (5)
packages/react/worklet-runtime/__test__/workletRuntime.test.js (3)

8-8: Correct import for event-mode tests

Importing RunWorkletSource here is necessary for the EVENT path exercised below. Looks good.


357-377: Return wrapper assertions read correctly

Asserting { returnValue, eventReturnResult } on EVENT invocations matches the runtime contract.


379-391: Non‑event path returns raw value

Good coverage that non‑EVENT calls don’t wrap the return.

packages/react/worklet-runtime/__test__/eventPropagation.test.js (1)

24-26: Restore the spied console after the suite

Use mockRestore() so other suites aren’t affected. (Prior feedback.)

Apply this diff:

-  afterAll(() => {
-    consoleMock.mockReset();
-  });
+  afterAll(() => {
+    consoleMock.mockRestore();
+  });
packages/react/worklet-runtime/src/workletRuntime.ts (1)

76-87: Event return shape integration looks correct

Capturing hasEventMethods once and returning { returnValue, eventReturnResult } only in event mode matches the API and avoids double detection.

@f0rdream f0rdream requested review from Yradex, gaoachao and hzy September 28, 2025 09:30
@Yradex Yradex merged commit 3202c88 into lynx-family:main Sep 29, 2025
50 checks passed
gaoachao added a commit that referenced this pull request Sep 30, 2025
Regression of #1835
See
#1835 (comment)
for more detail
<!--
  Thank you for submitting a pull request!

We appreciate the time and effort you have invested in making these
changes. Please ensure that you provide enough information to allow
others to review your pull request.

Upon submission, your pull request will be automatically assigned with
reviewers.

If you want to learn more about contributing to this project, please
visit:
https://github.com/lynx-family/lynx-stack/blob/main/CONTRIBUTING.md.
-->

<!-- The AI summary below will be auto-generated - feel free to replace
it with your own. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Removed the stop-propagation compatibility diagnostic test and its
expected warning outputs for webpack and rspack.
* Deleted the related sample component and build configuration files
used solely by this test.

* **Chores**
  * Added a placeholder changeset entry.

No user-facing features, behavior, or API changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

## Checklist

<!--- Check and mark with an "x" -->

- [ ] Tests updated (or not required).
- [ ] Documentation updated (or not required).
- [ ] Changeset added, and when a BREAKING CHANGE occurs, it needs to be
clearly marked (or not required).
colinaaa pushed a commit that referenced this pull request Oct 1, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @lynx-js/react@0.114.1

### Patch Changes

- Add `event.stopPropagation` and `event.stopImmediatePropagation` in
MTS, to help with event propagation control
([#1835](#1835))

    ```tsx
    function App() {
      function handleInnerTap(event: MainThread.TouchEvent) {
        "main thread";
        event.stopPropagation();
        // Or stop immediate propagation with
        // event.stopImmediatePropagation();
      }

      // OuterTap will not be triggered
      return (
        <view main-thread:bindtap={handleOuterTap}>
          <view main-thread:bindtap={handleInnerTap}>
            <text>Hello, world</text>
          </view>
        </view>
      );
    }
    ```

Note, if this feature is used in [Lazy Loading Standalone
Project](https://lynxjs.org/react/code-splitting.html#lazy-loading-standalone-project),
both the Producer and the Consumer should update to latest version of
`@lynx-js/react` to make sure the feature is available.

- Fix the "ReferenceError: Node is not defined" error.
([#1850](#1850))

This error would happen when upgrading to `@testing-library/jest-dom`
[v6.9.0](https://github.com/testing-library/jest-dom/releases/tag/v6.9.0).

- fix: optimize main thread event error message
([#1838](#1838))

## @lynx-js/rspeedy@0.11.5

### Patch Changes

- Bump Rsbuild v1.5.13 with Rspack v1.5.8.
([#1849](#1849))

## @lynx-js/react-rsbuild-plugin@0.11.1

### Patch Changes

- Updated dependencies
\[[`19f823a`](19f823a)]:
    -   @lynx-js/css-extract-webpack-plugin@0.6.4
    -   @lynx-js/react-alias-rsbuild-plugin@0.11.1
    -   @lynx-js/use-sync-external-store@1.5.0
    -   @lynx-js/react-refresh-webpack-plugin@0.3.4
    -   @lynx-js/react-webpack-plugin@0.7.1

## @lynx-js/testing-environment@0.1.8

### Patch Changes

- Fix the "ReferenceError: Node is not defined" error.
([#1850](#1850))

This error would happen when upgrading to `@testing-library/jest-dom`
[v6.9.0](https://github.com/testing-library/jest-dom/releases/tag/v6.9.0).

## @lynx-js/web-constants@0.17.2

### Patch Changes

- feat: support load bts chunk from remote address
([#1834](#1834))

    -   re-support chunk splitting
    -   support lynx.requireModule with a json file
- support lynx.requireModule, lynx.requireModuleAsync with a remote url
- support to add a breakpoint in chrome after reloading the web page

-   Updated dependencies \[]:
    -   @lynx-js/web-worker-rpc@0.17.2

## @lynx-js/web-core@0.17.2

### Patch Changes

- feat: support load bts chunk from remote address
([#1834](#1834))

    -   re-support chunk splitting
    -   support lynx.requireModule with a json file
- support lynx.requireModule, lynx.requireModuleAsync with a remote url
- support to add a breakpoint in chrome after reloading the web page

- Updated dependencies
\[[`a35a245`](a35a245)]:
    -   @lynx-js/web-worker-runtime@0.17.2
    -   @lynx-js/web-constants@0.17.2
    -   @lynx-js/web-mainthread-apis@0.17.2
    -   @lynx-js/web-worker-rpc@0.17.2

## @lynx-js/web-mainthread-apis@0.17.2

### Patch Changes

- Updated dependencies
\[[`a35a245`](a35a245)]:
    -   @lynx-js/web-constants@0.17.2
    -   @lynx-js/web-style-transformer@0.17.2

## @lynx-js/web-worker-runtime@0.17.2

### Patch Changes

- feat: support load bts chunk from remote address
([#1834](#1834))

    -   re-support chunk splitting
    -   support lynx.requireModule with a json file
- support lynx.requireModule, lynx.requireModuleAsync with a remote url
- support to add a breakpoint in chrome after reloading the web page

- Updated dependencies
\[[`a35a245`](a35a245)]:
    -   @lynx-js/web-constants@0.17.2
    -   @lynx-js/web-mainthread-apis@0.17.2
    -   @lynx-js/web-worker-rpc@0.17.2

## @lynx-js/css-extract-webpack-plugin@0.6.4

### Patch Changes

- Avoid generating `.css.hot-update.json` when HMR is disabled.
([#1811](#1811))

## create-rspeedy@0.11.5



## @lynx-js/react-alias-rsbuild-plugin@0.11.1



## upgrade-rspeedy@0.11.5



## @lynx-js/web-core-server@0.17.2



## @lynx-js/web-rsbuild-server-middleware@0.17.2



## @lynx-js/web-style-transformer@0.17.2



## @lynx-js/web-worker-rpc@0.17.2

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants