Skip to content

Respect package.json exports in js-api typegen pipeline - #1

Draft
huntie wants to merge 561 commits into
mainfrom
cursor/fix-js-api-exports-resolution-db59
Draft

Respect package.json exports in js-api typegen pipeline#1
huntie wants to merge 561 commits into
mainfrom
cursor/fix-js-api-exports-resolution-db59

Conversation

@huntie

@huntie huntie commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Summary:

Teach yarn build-types to honor react-native package.json "exports" so runtime-only subpaths ("types": null, e.g. react-private-interface) do not widen the generated type graph, and so @microsoft/api-extractor receives an exports map in its temp package.json.

This is a prerequisite for react#57940, which failed build_js_types after exposing ReactNativeFeatureFlags through react-private-interface (pulling ReactNativeFeatureFlagsBase.d.ts into types_generated and breaking test-generated-typescript).

Changes:

  • Resolve react-native/* imports via resolve.exports in simpleResolve
  • Skip expanding src/private/* deps from "types": null export entry points; strip those re-exports from generated .d.ts
  • Copy rewritten exports into the api-extractor temp package.json
  • Drop obsolete react-native-strict-api customConditions from the api-extractor tsconfig

Changelog:

[INTERNAL] [FIXED] - Respect package.json exports boundaries in the js-api typegen pipeline

Test Plan:

cipolleschi and others added 30 commits July 23, 2026 18:10
Summary:
Bump Hermes V1 to `260318099.0.1` and update the RNTester CocoaPods lockfile to resolve the corresponding prebuilt Hermes pod.

## Changelog:

[INTERNAL] [CHANGED] - Bump Hermes V1 to 260318099.0.1.

Pull Request resolved: react#57647

Test Plan:
- `git diff --check`
- Verified the remote commit contains only the Hermes version property and RNTester `Podfile.lock` updates.

Reviewed By: GijsWeterings

Differential Revision: D113396501

Pulled By: cipolleschi

fbshipit-source-id: 9a688c08a32bec8e4daa9deb85e0e61a13668410
Summary:
Chronos Job Instance ID: 1125908309148932
Sandcastle Job Instance ID: 63050395308596005

Processed xml files:
android_res/com/facebook/content/strings/res/values/strings.xml
android_res/com/facebook/common/util/res/values/strings.xml
android_res/com/facebook/common/i18n/res/values/strings.xml
android_res/com/facebook/common/timeformat/res/values/strings.xml
android_res/rendercore/res/values/strings.xml
../xplat/messengervr/msys/feature_aggregation/src/i18n/res/values/strings.xml
android_res/com/oculus/socialplatform/msys/res/values/strings.xml
android_res/com/facebook/reportaproblem/res/values/strings.xml
android_res/com/meta/eclipse/res/values/strings.xml
android_res/com/facebook/nativetemplates/res/values/strings.xml
android_res/com/facebook/resources/res/values/strings.xml
android_res/com/facebook/common/strings/external/res/values/strings.xml
android_res/com/facebook/common/strings/res/values/strings.xml
android_res/com/facebook/widget/res/values/strings.xml
android_res/com/facebook/fbui/widget/pagerindicator/strings/res/values/strings.xml
android_res/com/facebook/fbui/widget/contentview/strings/res/values/strings.xml
android_res/com/facebook/iorg/common/upsell/res/values/strings.xml
android_res/com/facebook/iorg/common/res/values/strings.xml
android_res/com/facebook/dialtone/res/values/strings.xml
android_res/com/facebook/zero/messenger/semi/res/values/strings.xml
android_res/com/facebook/zero/res/values/strings.xml
android_res/com/facebook/zero/common/res/values/strings.xml
android_res/com/facebook/widget/facepile/res/values/strings.xml
android_res/com/facebook/tabbar/res/values/strings.xml
android_res/com/facebook/ui/toolbar/res/values/strings.xml
android_res/com/facebook/dialtone/messenger/res/values/strings.xml
android_res/com/bloks/foa/cds/bottomsheet/strings/res/values/strings.xml
android_res/com/facebook/horizon/res/values/strings.xml
../xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/res/views/uimanager/values/strings.xml
../xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/res/systeminfo/values/strings.xml
android_res/com/facebook/ui/emoji/res/values/strings.xml
android_res/com/facebook/ui/emoji/common/res/values/strings.xml
android_res/com/facebook/feedback/reactions/res/values/strings.xml
android_res/com/facebook/ufiservices/res/values/strings.xml
android_res/com/facebook/ui/edithistory/res/values/strings.xml
android_res/com/facebook/pages/common/userinviter/res/values/strings.xml
android_res/com/facebook/pages/common/bannedusers/res/values/strings.xml
android_res/com/facebook/friending/common/res/values/strings.xml
android_res/com/facebook/messaging/ui/stickerstore/res/values/strings.xml
android_res/com/facebook/stickers/res/values/strings.xml
android_res/com/facebook/messaging/shared/res/values/strings.xml
android_res/com/facebook/caspian/res/values/strings.xml
android_res/com/facebook/timeline/widget/actionbar/res/values/strings.xml
android_res/com/facebook/showpages/res/values/strings.xml
android_res/com/facebook/nux/res/values/strings.xml
android_res/com/facebook/facecast/common/badge/res/values/strings.xml
android_res/com/facebook/feedbase/res/values/strings.xml
android_res/com/facebook/feedback/ui/res/values/strings.xml
android_res/com/facebook/video/player/res/values/strings.xml
android_res/com/facebook/spherical/res/values/strings.xml
android_res/com/facebook/saved/common/res/values/strings.xml
android_res/com/facebook/video/comments/res/values/strings.xml
android_res/com/facebook/messaging/media/picker/res/values/strings.xml
android_res/com/facebook/messaging/permissions/res/values/strings.xml
android_res/com/facebook/messaging/media/res/values/strings.xml
android_res/com/facebook/messaging/res/values/strings.xml
android_res/com/facebook/ui/media/contentsearch/res/values/strings.xml
android_res/com/facebook/transliteration/res/values/strings.xml
android_res/com/facebook/bookmark/res/values/strings.xml
android_res/com/facebook/orca/res/values/strings.xml
android_res/com/facebook/workshared/userstatus/donotdisturb/res/values/strings.xml
android_res/com/facebook/widget/tokenizedtypeahead/res/values/strings.xml
android_res/com/facebook/widget/refreshableview/res/values/strings.xml
android_res/com/facebook/rtc/common/res/values/strings.xml
android_res/com/facebook/payments/ui/res/values/strings.xml
android_res/com/facebook/pages/app/clicktomessengerads/messagesuggestion/ui/res/values/strings.xml
android_res/com/facebook/messagingneue/res/values/strings.xml
android_res/com/facebook/messaging/widget/toolbar/res/values/strings.xml
android_res/com/facebook/messaging/users/username/res/values/strings.xml
android_res/com/facebook/messaging/tincan/messenger/res/values/strings.xml
android_res/com/facebook/messaging/threadview/quickpromotion/res/values/strings.xml
android_res/com/facebook/messaging/threadview/message/res/values/strings.xml
android_res/com/facebook/messaging/threadview/games/res/values/strings.xml
android_res/com/facebook/messaging/xma/res/values/strings.xml
android_res/com/facebook/messaging/threadview/admin/res/values/strings.xml
android_res/com/facebook/messaging/reactions/res/values/strings.xml
android_res/com/facebook/messaging/threadview/attachment/video/res/values/strings.xml
android_res/com/facebook/messaging/settings/res/values/strings.xml
android_res/com/facebook/messaging/searchnullstate/res/values/strings.xml
android_res/com/facebook/messenger/login/res/values/strings.xml
android_res/com/facebook/messaging/promotion/res/values/strings.xml
android_res/com/facebook/messaging/presence/res/values/strings.xml
android_res/com/facebook/messaging/polling/adminmessage/res/values/strings.xml
android_res/com/facebook/messaging/photos/view/res/values/strings.xml
android_res/com/facebook/messaging/omnipicker/res/values/strings.xml
android_res/com/facebook/messaging/livelocation/shared/res/values/strings.xml
android_res/com/facebook/messaging/invites/res/values/strings.xml
android_res/com/facebook/messaging/groups/threadactions/res/values/strings.xml
android_res/com/facebook/messaging/groups/admin/res/values/strings.xml
android_res/com/facebook/messaging/extensions/res/values/strings.xml
android_res/com/facebook/messaging/ephemeral/res/values/strings.xml
android_res/com/facebook/messaging/emoji/res/values/strings.xml
android_res/com/facebook/messaging/customthreads/res/values/strings.xml
android_res/com/facebook/messaging/contacts/picker/res/values/strings.xml
android_res/com/facebook/messaging/search/picker/res/values/strings.xml
android_res/com/facebook/contacts/res/values/strings.xml
android_res/com/facebook/messaging/connectivity/res/values/strings.xml
android_res/com/facebook/messaging/composer/tooltips/res/values/strings.xml
android_res/com/facebook/messaging/composer/platformmenu/res/values/strings.xml
android_res/com/facebook/messaging/composer/botcomposer/quickreply/res/values/strings.xml

allow-large-files
ignore-conflict-markers
opt-out-review
drop-conflicts

Differential Revision: D113502124

fbshipit-source-id: 33b9ed9fbf8ca5bb2e000df8b273886cea33848a
…7649)

Summary:
Pull Request resolved: react#57649

Use overflow state to set IMPORTANT_FOR_INTERACTION_DONT_CLIP_DESCENDANTS

Changelog: [Internal]

Reviewed By: fabriziocucci

Differential Revision: D112356198

fbshipit-source-id: 360ba1cdbf32d3c9595a798a200334d3ff8c1062
…ster

Summary:
Chronos Job Instance ID: 1125908309152665
Sandcastle Job Instance ID: 49539596432298597
allow-large-files
ignore-conflict-markers
opt-out-review
drop-conflicts

Differential Revision: D113510786

fbshipit-source-id: e0f5e88bb9c0ef851a6c74d2f07225cf4fabf4b1
…ster

Summary:
Chronos Job Instance ID: 1125908309159793
Sandcastle Job Instance ID: 2821145614
allow-large-files
ignore-conflict-markers
opt-out-review
drop-conflicts

Differential Revision: D113541639

fbshipit-source-id: 7f81f18a6280762143e4c9f6c3e791061727a404
…renderer/mounting/MountingTransaction.cpp (react#57585)

Summary: Pull Request resolved: react#57585

Reviewed By: cortinico

Differential Revision: D112079573

fbshipit-source-id: 28d43599a390de83f30647156affb84af2af3ab8
react#57658)

Summary:
Pull Request resolved: react#57658

Changelog: [Internal]

Reviewed By: fabriziocucci, javache

Differential Revision: D113045366

fbshipit-source-id: c3121e93ee2c8eee18dc3c1440017747204d4275
Summary:
Pull Request resolved: react#57657

Changelog: [INTERNAL]

RCTTiming is NOT used in the RN new architecture: react#57081

And will be deleted in: react#57037

This change removes it from the binary

Reviewed By: javache

Differential Revision: D113474826

fbshipit-source-id: fb57d797ba299f48d5affac2b540388018c85291
Summary:
Pull Request resolved: react#57665

Constructing a `ReactViewGroup` now reads the `syncAndroidClipBoundsWithOverflow` feature flag: the `pointerEvents` setter calls `ImportantForInteractionHelper.setImportantForInteraction`, which reads that flag, and `initView()` sets `pointerEvents`. Reading a feature flag loads the native `react_featureflagsjni` library, which is not on the Robolectric library path for this test, so `BaseViewManagerDelegateTest.setUp()` fails with:

```
java.lang.UnsatisfiedLinkError: no react_featureflagsjni
```

Install the local (non-native) feature-flags accessor in `setUp()` via `ReactNativeFeatureFlagsForTests.setUp()` and reset it in `tearDown()`, matching the approach already used in `ImportantForInteractionHelperTest`.

Changelog: [Internal]

Reviewed By: fabriziocucci, javache

Differential Revision: D113543605

fbshipit-source-id: 7e31aecfd2771bf22bc5457fa62491a83bb86d52
…ster

Summary:
Chronos Job Instance ID: 1125908309167076
Sandcastle Job Instance ID: 67553994940648609
allow-large-files
ignore-conflict-markers
opt-out-review
drop-conflicts

Differential Revision: D113583838

fbshipit-source-id: f3748ef59d932decdc004be9ac0c4580b11e7c8b
Summary:
Pull Request resolved: react#57656

Changelog: [Internal]

View event handlers can run synchronously before NativeAnimated nodes created on the async path have been flushed into the active graph. Flush pending async-created nodes on the render thread before evaluating view-driven animation events, so focus-driven animations can resolve their value nodes on the first native focus event instead of needing another runloop/event.

Reviewed By: zeyap

Differential Revision: D111257551

fbshipit-source-id: d5b7322cfce90b820ccf06422f1c830255bea3b3
Summary:
Fixes this warning that will break soon due to AGP9:

```
> Task :packages:react-native:ReactAndroid:generateDebugResources
Nested resources detected.
+ src/main/res
-- src/main/res/devsupport
-- src/main/res/shell
-- src/main/res/views/alert
-- src/main/res/views/modal
-- src/main/res/views/uimanager
-- src/main/res/views/view

Nested resources means you have layout like this:
res.srcDirs = [
    'src/main/res/',
    'src/main/res/category1'
]
However, you should use following structure instead:
res.srcDirs = [packages:rn-tester:android:app:de
    'src/main/res/common',
    'src/main/res/category1',
    'src/main/res/category2'
]
This Warning will be transformed into Error in version VERSION_9_0
```

## Changelog:

[INTERNAL] -

Pull Request resolved: react#57637

Test Plan: CI

Reviewed By: javache

Differential Revision: D113546520

Pulled By: cortinico

fbshipit-source-id: 5ccad2642b3e777a5a14fa64ff9128a9862416c2
Summary:
Currently codegen doesn't pass the platform file extension. This makes it possible to use `NativeModule.ios.ts` and will generate obj-c++ spec files.

Root cause: `generateSchemaInfos` called `libraries.map(generateSchemaInfo)`, so `Array.map` passed the array index as the `platform` argument (0, 1, 2, ...) instead of the real platform. That value flows into `combineSchemasInFileList`, which uses it to resolve platform-specific sources, so `.ios.ts` extensions never resolved. This threads the real platform from the per-platform loop in `execute()` through to `generateSchemaInfo`.

Changelog: [General] [Fixed] - Codegen: pass the target platform when generating schemas so platform-specific sources (e.g. `NativeModule.ios.ts`) resolve correctly

Pull Request resolved: react#57189

Test Plan: Added a regression test in `generate-artifacts-executor-test.js`: it mocks `combineSchemasInFileList` and asserts `generateSchemaInfos(libraries, 'ios')` forwards `'ios'` (not the array index) for every library. `arc lint` clean; the jest suite runs in CI.

Reviewed By: christophpurrer

Differential Revision: D113573175

Pulled By: fabriziocucci

fbshipit-source-id: 251a22d5cde07fbb76fab1abb10f4fcbcc5c9302
…t#57652)

Summary:
Pull Request resolved: react#57652

Migrate the `codegen` and `spm` command wrappers (Community CLI specific) out of `react-native.config.js` and into the `react-native/community-cli-plugin` package, alongside the existing `bundleCommand` and `startCommand`.

This continues moving command definitions into the plugin so `react-native.config.js` becomes a thin routing layer, as the file's own comment already calls for.

**Changes**

- Add `src/commands/spm/index.js` and `src/commands/codegen/index.js`, each defining and default-exporting its command (unchanged `name`, `description`, and `options`) plus an exported args type — mirroring the `bundle`/`start` command structure.
- Export `spmCommand` and `codegenCommand` from `src/index.flow.js`.
- Drop the inline `spmCommand` and `codegenCommand` definitions from `react-native.config.js`.
- Add docs to README.

Changelog: [Internal]

Reviewed By: robhogan

Differential Revision: D113412987

fbshipit-source-id: a39a7ef208ee970f2b7f9d4c38a86c39752db09a
Summary:
Pull Request resolved: react#57654

Reorganise these modules under a `src/dev-server/` directory (out of `src/(commands|utils)/`, more clearly identifying this unit of the codebase.

No public API change: the package still exports `startCommand` unchanged.

Renames inside this directory:

- `middleware.js` → `loadCommunityMiddleware.js`
- `runServer.js` → `runDevServer.js`

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D113412985

fbshipit-source-id: 9e27c7a02f057a37d702d5f13e55271244573d35
Summary:
Pull Request resolved: react#57653

Actions TODO comment dependant on react-native-community/cli#2605 (landed).

This drops our local dependency on `react-native-community/cli-server-api` Flow types, as well as avoiding this transient dependency in end user projects.

**Other changes**

- Promote `debug` log to a user-facing `warn` — several features in RN will break/degrade in this case and we want to flag explicitly.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D113412986

fbshipit-source-id: a47c9cfea53a3f77259fd6c5fdbf48d953616377
Summary:
Pull Request resolved: react#57689

[changelog](https://github.com/facebook/flow/blob/main/Changelog.md)
Changelog: [Internal]

Reviewed By: SamChou19815

Differential Revision: D113605655

fbshipit-source-id: 800e8f92d943a8f32d9a6aeff75ff9bfa6527827
…entrancy (react#57667)

Summary:
`IntentModule.onHostResume()` iterates `pendingOpenURLPromises` (an `ArrayList`) directly and calls `getInitialURL()` for each pending promise. When `getCurrentActivity()` returns `null` at that moment — e.g. a deep link or notification tap landing mid activity-transition during a rapid pause/resume — `getInitialURL()` re-enters `waitForActivityAndGetInitialURL()`, which calls `pendingOpenURLPromises.add(promise)` on the very list being iterated. The next iteration then throws `java.util.ConcurrentModificationException`:

```
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
    at java.util.ArrayList$Itr.next(ArrayList.java:967)
    at com.facebook.react.modules.intent.IntentModule$waitForActivityAndGetInitialURL$1.onHostResume(IntentModule.kt:90)
```

The `synchronized(this@IntentModule)` guard does not prevent this: the re-entrancy is on the same thread, which already holds the (reentrant) lock, so no second lock acquisition happens. The crash is the `ArrayList` iterator's `modCount` check, not a cross-thread race.

The fix snapshots the pending promises into a local copy, clears the shared list, and nulls the listener **before** draining. Re-queued promises then land in the now-empty `pendingOpenURLPromises` and register a fresh listener for the next resume, instead of mutating the list being iterated. Behaviour is otherwise unchanged.

## Changelog:

[ANDROID] [FIXED] - Fix ConcurrentModificationException when getInitialURL re-enters during onHostResume

Pull Request resolved: react#57667

Test Plan:
Added `IntentModuleTest.getInitialURL_onHostResumeWithNullActivity_doesNotThrowAndPreservesPromise`, a Robolectric regression test that registers a pending promise while the current activity is `null`, then drives `onHostResume` so the drain re-queues, and asserts the drain does not throw and the promise is preserved (neither resolved nor rejected).

Ran locally against `main` with the exact reproduction scenario:

**With the fix** — passes:
```
$ ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest \
    --tests "com.facebook.react.modules.intent.IntentModuleTest"
BUILD SUCCESSFUL
# tests=1, failures=0, errors=0
```

**Without the fix** (reverting `IntentModule.kt` only) — fails with the exact crash, confirming the test is a genuine regression test:
```
IntentModuleTest > getInitialURL_onHostResumeWithNullActivity_doesNotThrowAndPreservesPromise FAILED
    java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
        at java.util.ArrayList$Itr.next(ArrayList.java:967)
        at com.facebook.react.modules.intent.IntentModule$waitForActivityAndGetInitialURL$1.onHostResume(IntentModule.kt:90)
```

`ktfmt` (Meta style, matching the repo's `ktfmt` configuration) reports both changed files as already formatted.

Reviewed By: cortinico

Differential Revision: D113595327

Pulled By: fabriziocucci

fbshipit-source-id: b1247cb6e473fd7c550dda8b2b584d720ea3e46c
…Headers resolves <hermes/...> (react#57661)

Summary:
The iOS prebuild workflow (`.github/workflows/prebuild-ios-core.yml`) splits into two jobs on separate runners:

- **`build-rn-slice`** stages the hermes-ios headers during setup (`.build/artifacts/hermes/destroot/include/hermes`), but uploads only `.build/headers` + the SPM Products — **not** the hermes artifact.
- **`compose-xcframework`** (fresh runner) runs the compose (`-c` → `buildXCFrameworks`), which computed the hermes include path from `.build/artifacts/hermes/destroot/include`. That directory never exists on the compose runner, so `hermesHeaders` resolved to `null` and the hermes-header fold in `headers-compose.js` was **silently skipped**.

Net effect: the published `ReactNativeHeaders.xcframework` shipped without the `hermes/` namespace, so consumers (e.g. Expo prebuilt) couldn't resolve `<hermes/...>`. Because the value was `null` rather than a bad path, not even the existing warning fired — every nightly regressed silently.

## Fix

Build-time staging, matching the artifact's self-contained design (the orphaned consumer-side sidecar + health check in `download-spm-artifacts.js` confirm the bake was intended to happen at compose time):

- **Workflow:** `compose-xcframework` now re-stages the hermes-ios headers before composing — a `Set Hermes version` step (`$GITHUB_ENV` doesn't cross jobs) and a `Stage Hermes headers` step that extracts the tarball into `.build/artifacts/hermes`. Both are guarded by the same `cache-hit` condition as the sibling steps.
- **Fail-closed guard:** the inline hermes resolution is extracted into an exported `resolveHermesHeaders(buildFolder, required)` with a `findFirst` fallback (mirroring the consumer-side stager). When a version-stamped CI cut can't find the headers it now **throws** instead of silently shipping without `hermes/`. Gated behind a new `--require-hermes` flag, which the workflow passes only when `version-type` is set — so local `-c` keeps the previous no-fold behavior.

## Changelog:

[IOS] [FIXED] - Prebuilt `ReactNativeHeaders.xcframework` now ships the Hermes public headers so consumers resolve `<hermes/...>` out of the box

Pull Request resolved: react#57661

Test Plan:
- New unit tests for `resolveHermesHeaders` (`__tests__/xcframework-test.js`): resolves at the standard path, resolves via the `findFirst` fallback, returns `null` when absent + not required, throws when absent + required.
- `yarn jest packages/react-native/scripts/ios-prebuild/__tests__/xcframework-test.js --no-cache -i` → 4/4 pass (red before the resolver was exported).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed By: javache

Differential Revision: D113554720

Pulled By: cipolleschi

fbshipit-source-id: 30d21240ae97978e58a95ffdfad4088ed85536d4
Summary:
The scripts under `packages/react-native/scripts/spm/` are executed as plain `node` (via `setup-apple-spm.js` during the SwiftPM Xcode build), with **no Babel** to strip Flow. They use Flow-in-comment syntax (`/*: T */`) throughout so they parse un-transpiled.

A handful of uninitialized `let X: T;` declarations had slipped in with **raw** Flow annotations. Node parses the whole file on `require`, so each one throws `SyntaxError: Unexpected token ':'` at load time — taking the entire module down before it can run.

The jest suites didn't catch it because jest runs these files through `react-native/babel-preset`, which strips raw and comment-form annotations alike. Only the bare-`node` shipped path (SwiftPM setup) hits the error.

## Fix

Convert each offending declaration to Flow-comment form. Prettier's `flow` parser only keeps a comment type attached to the binding when the declaration is **initialized**, so each site gets a type-appropriate (inert) initializer — every variable is reassigned in the immediately-following `try`/branch before any use:

| file | lines | form |
|---|---|---|
| `autolinking-plugins.js` | 120, 178 | `/*: unknown */ = undefined` |
| `download-spm-artifacts.js` | 946, 973 | `/*: string */ = ''` |
| `download-spm-artifacts.js` | 1352 | `/*: {...} */ = {}` |
| `generate-spm-autolinking.js` | 429 | `/*: Array<...> */ = []` |
| `generate-spm-xcodeproj.js` | 1802 | `/*: string */ = ''` |
| `generate-spm-xcodeproj.js` | 1808 | `/*: unknown */ = undefined` |
| `generate-spm-xcodeproj.js` | 1863 | `/*: Array<...> */ = []` |
| `scaffold-package-swift.js` | 200, 1129 | `/*: Array<...> */ = []` |
| `scaffold-package-swift.js` | 933 | annotation dropped — Flow infers `PodspecModel` from `readPodspec()` |

## Changelog:
[INTERNAL] [FIXED] - Fix raw Flow type annotations that broke bare-node execution of the SwiftPM setup scripts

Pull Request resolved: react#57660

Test Plan:
- `node --check` passes on all 14 `scripts/spm/*.js` (previously `autolinking-plugins.js` and `download-spm-artifacts.js` threw `SyntaxError` at parse time).
- `flow focus-check` reports **0 errors** in the touched files.
- Prettier: clean.
- SPM jest suites: green.

## Changelog:

[INTERNAL] [FIXED] - Fix raw Flow type annotations that broke bare-node execution of the SwiftPM setup scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed By: cortinico

Differential Revision: D113554595

Pulled By: cipolleschi

fbshipit-source-id: b7fa29dd54266b749e1671333fb0530f6b6a8aab
Summary:
The SwiftPM autolinking flow hardcodes `react-native-community/cli config` to generate `autolinking.json` (`generate-spm-autolinking-config.js`). Apps that replace community autolinking — most notably **Expo**, which ships `expo-modules-autolinking` instead of `react-native-community/cli` — had no way to override that command, and any failure was swallowed. The result: the config command fails, `autolinking.json` is never written, the `Autolinked` SwiftPM package comes out empty, and `import Expo` (and every Expo module) fails to resolve — surfacing much later as an inscrutable `unable to resolve module dependency: 'Expo'`.

CocoaPods already solves the injection half: `use_native_modules!(config_command = $default_command)` accepts the command as a parameter, so an Expo `Podfile` passes `expo-modules-autolinking react-native-config` in place of the `rncli` default. This PR adds the equivalent hook to the SwiftPM path **and** closes the silent-failure trap.

### 1. Allow overriding the config command

`generateAutolinkingConfig` already accepted a `configCommand` option internally; it was just never reachable. Two ways to supply it, mirroring the CocoaPods hook:

- **`--config-command '<json>'`** — CLI flag taking a JSON array of the argv.
- **`RCT_SPM_AUTOLINKING_CONFIG_COMMAND`** — env var in the same JSON-array format. This is the vehicle for the injected Xcode build phase, which usually can't rewrite the script's argv but can read env.

Both go through one `parseConfigCommandJson` validator (rejects non-JSON, non-arrays, empty arrays, and non-string / empty-string elements, with a `source`-named error). Precedence: **`--config-command` > `RCT_SPM_AUTOLINKING_CONFIG_COMMAND` > default** (local `rncli` → `npx --no-install` fallback, unchanged). JSON (rather than whitespace-splitting) because real commands contain dashed flags and quoted script strings.

The value is the **command to execute** — its stdout is captured as the config JSON and written verbatim — exactly matching CocoaPods, not a precomputed result. An Expo app feeds the same argv it already builds for `use_native_modules!`:

```jsonc
RCT_SPM_AUTOLINKING_CONFIG_COMMAND='["node","--no-warnings","--eval","require('expo/bin/autolinking')","expo-modules-autolinking","react-native-config","--json","--platform","ios","--source-dir","/abs/path"]'
```

(Use `--platform ios`: the generator requires `project.ios.sourceDir` and everything downstream is iOS-only.)

### 2. Fail closed when the config command errors

Previously `main()` swallowed a config-command failure as a warning and continued, which is what let the empty package be produced silently. That policy is now extracted into `generateAutolinkingConfigOrFailClosed`: on a config-command error (non-zero exit, unparseable output, or a config missing `project.ios.sourceDir`) it logs an actionable message naming `RCT_SPM_AUTOLINKING_CONFIG_COMMAND` / `--config-command`, sets `process.exitCode = 2` (a hard Xcode build-phase error, matching the existing `RemoteVersionError` path), and stops.

The guard is deliberately narrow: a **genuinely native-module-free app never reaches the error path** — its command exits 0 with valid, empty-dependency JSON, so the generator returns normally and the legitimate empty-package path stays valid. Only an *erroring* command fails the build.

## Changelog:

[IOS] [ADDED] - Allow overriding the SwiftPM autolinking config command via `--config-command` / `RCT_SPM_AUTOLINKING_CONFIG_COMMAND`
[IOS] [CHANGED] - Fail closed with an actionable error when the SwiftPM autolinking config command fails, instead of silently emitting an empty Autolinked package

Pull Request resolved: react#57662

Test Plan:
New unit tests, developed red → green:

- `generate-spm-autolinking-config-test.js` — env var honored; explicit `configCommand` beats env; invalid-JSON and invalid-shape (`[]`, `[1,2]`) throw with the source name; env unset falls back to the default command; env state saved/restored per test.
- `setup-apple-spm-test.js` — `parseArgs` parses `--config-command` into an argv array, defaults to `null` when omitted, and throws on an invalid value; `generateAutolinkingConfigOrFailClosed` returns the result on success (exit code untouched), passes `projectRoot`/`configCommand` through, and on a config-command error returns `null`, sets exit 2, and logs an actionable error that names the env var and preserves the underlying cause.

```
$ yarn jest --no-cache -i \
    packages/react-native/scripts/spm/__tests__/generate-spm-autolinking-config-test.js \
    packages/react-native/scripts/spm/__tests__/setup-apple-spm-test.js
Test Suites: 2 passed, 2 total
Tests:       39 passed, 39 total
```

`prettier` and `eslint` clean on all changed files.

Reviewed By: zeyap

Differential Revision: D113554857

Pulled By: cipolleschi

fbshipit-source-id: d0baeeefc91aed144cf9e405e198531ff88088a7
…eact#57606)

Summary:
Adds a generator-time gate so a Swift-C++-interop-hostile C++ type can never silently ship in the prebuilt iOS core headers again.

**The class this guards:** the prebuilt core headers ship as real clang modules. A C++ value type reachable from a shipped module whose *implicit copy constructor is declared but ill-formed on instantiation* — e.g. an implicit-copy struct with a `std::vector<move-only-T>` member — compiles fine in C++ but hard-errors when a Swift target with `-cxx-interoperability-mode=default` imports the module and its generated bridging uses the type as a copyable value. This is invisible to C++ CI (plain C++ never instantiates the copy), so it only surfaced as a broken community-library nightly (react-native-unistyles, via `TraceRecordingState` / `HostTracingProfile` — fixed in react#57605).

**The gate:** a fourth compile gate (stage 3d) in `headers-verify.js`'s `runCompileGates`, compiling a Swift TU with `-cxx-interoperability-mode=default` that imports every module declared by the composed `ReactNativeHeaders` module map (parsed at runtime, not hardcoded), plus a probe that forces the ClangImporter copyability path for a small explicit list of value types (`SWIFT_CXX_INTEROP_PROBE_TYPES`). Because a bare `import` does not eagerly instantiate copy constructors on the current toolchain, the probe reproduces what a real interop consumer's generated code does: for each listed type it conditionally instantiates a copy, which fails exactly as the real consumer fails if the type regresses to an implicit copy. Adding future coverage is a one-line list edit.

One module (`React_RCTAppDelegate`) is excluded as non-importable under interop by design (ObjC bootstrap module with C++-only factory conformances); the inspector C++ graph it would reach is covered directly by the probe instead. The exclusion is documented in-source, and new exclusions are required to carry a justification.

Independent review confirmed the probe reproduces the real failure for the right reason (the dangerous shape reports `is_copy_constructible_v == true`, so the probe enters the branch and forces the ill-formed body instantiation — mirroring ClangImporter, not masking the bug), and that both offender types are covered.

⚠️ **Land after react#57605 — the gate runs against the composed artifacts, which are built from source; until the move-only type fixes are in, the gate correctly fails the prebuild on the unfixed types.

## Changelog:

[INTERNAL] [ADDED] - Prebuild gate that fails composed iOS headers unusable from a Swift C++-interop consumer

Pull Request resolved: react#57606

Test Plan:
Against a freshly composed Debug artifact (`node scripts/ios-prebuild -c -f Debug`):

- **Green**: `node scripts/ios-prebuild/headers-verify.js --flavor Debug` passes the new gate with both probe types (`TraceRecordingState`, `HostTracingProfile`) fixed in the artifact.
- **Red, TraceRecordingState**: restoring the unfixed `TraceRecordingState.h` into the composed artifact fails the gate with the exact `__construct_at` / implicit-copy diagnostic; restoring the fix → green.
- **Red, HostTracingProfile** (proves coverage isn't accidental): same protocol with `HostTracingProfile` unfixed (TraceRecordingState left fixed) → gate fails naming `HostTracingProfile` and its probe specialization; restore → green.
- `node --check`, `prettier --check`, `flow focus-check` clean. Gate is skipped by `--skip-compile` like the other compile gates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed By: cortinico

Differential Revision: D113011048

Pulled By: cipolleschi

fbshipit-source-id: 51cfd9fde67c60bce9d13e490fe966ed059432c2
…y interops are removed (react#57670)

Summary:
Pull Request resolved: react#57670

Changelog: [iOS][Changed] - Compile out `RCTGetModuleClasses`/`RCTRegisterModule` and skip static module registration when both `RCT_REMOVE_LEGACY_MODULE_INTEROP` and `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` are defined

`RCTGetModuleClasses()` and the `RCTModuleClasses` registry it reads (populated by
`RCTRegisterModule`) exist solely to support the two legacy interop layers. Every reader
of the registry is already gated behind either `RCT_REMOVE_LEGACY_MODULE_INTEROP` (TurboModule
⇄ legacy NativeModule interop) or `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` (Fabric ⇄ legacy
ViewManager interop). When an app defines **both** macros, the registry has no readers and the
function, its backing statics, and `RCTRegisterModule` are dead code.

This diff compiles them out in that configuration:

- `React/Base/RCTBridge.mm`: guard `RCTModuleClasses`/`RCTModuleClassesSyncQueue`,
  `RCTGetModuleClasses()`, `getCoreModuleClasses()`, and `RCTRegisterModule()` behind
  `#if !defined(RCT_REMOVE_LEGACY_MODULE_INTEROP) || !defined(RCT_REMOVE_LEGACY_COMPONENT_INTEROP)`.
  The old-arch warning helpers (`getModulesLoadedWithOldArch` et al.) stay compiled in — they have
  an external caller in `RCTInstance.mm` — and simply observe an empty list once the registry's only
  writer is gone.
- `React/Base/RCTBridge+Private.h`: guard the matching `RCT_EXTERN` declarations.
- `React/Base/RCTBridgeModule.h`: add a "both-removed" variant of `RCT_EXPORT_MODULE` /
  `RCT_EXPORT_MODULE_NO_LOAD` that emits only `+moduleName` and omits the `RCTRegisterModule` call,
  so native modules still link when the symbol is gone.
- `RCTLegacyUIManagerConstantsProvider.{h,mm}`: wrap the file body in
  `#ifndef RCT_REMOVE_LEGACY_COMPONENT_INTEROP`, matching the sibling `LegacyViewManagerInterop*`
  files. It was the one caller of `RCTGetModuleClasses` whose file body was not already guarded.
- `fbobjc/Libraries/FBReactModuleRegistration/FBReactModuleRegistration.mm`: the FIT/new-arch path
  (`RCT_REMOVE_LEGACY_ARCH`) hand-calls `RCTRegisterModule`. On `cove-ios`, which sets all three
  macros, that symbol is now compiled out, so the call is additionally gated to only run when
  `RCTRegisterModule` still exists. The registry has no readers in that config, so this is a no-op.

The default build (neither/one macro set) is behavior-unchanged: all guarded code is still compiled in.

Reviewed By: sbuggay

Differential Revision: D113555374

fbshipit-source-id: ff695e96977cc159687635cfe61daf5ab9ebff51
Summary:
Pull Request resolved: react#57671

Changelog: [Internal]

`RCTClipboard` is a TurboModule: it conforms to `NativeClipboardSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeClipboardSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113568495

fbshipit-source-id: e4d95285f9540d82162031c2dec426e45704853c
…t#57672)

Summary:
Pull Request resolved: react#57672

Changelog: [Internal]

`RCTAccessibilityManager` is a TurboModule: it conforms to `NativeAccessibilityManagerSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeAccessibilityManagerSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113579886

fbshipit-source-id: a0ac4a470ff02880c9b221f475c897c92865fbd4
…57673)

Summary:
Pull Request resolved: react#57673

Changelog: [Internal]

`RCTActionSheetManager` is a TurboModule: it conforms to `NativeActionSheetManagerSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeActionSheetManagerSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113579871

fbshipit-source-id: 53600d01a6dab6cc1843d3aa1c8af7f0dae3ffd2
Summary:
Pull Request resolved: react#57675

Changelog: [Internal]

`RCTAlertManager` is a TurboModule: it conforms to `NativeAlertManagerSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeAlertManagerSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113579876

fbshipit-source-id: 73eafd8158cbf7b42b893649bfa680048d849894
Summary:
Pull Request resolved: react#57676

Changelog: [Internal]

`RCTAppState` is a TurboModule: it conforms to `NativeAppStateSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeAppStateSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113579877

fbshipit-source-id: b12cd86e90fcd323ddf3daa64c832c9d7c918e2e
Summary:
Pull Request resolved: react#57679

Changelog: [Internal]

`RCTAppearance` is a TurboModule: it conforms to `NativeAppearanceSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeAppearanceSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113579869

fbshipit-source-id: 3706395afba81397c87e33cc4b058dbb78222bcd
Summary:
Pull Request resolved: react#57680

Changelog: [Internal]

`RCTDevLoadingView` is a TurboModule: it conforms to `NativeDevLoadingViewSpec` and implements `getTurboModule:` returning the codegen'd `...SpecJSI`. For TurboModules, the JS->ObjC dispatch is driven by codegen (the generated spec supplies the `selector` and argument kinds, invoked at runtime via `NSMethodSignature` / `NSInvocation`), not by the `RCT_EXPORT_METHOD` macro's `__rct_export__` metadata.

The exported methods here are async-void with concrete parameter types (no generic `id` requiring `RCTConvert` coercion), so the macro is not functionally required. Convert them to plain ObjC method declarations; conformance to the codegen'd `NativeDevLoadingViewSpec` protocol keeps compiler-enforced signature parity.

Signature-only refactor with no change to the JS-facing API. Sync methods, methods with `id` params, and `constantsToExport` are intentionally left untouched.

Reviewed By: cortinico

Differential Revision: D113579873

fbshipit-source-id: 78a43f3586b2b7433fad1ab13be50fae63197870
chrfalch and others added 27 commits August 19, 2026 09:19
…correct the rest (react#58006)

Summary:
Rebased onto `main` after react#57757, react#57756, react#57762 and react#57744 landed. Because this
PR renames `__doc__` and Prettier-formats every file, a hunk-level rebase was the
wrong tool — it stopped on the second of nine commits and the reformat commit then
conflicted with everything after it. So the history is **replayed** on top of the
new `main` instead, and reordered to put Prettier *before* the content edits, which
makes the substantive diff reviewable rather than tangled with reflow.

Verified nothing those four PRs added was lost: every heading on `main` survives
except the two this PR deliberately renames, and their facts — the
`artifactsVersionOverride` pin, the autolinking-config-command pin, the
pre-injection build-setting values, and the promoted-scalar caveat — are carried
into the new "Files the tool touches" table rather than sitting in a row this PR
deletes.

Housekeeping and accuracy pass over `packages/react-native/scripts/spm/__docs__`.
Docs only — no code or test changes.

**Rename.** `scripts/spm/__doc__` was the only `__doc__` directory in the repo;
every other subsystem uses `__docs__` (27+ directories, and the convention
documented in `__docs__/GUIDELINES.md`). The misspelling also meant these files
were **published to npm**: `package.json` includes `scripts/spm` in `files` and
excludes `!**/__docs__/**`, which the misspelled directory dodged.

**Removed two superseded documents.** `rfc-spm-xcframework.md` (707 lines) was a
stale ancestor of RFC0994, still describing `spm init`, xcodeproj *generation*, the
`.xcodeproj.legacy` rename migration, a `spm clean` command that does not exist,
stub `Package.swift` files and VFS overlays. `spm-plugins-assessment.md` evaluated
SwiftPM plugins as a replacement for the injected build phases — conclusion valid,
detail stale (it listed a "Prepare VFS Overlay" phase). Both have had their durable
content folded into RFC0994.

**Corrected the remaining docs against the implementation.** The substantive one:
auto-sync is **two** hooks, not one — a scheme pre-action in the app's *shared*
scheme plus the build phase. The docs described only the phase.

Two claims here are corrections of my own earlier drafts, made after testing them
on `private/helloworld` rather than reading the code:

- Neither hook can bootstrap a clean checkout. With `build/` deleted,
  `xcodebuild -scheme … build` fails in nine lines of log, `Resolve Package Graph`
  first, the pre-action never running. The one-time setup run really is required,
  and the ordering table now shows the measured sequence.
- A sync failure is not unconditionally non-fatal. Exit 2 — a dependency with no
  `Package.swift` — maps to `exit 1` and fails the build deliberately; only other
  non-zero codes warn. As written, the doc also contradicted the exit-2 behaviour
  documented elsewhere in the same file.

Smaller fixes: `hermesvm` → `hermes-engine` (that name is in no code), plugin
`watchPaths` added to the staleness inputs, and the `#auto-sync-build-phase`
anchors left dangling by the heading rename.

**Added a `__docs__/README.md` index**, per `GUIDELINES.md`, plus a **"Files the
tool touches"** table — there was no single answer to what the tool creates or
modifies. Writing it surfaced that `add` creates or appends to `ios/.gitignore`
(documented nowhere), and that `deinit` does not revert that block, anything
`--deintegrate` changed, or a promoted array setting — so the "exact inverse of
`add`" claim needed qualifying in three places.

**Documented `spm.dependencies`**, the SwiftPM analog of a podspec's
`s.dependency`, which was implemented but absent from every doc, and corrected the
scaffold prerequisite: it is `add`/`update` that stops, with a distinct exit code 2,
not the build.

**Prettier-formatted the docs, in its own commit.** These were the only unformatted
markdown files in the repository, so `prettier --list-different "./**/*.md"` goes
from three failures to clean.

## Changelog:

[Internal] - SwiftPM docs: rename `__doc__` to `__docs__`, remove two superseded
documents, and correct the rest against the implementation

Pull Request resolved: react#58006

Test Plan:
- `npx prettier --check "packages/react-native/scripts/spm/__docs__/*.md"` passes;
  `npx prettier --list-different "./**/*.md"` is empty for the whole repo.
- All 19 intra-doc anchor links verified against GitHub's slug rules; none dead.
  Every relative path out of the new README resolves, including the root-index hop.
- `npm pack --dry-run --ignore-scripts` lists no `scripts/spm/__docs__/` entries
  while every `scripts/spm/*.js` still ships.
- The Prettier commit is content-neutral: comparing `[A-Za-z0-9]+` token streams
  across it, all three files are identical word for word.
- Behavioural claims traced to source: `VALID_ACTIONS` in `setup-apple-spm.js`;
  `injectOrCreateScheme` / `addPreActionToScheme` and the `RC -eq 2` branch of
  `buildSyncAutolinkingScript` in `generate-spm-xcodeproj.js`; `BUILTIN_FRAMEWORKS`
  in `flavored-frameworks.js`; `generateXCFrameworksPackageSwift` in
  `generate-spm-package.js`; `ensureGitignoreSpmEntries` and its `action =--sanitized--

Reviewed By: fabriziocucci

Differential Revision: D116598871

Pulled By: cipolleschi

fbshipit-source-id: b1822dcc885b9c882ec3a2626addc42553244b51
…eaders (react#58007)

Summary:
`ios-prebuild/__docs__/README.md` said `React.framework`'s headers-spec layout
"is what both CocoaPods and SwiftPM consume". That is right for CocoaPods and
misleading for SwiftPM: `React.xcframework` is not a member of the Swift package
graph at all, so nothing on the SwiftPM side reads its framework module map.

What actually happens is a staging step on the consumer side —
`stageReactHeadersTarget` in `scripts/spm/flavored-frameworks.js` copies
`React.framework/Headers` into `ReactHeadersTarget/include/React` and rewrites
`framework module React` to a plain `module React`, which is then vended as the
`ReactHeaders` target. The prebuild output is still the source of those headers,
which is why the sentence was nearly right; the consumption path is what differs.

Says so, and keeps the CocoaPods half explicit about `FRAMEWORK_SEARCH_PATHS` so
the two paths read as the distinct mechanisms they are.

## Changelog:

[Internal] - Clarify that SwiftPM consumes the prebuilt React headers through a
staged `ReactHeaders` target, not through the XCFramework's module map

Pull Request resolved: react#58007

Test Plan:
Docs only. Verified against `scripts/spm/generate-spm-package.js`, whose generated
`ReactNative` manifest declares exactly three headers-only products and no runtime
`binaryTarget`, and against `stageReactHeadersTarget`, which does the copy and the
module-map rewrite only after Debug and Release are asserted to expose identical
headers. Prettier clean — the file was formatted before this change and still is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Reviewed By: fabriziocucci

Differential Revision: D116598314

Pulled By: cipolleschi

fbshipit-source-id: 3b58101e64410d35f0346c696992dda8261e00a4
Summary:
Pull Request resolved: react#58001

The existing npm `clang-format` wrapper bundles LLVM 15, causing `yarn clang-format` to disagree with the formatting of checked-in headers.

Replace it with checksum-pinned DotSlash artifacts for `clang-format` 21.1.2 across Linux, macOS, and Windows. Add a chunked formatting runner and remove the obsolete npm dependency. The runner skips generated sources and ignores dirsynchronized or vendored subtrees that maintain their own formatting.

Changelog: [Internal]

Reviewed By: christophpurrer

Differential Revision: D116500359

fbshipit-source-id: 2141bee22ed2b712aae1637a1f052db23535bbb0
…oid) (react#58012)

Summary:
Pull Request resolved: react#58012

**Motivation**

Third-party networking stacks that replace React Native's networking modules have no supported way to report CDP and Web Performance network events, so their requests are invisible in the React Native DevTools Network panel.

**This diff**

On Android, the `InspectorNetworkReporter` integration point (our intended public API) is closed — change `InspectorNetworkReporter`'s visibility to `public`, and annotate with`UnstableReactNativeAPI`.

On iOS no visibility change is needed — `RCTInspectorNetworkReporter` already ships via `React-Core`.

**🚧 Unstable API**

These APIs are exposed, but marked with `UnstableReactNativeAPI` (on Android) until we consolidate their use by an external integrator (Expo). This leaves open the potential for minor changes, although unlikely.

**Changes**

- `InspectorNetworkReporter` is now `public`, annotated `UnstableReactNativeAPI`, requiring explicit opt-in. `NetworkEventUtil` and `WebSocketModule` opt in via `Silvochka:OptIn`.
- `react/networking/` joins the prefab header export in `build.gradle.kts`, matching iOS's `React-networking` pod parity.
- Doc comments on `NetworkReporter`, `RCTInspectorNetworkReporter` and `InspectorNetworkReporter` drop `[Experimental]`, naming each as the third-party integration point.

Changelog:
[Android][Added] - **Network Inspection**: Expose network event reporting to third-party networking stacks as an unstable API

Reviewed By: vzaidman

Differential Revision: D116472113

fbshipit-source-id: be5a78b9ed0080b5305df588a7e7fd29d3169bb8
Summary:
Android Maestro E2E jobs currently stop after the first flow that exhausts its in-process retries. The workflow-level retry then starts the whole suite again, including flows that already passed.

This changes the retry model so that:

- each CI attempt runs every selected flow once and continues after individual failures;
- per-flow `passed`, `failed`, and `pending` state is saved atomically after every flow;
- cumulative results and attempt counts are shown in the GitHub job summary and uploaded as an artifact;
- retry workflows download that state and skip flows that already passed, running only failed or unfinished flows;
- a flavor whose downloaded state is already fully passed skips emulator startup and APK installation entirely;
- a missing state artifact falls back to running the full suite, so infrastructure failures remain retryable.

The Android E2E timeout is increased from 60 to 90 minutes to give the initial all-flows attempt enough time to finish. The existing three workflow attempts now provide up to three executions per failing flow instead of multiplying workflow retries by in-process retries.

### CI follow-ups

- RNTester Debug completed all 40 flows with 38 passes and 2 failures. Retry 1 downloaded its state, skipped exactly those 38 passing flows, and executed only the two failures, validating the selective retry behavior.
- RNTester Release became unhealthy after an early flow failure and timed out while later flows were still pending. The state file was being written incrementally, but its artifact upload was inside the timed composite action and was killed by the same timeout. Moved state upload into the outer reusable workflow so `if: always()` preserves passed, failed, and pending results after an E2E timeout.
- The next Release retry downloaded the preserved state, skipped its 7 prior passes, reran the remaining flows, and recovered to 40/40. A later matrix retry could still start that completed flavor and fail during an unnecessary APK installation, so retry jobs now skip the entire E2E action when downloaded state is fully passed.
- Both `test_js` variants failed because the repository Jest preset throws whenever `console.error` is called, and the new failure-path unit test intentionally exercised a production error log. The test now mocks that expected log while continuing to assert the aggregate flow failure.
- RNTester Debug consistently failed `image-wide-gamut.yml` with exactly 83.974% screenshot similarity and `legacy-native-module.yml` before the APIs search became visible, while Release passed both immediately. Reproducing Debug with an offline Android emulator showed the actual cause: importing `ImageExample.js` eagerly starts a Facebook image prefetch, and its promise could reject before the Image Loading Events example attached its rejection callback. That produced an `Uncaught (in promise): UnknownHostException` LogBox notification, which covered the screenshot and intercepted the APIs tab. The prefetch now attaches a rejection handler immediately while preserving the existing example-level success/failure reporting. No LogBox dismissal or E2E-specific workaround remains.

- The remaining `image-wide-gamut.yml` failure was separate from LogBox: the flow explicitly accepted `P3: error` but then compared against a golden screenshot containing the successfully loaded Display-P3 fixture. The same ICC-profiled WebKit sample is now embedded as a compact data URI, removing the external network dependency, and the flow requires `P3: loaded` before screenshot comparison.

## Changelog:

[INTERNAL] [CHANGED] - Retry only failed or unfinished Android Maestro E2E flows.

Pull Request resolved: react#57998

Test Plan:
- `git diff --check` (passed)
- `node --check .github/workflow-scripts/maestro-android.js` (passed)
- `yarn jest .github/workflow-scripts/__tests__/maestro-android-test.js --runInBand --config '{"testEnvironment":"node","transform":{}}'` (passed)
- `ruby -ryaml -e "ARGV.each { |file| YAML.parse_file(file) }" .github/actions/maestro-android/action.yml .github/workflows/e2e-android-rntester.yml .github/workflows/e2e-android-templateapp.yml .github/workflows/test-all.yml` (passed)
- `yarn prettier --check .github/workflow-scripts/maestro-android.js .github/workflow-scripts/__tests__/maestro-android-test.js .github/actions/maestro-android/action.yml .github/workflows/e2e-android-rntester.yml .github/workflows/e2e-android-templateapp.yml .github/workflows/test-all.yml` (passed)
- Verified the retry-state `jq` predicate returns false only when every recorded flow has passed.
- `./node_modules/.bin/prettier --check packages/rn-tester/js/examples/Image/ImageExample.js` (passed)
- Reproduced on a local Android Debug emulator with external DNS unavailable. Before the fix, `logcat` reported `Uncaught (in promise): UnknownHostException` and LogBox covered the bottom navigation. After the fix, no unhandled rejection or LogBox appeared, the APIs tab displayed `explorer_search`, and the wide-gamut example was unobstructed.

- Verified on a local Android Debug emulator with external DNS unavailable that the embedded Display-P3 fixture reaches `P3: loaded` and renders without a network request.
- The next Debug run passed the stricter `P3: loaded` gate but still reproduced the exact 83.974% screenshot mismatch, followed by the legacy APIs-search failure. This confirms the embedded Display-P3 fixture is loading and that a second Debug-only UI state remains. The previous action skipped its Maestro-log upload because the emulator wrapper masked the inner script failure, and it pulled `screen.mp4` before stopping the recorder, leaving an unplayable artifact. The runner now captures a PNG, UI hierarchy, logcat, and Metro output when each flow fails, stops `screenrecord` before pulling the MP4, and always uploads the diagnostic bundle. The next run will expose the actual obstructing UI so it can be fixed at its source.
- The first diagnostic run exposed a flaw in the new evidence collection itself: Debug remained inside the E2E action far beyond its normal duration because the `uiautomator`/ADB capture commands were unbounded. Every diagnostic subprocess now has a 15-second timeout, so a problem collecting evidence is logged but can never stall later flows or prevent state persistence.
- The captured screenshots and hierarchy identified the root cause shared by all remaining Debug failures (including one additional FlatList flow): `StaticViewConfigValidator` reported that `AndroidTextInput.validAttributes.fontVariationSettings` had the wrong value. The object-syntax change in react#57929 added a JS processor to the static TextInput config, but runtime native view-config reflection still derived plain `true` from the native `String` prop. Debug validation therefore emitted a LogBox; its notifications covered the wide-gamut screenshot and FlatList controls, and its expanded console blocked the legacy APIs tab. Native view-config construction now recognizes `fontVariationSettings` and installs the same processor as the static config. This both makes validation agree and preserves object-to-string normalization when reflected native configs are active. A regression test verifies the reflected attribute matches the static processor and serializes object settings deterministically.
- Targeted regression test: `yarn test packages/react-native/Libraries/ReactNative/__tests__/getNativeComponentAttributes-test.js --runInBand` (passed).
- Both `test_js` variants then failed at ESLint before running tests because the new regression test lacked the required Flow file annotation and its CommonJS imports were not in repository order. Added `flow strict-local` and reordered the requires; the targeted Jest test and Prettier check remain green.
- Final Android validation on `bd55f0aa4226`: the initial RNTester emulators lost ADB after three passing flows, so the outer state artifacts preserved those passes and marked the remaining work for retry. Retry 1 started fresh emulators, skipped the three prior passes, ran the remaining 37 flows, and finished with 40/40 passed in both Debug and Release; retry 2 was skipped. `image-wide-gamut.yml`, `legacy-native-module.yml`, and `flatlist-inverted-recycle-maintainvisible.yml` each passed on their first execution after the view-config fix. All Android checks are green, including Android builds, HelloWorld, TemplateApp E2E, and RNTester E2E.

Reviewed By: Abbondanzo

Differential Revision: D116463690

Pulled By: cipolleschi

fbshipit-source-id: 20f6d04ac8e21cdbb1fd04618cd879df6d413276
…etaches (react#57941)

Summary:
Fixes react#43586.

`value.addListener(cb)` stops firing forever once any component bound to that `Animated.Value` unmounts, even though the value is still alive and still animating.

The chain:

1. [`AnimatedProps.__detach()`](https://github.com/react/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedProps.js#L222-L235) loops `node.__removeChild(this)` on unmount.
2. [`AnimatedWithChildren.__removeChild()`](https://github.com/react/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedWithChildren.js#L63-L65) does `if (this._children.length === 0) { this.__detach(); }` — the **value** detaches itself.
3. [`AnimatedNode.__detach()`](https://github.com/react/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedNode.js#L51-L52) calls `this.removeAllListeners()`, which discards callbacks the **caller** registered.

An `Animated.Value` is owned by the caller and routinely outlives the components it drives. `addListener` / `removeListener` / `removeAllListeners` are documented public API. Detaching from the graph should not silently unregister the caller's callbacks.

### Why this is a regression, not intended behaviour

`removeAllListeners()` was added to `__detach()` in cd83194 (Oct 2022) — but **behind a feature flag**, `removeListenersOnDetach`, which shipped as `() => false` in OSS:

```js
// v0.71.19 Libraries/Animated/nodes/AnimatedNode.js
__detach(): void {
  if (ReactNativeFeatureFlags.removeListenersOnDetach()) {
    this.removeAllListeners();
  }
  ...
```

49d5e7c (Nov 2022) then deleted the flag as an "unused feature flag" under `changelog: [internal]`, inlining the enabled branch. That flipped the OSS default and is exactly the 0.71 → 0.72 regression a second reporter bisected in [this comment](react#43586 (comment)). The user-facing semantics change was never the intent of that commit.

### Why removing the call is safe

cd83194's stated purpose was narrow:

> Removing listener on detached node leads to a red box, if the said node is `DiffClampAnimatedNode`. This is because calling `AnimatedNode.__getNativeTag()` makes native module call and creates node in native. This node is not completely initialised and red boxes […] The fix is make sure all listeners are removed before node is destroyed.

The requirement is **stop listening to native value updates before the native node is dropped**, not *discard the caller's callbacks*. Those are two different things, and today they are cleanly separable:

- `AnimatedValue.removeAllListeners()` clears `_listeners` (caller-owned) **and** calls `this._updateSubscription?.remove()` (node-owned: the `onAnimatedValueUpdate` emitter subscription plus `stopListeningToAnimatedNodeValue`).
- Only the second belongs in `__detach()`.

The 2022 hazard is also structurally gone. In 0.71, `_stopListeningForNativeValueUpdates()` called `NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag())` — on a detached node `__getNativeTag()` resurrects a half-initialised native node, which is the red box. Today's [`_updateSubscription.remove()`](https://github.com/react/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedValue.js#L182-L192) closes over a local `nativeTag` const and never calls `__getNativeTag()`. This PR adds no new `__getNativeTag()` call on any path.

So `__detach()` now tears down only what the node owns, and the ordering that mattered (stop listening → `dropAnimatedNode`) is preserved.

### Does this leak?

No framework-owned resource is retained.

- The `onAnimatedValueUpdate` `NativeEventEmitter` subscription and the native `startListeningToAnimatedNodeValue` state are still released on detach — asserted by a new test.
- `_listeners` lives on the `AnimatedValue` itself. React Native keeps no registry of JS `Animated` values, so a value is reachable only from user code. Drop the value and the listeners go with it.
- If the caller deliberately keeps a value alive past its components, the retained graph is exactly what their own closures capture, and `removeListener()` / `removeAllListeners()` are the documented way to release it.

Both in-tree consumers that register listeners on an `AnimatedValue` already clean up after themselves and never relied on `__detach()` doing it — [`ScrollViewStickyHeader`](https://github.com/react/react-native/blob/main/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js#L247-L251) and [`createAnimatedPropsHook`](https://github.com/react/react-native/blob/main/packages/react-native/src/private/animated/createAnimatedPropsHook.js#L221-L223), both in effect cleanups.

Honest behavioural delta: a caller who adds a listener on every mount and never removes it, on a value that outlives those components, will now accumulate listeners. Previously the accumulation was hidden by the very bug being fixed. Note the old behaviour was not a dependable cleanup mechanism either — it only fired when the *last* child detached, and never for listeners added after detach.

## Relationship to react#57170

They overlap and **cannot both land as-is**.

- Textual: react#57170 rewrites `AnimatedValue.__detach()`, so `git apply --3way` of its patch onto this branch conflicts in `AnimatedValue.js`.
- Semantic: react#57170 new tests assert the behaviour this PR changes (`__detach()` → `removeAllListeners()` on the r/g/b/a channels). Those assertions would need rewriting on top of this change.

They also address the same underlying defect from opposite ends. react#57170 clamps `_listenerCount` so it cannot go negative. I measured where the negative count comes from — `__detach()` zeroing `_listenerCount` out from under a caller who still holds a listener id:

| flow | `main` | this PR |
|---|---|---|
| `addListener` → `__detach()` | `_listenerCount === 0` | `_listenerCount === 1` |
| … then `removeListener(id)` | `_listenerCount === -1` | `_listenerCount === 0` |

This PR removes that root cause, so the count stays consistent without clamping. I have no opinion on whether the clamp is still wanted as defence-in-depth — flagging the interaction for whoever reviews both.

## Changelog

[GENERAL] [FIXED] - Animated - `Animated.Value` listeners registered with `addListener` are no longer removed when a component bound to the value unmounts

Pull Request resolved: react#57941

Test Plan:
Tests added:

- `packages/react-native/Libraries/Animated/__tests__/Animated-test.js`
  - `should keep listeners when the last attached node detaches` — node-graph level.
  - `should keep listeners when a bound component unmounts` — the user-visible path: render `<Animated.View style={{transform: [{translateX: value}]}} />`, unmount it, then `setValue(42)` and assert the listener fires.
- `packages/react-native/src/private/animated/__tests__/AnimatedNative-test.js`
  - `should stop listening to native updates on unmount, but keep listeners` — guards what `removeAllListeners()` was there for: after unmount, `stopListeningToAnimatedNodeValue(tag)` and `dropAnimatedNode(tag)` are still called and a subsequent `onAnimatedValueUpdate` emission does **not** reach the listener, while `hasListeners()` stays `true`.
  - `should resume delivering native updates when remounted` — native driver end to end: unmount, remount, and native updates on the new tag reach the original listener.

Counterfactual — reverting only the two source files and keeping the tests:

```
$ git checkout -- packages/react-native/Libraries/Animated/nodes/AnimatedNode.js \
                  packages/react-native/Libraries/Animated/nodes/AnimatedValue.js
$ yarn jest packages/react-native/Libraries/Animated/__tests__/Animated-test.js \
            packages/react-native/src/private/animated/__tests__/AnimatedNative-test.js

  ● Native Animated › Animated Listeners › should stop listening to native updates on unmount, but keep listeners
  ● Native Animated › Animated Listeners › should resume delivering native updates when remounted
  ● Animated › Animated Listeners › should keep listeners when the last attached node detaches
  ● Animated › Animated Listeners › should keep listeners when a bound component unmounts

Test Suites: 2 failed, 2 total
Tests:       4 failed, 97 passed, 101 total
```

All four fail without the change; the 97 pre-existing tests in those two files pass either way.

Full suite, with the change restored:

```
$ yarn test
Test Suites: 218 passed, 218 total
Tests:       1 skipped, 5589 passed, 5590 total

$ yarn flow-check
Found 0 errors

$ yarn lint
Done in 9.20s.   (eslint --max-warnings 0 .)
```

Baseline on `main` measured on the same checkout: 218 suites, 5585 passed / 1 skipped — this PR adds exactly the 4 tests above.

Not verified: I did not run this on a device or simulator, so the fix is verified through the JS graph and the mocked native-driver harness rather than against the reporter's app.

Reviewed By: javache

Differential Revision: D116040594

Pulled By: zeyap

fbshipit-source-id: b9bcb18c8ca01fa28a1dd8ff1c1737c15627d520
Summary:
Pull Request resolved: react#58000

VirtualizedList viewability only considered the scroll-axis viewport length. A horizontal list with zero height could therefore report items as viewable. Record the cross-axis length in the same scroll metrics snapshot and require both viewport dimensions to be non-zero for viewability.

Changelog:
[General][Fixed] - Prevent zero-sized lists from reporting viewable items

Reviewed By: zeyap, christophpurrer

Differential Revision: D116356139

fbshipit-source-id: 77c1b61bddfde1dcac9e670ddab80915ef42149a
Differential Revision:
D116356139

Original commit changeset: 77c1b61bddfd

Original Phabricator Diff: D116356139

fbshipit-source-id: 427c3f65354fb0b74191d3a3f1f4db5d51d9c9d1
…guards (react#57996)

Summary:
Pull Request resolved: react#57996

Classifies `react/renderer/viewtransition:viewtransition` as a private target under the C++ stable API three-tier visibility model. Adds `#include <react/cxxstableapi/PrivateGuard.h>` to the module's only header, `ViewTransitionModule.h`, and declares the guard dependency in the build config (parent podspec is already configured).

Changelog: [Internal]

Reviewed By: zeyap

Differential Revision: D116452310

fbshipit-source-id: e7e6eec996d099b11797b3e9ed46ba074e93d6a1
…OS (react#57546)

Summary:
On iOS, a programmatic non-animated scroll — `scrollTo` / `scrollToOffset({ animated: false })`, or any library driving the offset frame-by-frame — cancels every active touch in enclosing scroll views. Two mechanisms combine into this:

1. `scrollToOffset:animated:` calls `_forceDispatchNextScrollEvent` and, for non-animated scrolls, `_handleFinishedScrolling` — so every call emits `onScroll` (twice) plus `onMomentumScrollEnd`, bypassing `scrollEventThrottle` entirely. A per-frame driver produces a continuous stream of unthrottled `topScroll` events (~60/s measured with `scrollEventThrottle={2000}`).
2. In the responder system, any `topScroll` event without `responderIgnoreScroll: true` starts a responder negotiation, and `ScrollView`'s `onScrollShouldSetResponder` answers `true` whenever a finger is down inside it. Each event therefore steals the responder from a pressed `Pressable`/`Touchable` and the press is cancelled — `onPressIn` fires, `onPress` never does.

On Android scroll events carry `responderIgnoreScroll: true`.

I added `responderIgnoreScroll` to the C++ `ScrollEvent` payload and set it to `!_isUserTriggeredScrolling` in `_scrollViewMetrics`. Programmatic scrolls no longer transfer the responder, while user-initiated scrolls (drag, deceleration, scroll-to-top) keep today's behavior.

## Changelog:

[IOS] [FIXED] - Programmatic (non-user-initiated) scrolls no longer cancel active touches in enclosing scroll views

Pull Request resolved: react#57546

Test Plan:
Reproducible code — an endless marquee `FlatList` nested in a `ScrollView`, driven by `requestAnimationFrame` + `scrollToOffset({ animated: false })`, with a sibling `TouchableOpacity` and a counter proving the touches reach JS:

<details><summary>App.tsx</summary>

```tsx
import { useEffect, useMemo, useRef, useState } from 'react';
import {
  FlatList,
  ScrollView,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

const dpPerSecond = 20;
const size = 80;
const gap = 8;
const data = Array.from({ length: 6 }).map((_, i) => ({
  id: `id-${i}`,
  n: i + 1,
}));
const renderItem = ({ item }: { item: (typeof data)[0] }) => (
  <View
    style={{
      width: size,
      height: size,
      backgroundColor: 'red',
      justifyContent: 'center',
      alignItems: 'center',
    }}>
    <Text>#{item.n}</Text>
  </View>
);

const Carousel = ({ paused }: { paused: boolean }) => {
  const ref = useRef<FlatList>(null);
  const [width, setWidth] = useState(0);
  const offset = useRef(0);

  useEffect(() => {
    if (paused) return;
    const x = (size + gap) * data.length;
    let last = Date.now();
    let raf: number;
    const loop = () => {
      const now = Date.now();
      offset.current =
        (offset.current + (dpPerSecond * (now - last)) / 1000) % x;
      last = now;
      ref.current?.scrollToOffset({ offset: offset.current, animated: false });
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [paused]);

  const neededToFill = Math.ceil(width / (size + gap));
  const extendedData = useMemo(
    () => [
      ...data,
      ...data.slice(0, neededToFill).map((d) => ({ ...d, id: d.id + '-dup' })),
    ],
    [neededToFill]
  );

  return (
    <FlatList
      scrollEnabled={false}
      scrollEventThrottle={2000}
      showsHorizontalScrollIndicator={false}
      windowSize={3}
      onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
      contentContainerStyle={{ gap }}
      ref={ref}
      horizontal
      data={extendedData}
      keyExtractor={(item) => item.id}
      renderItem={renderItem}
    />
  );
};

export default () => {
  const [paused, setPaused] = useState(true);
  const [touches, setTouches] = useState(0);

  return (
    <View style={{ flex: 1 }} onTouchStart={() => setTouches((t) => t + 1)}>
      <ScrollView contentContainerStyle={{ paddingVertical: 64, gap: 32 }}>
        <Carousel paused={paused} />
        <TouchableOpacity onPress={() => setPaused((p) => !p)}>
          <Text style={{ fontSize: 20 }}>
            Try tapping me {paused ? '▶️' : '⏸️'}
          </Text>
        </TouchableOpacity>
        <Text style={{ fontSize: 16 }}>touches seen by JS: {touches}</Text>
      </ScrollView>
    </View>
  );
};
```

</details>

Before this change, only the first tap works (it starts the marquee); every following tap increments the touch counter but never toggles the button — the press is cancelled by the responder transfer. After this change, every tap toggles the marquee.

Recordings of the repro above (every touch is marked with a blue ring and counted on screen):

Before:

https://github.com/user-attachments/assets/bc5a8764-efe3-40ba-a9cb-a5023d140369

After:

https://github.com/user-attachments/assets/b2c1eea1-dcab-422c-9a4a-90fd69061e9d

Reviewed By: cipolleschi

Differential Revision: D116453881

Pulled By: j-piasecki

fbshipit-source-id: eb72fb0f1a4ac9fdde15f621201009d855d63890
…57978)

Summary:
Pull Request resolved: react#57978

Classifies `hermes/inspector-modern:chrome` as a private target under the C++ stable API three-tier visibility model. Adds `#include <react/cxxstableapi/PrivateGuard.h>` to all three headers in the module and declares the guard dependency in the build config.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D116283495

fbshipit-source-id: 3099293e51c039b0421c2315391eb16a7d389004
Summary:
Pull Request resolved: react#57961

Classifies `react/renderer/dom` as a private target under the three-tier C++ stable API visibility model. `DOM.h` now includes `<react/cxxstableapi/PrivateGuard.h>`. Consumers that opt into `RN_STRICT_API` now get an error if they include `DOM.h` directly; without that flag the guard is inert, so no existing build changes behaviour.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D116013879

fbshipit-source-id: 235d611e6dfd356b47d4eca42485a30eab711dd4
Summary:
Pull Request resolved: react#57966

This diff rolls umbrella guards for `react/renderer/components/image`.
Initially, we consider image component target as public so the Umbrella file is added as the proxy for all includes.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D109849952

fbshipit-source-id: b8d4b484ccdd6236d6d4d58877f72cae8b717ebd
…I guards (react#57962)

Summary:
Pull Request resolved: react#57962

Marks `jsinspector-modern/network` as a private module under the C++ Stable API by adding `#include <react/cxxstableapi/PrivateGuard.h>` to its four exported headers (`BoundedRequestBuffer.h`, `CdpNetwork.h`, `HttpUtils.h`, `NetworkHandler.h`), and wiring the `React-cxxstableapi` dependency into BUCK and the podspec.

The guards are inert unless a consumer defines `RN_STRICT_API`, so there is no behavior change.

Changelog: [Internal]

Reviewed By: huntie, cipolleschi

Differential Revision: D116026740

fbshipit-source-id: 42b9d5426ed2523b76e7ebb9222373dea6d21b7b
…ubtree. (react#57312)

Summary:
Pull Request resolved: react#57312

This change adds the umbrella-header and include-guard mechanism across the `react/renderer/components/*` modules, classifying each target as public. The change includes:

- react/renderer/components/modal:modal - `public`,
- react/renderer/components/root:root - `public`,
- react/renderer/components/scrollview:scrollview - `public`,

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D109316840

fbshipit-source-id: b6d6235fa772c7b39bfa92c11e396ebfb4fd00fc
…act#57980)

Summary:
Pull Request resolved: react#57980

Two changes to UnimplementedView on both iOS and Android:

1. **iOS: Add `#if DEBUG` guard** — the red overlay and error text in `RCTUnimplementedViewComponentView` and `RCTUnimplementedNativeComponentView` were shown in release builds (unlike Android which already had `ReactBuildConfig.DEBUG` guards). Users saw a red semi-transparent overlay with the component name. Now matches Android behavior — release users see nothing.

2. **Both platforms: Add production error logging** — `RCTLogError` on iOS and `ReactSoftExceptionLogger` on Android, outside the DEBUG guard. These fire in all builds including production, so missing native component registrations are reported to error dashboards instead of being completely silent.

Files changed:
- `RCTUnimplementedViewComponentView.mm` — primary Fabric fallback view (iOS)
- `RCTUnimplementedNativeComponentView.mm` — UnimplementedNativeView component (iOS)
- `ReactUnimplementedView.kt` — Android equivalent

Changelog:
[iOS][Fixed] - Hide unimplemented native component placeholders outside development builds
[General][Changed] - Log missing native component registrations on iOS and Android

 ---

Reviewed By: javache

Differential Revision: D101001824

fbshipit-source-id: df88c28052b005ff68cbd7600509c290d3df4363
…ets (react#58009)

Summary:
Pull Request resolved: react#58009

This change adds the umbrella-header and include-guard mechanism across `react/renderer/components/*` private modules. The change includes:

- react/renderer/components/inputaccessory:inputaccessory - `private`,
- react/renderer/components/legacyviewmanagerinterop:legacyviewmanagerinterop - `private`,
- react/renderer/components/progressbar:androidprogressbar - `private`,
- react/renderer/components/safeareaview:safeareaview - `private`,
- react/renderer/components/switch:androidswitch - `private`,
- react/renderer/components/unimplementedview:unimplementedview - `private`,
- react/renderer/components/virtualview:virtualview - `private`,

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D116298251

fbshipit-source-id: 799a820207cf67698b0ed6ad841b7999f7d1fa25
…eact#57985)

Summary:
Pull Request resolved: react#57985

The two RNTester TurboModule example screens had drifted apart, making it hard to
compare TurboModule and C++ TurboModule behaviour side by side. Align
`SampleTurboModuleExample` with the style already used by
`NativeCxxModuleExampleExample`:

- Order and group the entries in `_tests` the same way (callback, ArrayBuffer
  group, `get*` group, promises, `voidFunc`), so the buttons render in the same
  order on both screens.
- Make the `Examples` union match the tests that actually exist; it still listed
  many entries copied from the C++ example that `SampleTurboModule` does not
  implement (`getCustomHostObject`, `getSet`, `setMenuItem`, ...) and was missing
  `getEnum`, `getRootTag` and `getUnsafeObject`.
- Add the missing `installJSIBindings` entry to `ErrorExamples` and type
  `_renderResult` as `Examples | ErrorExamples`.
- Surface rejected promises from the error tests in the UI instead of only
  logging them to the console, matching the other screen.
- Fix `getUnsafeObject` to call `getUnsafeObject` instead of `getObject`.
- Merge the duplicated `NativeSampleTurboModule` imports and drop a stale Flow
  suppression.

Changelog:
[Internal]

Reviewed By: cipolleschi

Differential Revision: D116375214

fbshipit-source-id: cff2e138998b22d75ece64c7faf34869061105d3
…dii (react#58028)

Summary:
Fixes react#57841.

`outlineStyle: 'dotted'` and `'dashed'` render as solid on iOS. Android renders all three correctly, and the documented API lists all three values with no platform caveat.

### Root cause

`-[RCTViewComponentView invalidateLayer]` draws the outline through one of two paths:

- **Core Animation** — sets `_outlineLayer.borderWidth` / `.borderColor`. Cheap, but `CALayer` can only stroke a *solid* border.
- **Core Graphics** — `RCTAddContourEffectToLayer`, which forwards `outlineStyle` to `RCTGetBorderImage` and does honour dotted/dashed.

The branch choosing between them only looked at the border radii:

```objc
if (areBorderRadiiCircular(borderMetrics.borderRadii) && borderMetrics.borderRadii.topLeft.horizontal == 0) {
```

Every view **without a border radius** therefore took the Core Animation path, and its `outlineStyle` was silently dropped. Rounded views fell through to Core Graphics — which is why a dotted or dashed outline starts working as soon as a `borderRadius` is set.

The equivalent branch for *borders*, ~80 lines above, already gets this right: `useCoreAnimationBorderRendering` includes `borderMetrics.borderStyles.left == BorderStyle::Solid`, so a non-solid border falls through to Core Graphics. The outline branch never got the matching check when `outline` was implemented in react#46444.

### Fix

Add the missing `outlineStyle == OutlineStyle::Solid` term, so non-solid outlines fall through to Core Graphics exactly as non-solid borders already do. Solid outlines keep the cheaper Core Animation path, so there is no cost for the common case.

### Why it went unnoticed

The only RNTester examples using `outlineStyle: 'dotted'` / `'dashed'` also set a `borderRadius`, so they only ever exercised the working path. This PR adds the square-cornered cases next to them, labelled, so all six combinations of `{square, rounded} × {solid, dashed, dotted}` are visible at once.

### Note on react#57837

react#57837 (open) rewrites the two `UIColor *outlineColor = ...` lines in this same block to resolve
dynamic colors against the trait collection. This change deliberately leaves those two lines
untouched — it only adds a term to the surrounding `if` — so the two patches touch disjoint lines
and should merge without conflict in either order.

## Changelog:

[IOS] [FIXED] - Render `outlineStyle: 'dotted'` and `'dashed'` on views without border radii

Pull Request resolved: react#58028

Test Plan:
### Manual verification

RNTester → Components → View → Outline, iPhone 17 Pro Simulator (iOS 26.1), New Architecture, built from source (`RCT_USE_PREBUILT_RNCORE=0`).

| Before | After |
| --- | --- |
| <img src="https://raw.githubusercontent.com/neutronm/react-native/960889bd70d6cb18839f9d3586690908b0d8207d/assets/57841/before.png" width="420"> | <img src="https://raw.githubusercontent.com/neutronm/react-native/960889bd70d6cb18839f9d3586690908b0d8207d/assets/57841/after.png" width="420"> |
| `square dashed` and `square dotted` render solid | all six render correctly |

The rounded cases and every pre-existing example in the Outline screen are pixel-identical before and after; only the two square non-solid outlines change.

### Automated

| Command | Result |
| --- | --- |
| `yarn jest` | 222/223 suites, 5713 tests pass |
| `yarn flow-check` | `Found 0 errors` |
| `yarn lint` (`eslint --max-warnings 0 .`) | clean |
| `node ./scripts/clang-format.js <changed .mm files>` | no changes |
| `yarn build-types --validate` | `PASS  API snapshot is up to date.` |
| `tsc -p packages/react-native/__typetests__/tsconfig.json` | clean |
| `yarn test-ios` (`RNTesterUnitTests`) | `** TEST SUCCEEDED **` — 167 tests, 16 skipped, 0 failures |

The one Jest suite that did not pass in the full run (`GenerateComponentDescriptorH-test.js`) was a worker `SIGSEGV`, not an assertion failure; it passes 18/18 when run on its own.

### Unit tests

`React/Tests/Mounting/RCTViewComponentViewTests.mm` gains two cases next to the existing ones:

- `testSquareSolidOutlineUsesCoreAnimationBorder` — a square solid outline still uses `CALayer.borderWidth` (the fast path is not regressed).
- `testSquareDottedAndDashedOutlinesAreDrawnWithCoreGraphics` — square dotted and dashed outlines are drawn into `layer.contents` with `borderWidth == 0`.

`React/Tests/**` is excluded from the `React-Core` podspec, so these are not built by the OSS `RNTesterUnitTests` target. They were verified to compile against the installed pod headers with `clang -fsyntax-only -x objective-c++ -std=c++20`.

Reviewed By: cipolleschi

Differential Revision: D116779403

Pulled By: javache

fbshipit-source-id: b8d1f3508418016252ec9b1f9aaab7107c8f3b9f
Summary:
Pull Request resolved: react#58048

Restore `ECOSYSTEM.md` so existing GitHub links continue to resolve. Explain that React Native is now part of the React Foundation and direct readers to the Foundation website for current ecosystem information.

Changelog: [Internal]

___

Reviewed By: vzaidman

Differential Revision: D116920238

fbshipit-source-id: 5eb49de204fe32fb2eafc22e441d9de846858a1a
Summary:
Pull Request resolved: react#58013

Add `<React/MapBuffer.h>` as the public C++ entry point for MapBuffer and MapBufferBuilder. Guard direct leaf-header inclusion for strict API consumers while preserving existing React Native builds and legacy include paths.

Export and stage the umbrella consistently through Buck, CMake, Android Prefab, CocoaPods, and the Apple prebuilt-header inventory.

Changelog:
[Internal]

Reviewed By: cipolleschi

Differential Revision: D116632721

fbshipit-source-id: a44dd7ab1f1fd361ab1609a360667d727c2ccb17
Summary:
Pull Request resolved: react#58025

Rolls the umbrella-header + include-guard mechanism across the
`react/renderer/uimanager` module, classifying the target as public.

- Adds `<React/UIManager.h>`, re-exporting the module's public interface headers.
- Adds `<react/cxxstableapi/UmbrellaGuard.h>` to the module's public headers.
- Wires the umbrella header directory into the Buck, CMake, CocoaPods, Gradle,
  and iOS prebuild header configurations.
- Deprecated `SurfaceRegistryBinding.h` does not include umbrella guard because it will fail anyway when included directly (it includes AppRegistryBinding that does include guard).

React Native's own sources keep using the fine-grained
`<react/renderer/uimanager/...>` includes; only outside consumers use the
umbrella.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D116763079

fbshipit-source-id: 8994e01d782d0a16677e244f42f573a1583abdda
…#58041)

Summary:
Pull Request resolved: react#58041

Classifies `react/nativemodule/featureflags:featureflags` as a private target under the three-tier C++ stable API visibility model. Consumers that opt into `RN_STRICT_API` now get an error if they include its header directly; without that flag the guard is inert, so no existing build changes behaviour.

`NativeReactNativeFeatureFlags.h` is generated, so the guard is added to its template and the file regenerated with `yarn featureflags --update` rather than edited in place.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D116916158

fbshipit-source-id: 7fd9ab146d4dff33976f67b72202b9b4ed87ad03
Summary:
Pull Request resolved: react#58050

Classifies `react/runtime/hermes:hermes` as a "for frameworks" target under the three-tier C++ stable API visibility model. Consumers that opt into `RN_STRICT_API` now get a warning if they include its headers directly, which they can acknowledge with `RN_ALLOW_FRAMEWORKS`; without that flag the guard is inert, so no existing build changes behaviour.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D116926134

fbshipit-source-id: 99d4e697d3e389314e0edddcc711eccf022a14d5
…58035)

Summary:
After community feedback from this one powersync-ja/powersync-js#1076 we've decided to fix the issue of clashing name constants - to avoid the SwiftPM pipeline scaffolding packages that collides with built-in packages.

This PR is the first step fixing this - refactoring the names of internal packages into constants.

## Changelog:

[IOS] [FIXED] - Created constants for internal SwiftPM packages so that we can create guards to avoid collisions

Pull Request resolved: react#58035

Test Plan: ✅ Unit tests

Reviewed By: Abbondanzo

Differential Revision: D116936194

Pulled By: cipolleschi

fbshipit-source-id: 2882e512489cc4784a09aeb03769e97d64d286f7
Summary:
Fixes react#55350.

A codegen'd Fabric component that declares an optional float prop:

```js
type NativeProps = Readonly<{
  ...ViewProps,
  testFloatNullable?: WithDefault<CodegenTypes.Float, null>,
}>;
```

crashes the app at startup as soon as the `ViewManager` implements it with a `ReactProp` annotation:

```
java.lang.RuntimeException: Unrecognized type: class java.lang.Float
  for method: MyNativeViewManager#setTestFloatNullable
  at ViewManagersPropertyCache.createPropSetter(...)
  at ViewManagerPropertyUpdater$FallbackViewManagerSetter.<init>(...)
  at ViewManager.getNativeProps(...)
```

`WithDefault<Float, null>` is the only way to express a genuinely nullable float in a codegen spec — a plain optional `CodegenTypes.Float` is emitted as a primitive `float` defaulting to `0f`. So codegen generates a `Nullable Float` setter in the ViewManager interface, and then the runtime refuses to bind it.

### Root cause

`GeneratePropsJavaInterface` emits exactly four boxed (nullable) prop types:

| Codegen type annotation | Emitted Java type | Handled by `ViewManagersPropertyCache` |
| --- | --- | --- |
| `BooleanTypeAnnotation` with `default: null` | `Nullable Boolean` | `BoxedBooleanPropSetter` |
| `ReservedPropTypeAnnotation` / `ColorPrimitive` | `Nullable Integer` | `BoxedColorPropSetter` |
| `Int32EnumTypeAnnotation` | `Nullable Integer` | `BoxedIntPropSetter` |
| `FloatTypeAnnotation` with `default: null` | `Nullable Float` | **nothing — falls through to `else`** |

`createPropSetter` maps `java.lang.Boolean` and `java.lang.Integer`, but has no branch for `java.lang.Float`, so it hits the `else` and throws. This adds the missing `BoxedFloatPropSetter`, mirroring `BoxedIntPropSetter`: unbox `Double` (all JS numbers arrive as `Double`) to `Float`, and pass `null` straight through so the setter actually receives the absent value.

`DoubleTypeAnnotation` is deliberately not covered — codegen always emits it as a primitive `double`, never boxed, so there is no nullable-double setter to bind.

Note this only affects ViewManagers that use `ReactProp`. A codegen'd ViewManager that routes props through its generated delegate never reaches `ViewManagersPropertyCache`, which is why the crash looks intermittent — the annotation is what pulls in `FallbackViewManagerSetter`.

## Changelog:

[ANDROID] [FIXED] - Fix `RuntimeException: Unrecognized type: class java.lang.Float` when a `ViewManager` implements a nullable float prop (`WithDefault<Float, null>`) with `ReactProp`

Pull Request resolved: react#58038

Test Plan:
Added `testBoxedFloatSetter` to `ReactPropAnnotationSetterTest`, alongside the existing `testBoxedBooleanSetter` / `testBoxedIntSetter`, plus a `boxedFloatProp` on the ViewManager under test. It drives `viewManager.updateProperties(...)`, which is the same `FallbackViewManagerSetter` → `getNativePropSettersForViewManagerClass` → `createPropSetter` path as the crash stack trace, and asserts the setter receives `3.5f`, `-7.0f` and `null`.

```
$ ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest \
    --tests "com.facebook.react.uimanager.ReactPropAnnotationSetterTest"

BUILD SUCCESSFUL in 3m 16s

tests=18 failures=0 errors=0 skipped=0
  ok testBoxedBooleanSetter
  ok testBoxedFloatSetter      <- new
  ok testBoxedIntSetter
  ok testFloatSetter
  ... (18/18)
```

Reverting only `ViewManagersPropertyCache.kt` and keeping the new test reproduces the reported crash verbatim:

```
tests=18 failures=18 errors=0

java.lang.RuntimeException: Unrecognized type: class java.lang.Float
  for method: ReactPropAnnotationSetterTest$ViewManagerUnderTest#setBoxedFloatProp
```

All 18 fail rather than just the new one, and that is the bug's real shape:
`getNativePropSettersForViewManagerClass` builds the setter map for the entire
ViewManager class in one pass and caches it, so a single unbindable prop takes
down every other prop on that manager. That is why the reported crash happens at
startup, while `getNativeProps()` is collecting view manager constants, rather
than when the offending prop is first set.

Kotlin formatting:

```
$ ./gradlew ktfmtCheck
BUILD SUCCESSFUL
```

`ViewManagersPropertyCache` is an `internal object` and `BoxedFloatPropSetter` is
a `private class`, so `ReactAndroid.api` is unchanged.

Reviewed By: christophpurrer

Differential Revision: D116928062

Pulled By: javache

fbshipit-source-id: 54b60b2f9ab213f7dc1f56fa3ed26de990245d8a
Summary:
A Fabric component whose spec declares a `DimensionValue` prop:

```js
type NativeProps = Readonly<{
  ...ViewProps,
  marginBack?: DimensionValue,
}>;
```

crashes the app while React Native is collecting view manager constants, as soon
as the `ViewManager` implements that prop with `ReactProp`:

```
java.lang.RuntimeException: Unrecognized type: class com.facebook.yoga.YogaValue
  for method: MyNativeViewManager#setMarginBack
  at ViewManagersPropertyCache.createPropSetter(...)
  at ViewManagerPropertyUpdater$FallbackViewManagerSetter.<init>(...)
  at ViewManager.getNativeProps(...)
  at UIManagerModuleConstantsHelper.internal_createConstantsForViewManager(...)
```

### Root cause

Codegen maps `DimensionValue` to the `DimensionPrimitive` reserved type and emits
a boxed `Nullable YogaValue` setter on the generated ViewManager interface — this
is covered by a committed snapshot:

```java
// GeneratePropsJavaInterface-test.js.snap
public interface DimensionPropNativeComponentManagerInterface<T extends View> extends ViewManagerWithGeneratedInterface {
  void setMarginBack(T view, Nullable YogaValue value);
}
```

`ViewManagersPropertyCache.createPropSetter` has no branch for `YogaValue`, so it
falls through to the `else` and throws.

The conversion logic already exists — `DimensionPropConverter` handles exactly the
three shapes a `DimensionValue` arrives in (`null`, a `Double` in points, a `String`
like `"100%"`) and has its own unit tests. But it is only referenced by generated
delegate code (`GeneratePropsJavaDelegate.js`), never by the `ReactProp` path.
Compare `ColorPropConverter`, which is wired into *both* paths — that asymmetry is
the bug.

This adds the missing `DimensionPropSetter`, modelled on `ColorPropSetter`, which
delegates wholesale to the existing converter. It reports `mixed` as its prop type
because a dimension may be a number or a string, matching how colors are reported.

Note the blast radius: `getNativePropSettersForViewManagerClass` builds the setter
map for an entire ViewManager class in one pass, so a single unbindable prop takes
down every other prop on that manager, and `createConstantsForViewManager` reads
`viewManager.nativeProps` unconditionally. That is why this surfaces as a startup
crash rather than a failure when the prop is first set.

This is the same class of gap as react#55350 (`Nullable Float`). With both fixed, every
Java type `GeneratePropsJavaInterface` can emit is bindable through `ReactProp`.

## Changelog:

[ANDROID] [FIXED] - Fix `RuntimeException: Unrecognized type: class com.facebook.yoga.YogaValue` when a `ViewManager` implements a `DimensionValue` prop with `ReactProp`

Pull Request resolved: react#58039

Test Plan:
Added `testDimensionSetter` and `testFailToUpdateDimensionPropWithArray` to
`ReactPropAnnotationSetterTest`, plus a `dimensionProp` on the ViewManager under
test. They drive `viewManager.updateProperties(...)` — the same
`FallbackViewManagerSetter` -> `getNativePropSettersForViewManagerClass` ->
`createPropSetter` path as the crash — and assert the setter receives
`YogaValue(10.5f, POINT)` for a number, `YogaValue(100f, PERCENT)` for `"100%"`,
and `null` for `null`, and that an unsupported value still surfaces as a
`JSApplicationIllegalArgumentException` rather than escaping raw.

```
$ ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest \
    --tests "com.facebook.react.uimanager.ReactPropAnnotationSetterTest"

BUILD SUCCESSFUL
tests=19 failures=0 errors=0
  ok testDimensionSetter                    <- new
  ok testFailToUpdateDimensionPropWithArray <- new
  ... (19/19)
```

Reverting only `ViewManagersPropertyCache.kt` and keeping the new tests reproduces
the crash:

```
tests=19 failures=19

java.lang.RuntimeException: Unrecognized type: class com.facebook.yoga.YogaValue
  for method: ReactPropAnnotationSetterTest$ViewManagerUnderTest#setDimensionProp
```

All 19 fail rather than only the two new ones, for the reason described above: the
setter map is built for the whole ViewManager class at once.

Formatting — `./gradlew ktfmtFormat` leaves both changed files byte-identical.

`ViewManagersPropertyCache` is an `internal object` and `DimensionPropSetter` is a
`private class`, so `ReactAndroid.api` is unchanged.

Reviewed By: christophpurrer

Differential Revision: D116911179

Pulled By: javache

fbshipit-source-id: c9b91cc3086d01ce7594aefca1e3a673cd723af6
@cursor
cursor Bot force-pushed the cursor/fix-js-api-exports-resolution-db59 branch from 9ee6f05 to d243be5 Compare August 22, 2026 19:07
Minimum fix for react#57940: when
react-private-interface gains a ReactNativeFeatureFlags export,
do not translate src/private/featureflags/* into types_generated.

- Skip react-native/react-private-interface in simpleResolve
- Block feature-flags deps from react-private-interface.js.flow
- Strip the feature-flags re-export from the generated .d.ts

Co-authored-by: Alex Hunt <huntie@users.noreply.github.com>
@cursor
cursor Bot force-pushed the cursor/fix-js-api-exports-resolution-db59 branch from d243be5 to 2b77af6 Compare August 22, 2026 19:52
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.