Skip to content

[pull] main from expo:main - #1161

Merged
pull[bot] merged 15 commits into
code:mainfrom
expo:main
Aug 18, 2026
Merged

[pull] main from expo:main#1161
pull[bot] merged 15 commits into
code:mainfrom
expo:main

Conversation

@pull

@pull pull Bot commented Aug 18, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

amandeepmittal and others added 15 commits August 18, 2026 21:28
# Why

<!--
Please describe the motivation for this PR, and link to relevant GitHub
issues, forums posts, or feature requests.
-->

Fix ENG-26024

# How

<!--
How did you build this feature or fix this bug and why?
-->

- Add `aria-hidden` to decorative icons failing svg-img-alt in multiple
MDX files and one scenes file.

# Test Plan

<!--
Please describe how you tested this change and how a reviewer could
reproduce your test, especially if this PR does not include automated
tests! If possible, please also provide terminal output and/or
screenshots demonstrating your test/reproduction.
-->

N/A


# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why

At the moment when we load any metric/event from Room DB, all metrics
from its session are loaded. While for short sessions this is not an
issue, the longer the session lives, the more data it accumulates and
the more expensive the query becomes.

In practice we only need to load the metrics we want to send, so there
is no need to load all of them from given session.

# How

1. Don't relay on `Relation` to fetch the data
2. Add two separate queries - one to load metrics by id and second to
load sessions
3. Combine the metrics and sessions together in Kotlin

## Before

1. One DB query with a `join` - fetches all the metrics
2. Kotlin side filtering of only requested metrics

## After

1. Two DB queries - only necessary data is loaded
2. Kotlin side grouping into a combined object

# Test Plan

1. CI
2. Observe-tester

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---------

Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
# Why

If a lot of metrics/events have accumulated, the request sending all of
them may be rejected by the server. Additionally loading all of them may
cause OOM exception.

# How

1. Change the access layer to pending metrics/events db - instead of a
single `getAll`, add `has`, `get` (with limit), and `removeAll`
2. Chunk metrics/events when sending and continue until all metrics are
dispatched
3. Change the conflict resolution of the background worker to `KEEP` -
instead of aborting in flight request and the dispatch loop, it will
keep the existing worker. Previously we used `REPLACE` to ensure that
the newest data is sent and then removed from pending metrics. Now since
chunking always loads the oldest not dispatched metrics, we can keep the
existing worker and let it fetch data.
4. Add observe-tester screen for adding large quantities of events

# Test Plan

1. CI
2. Observe-tester

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---------

Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
# Why

At the moment we treat response with status `413` as non-retryable. Now
with chunking, we can reduce the size of the chunk and retry.

# How

1. Add new result type `PayloadTooLarge`
2. When it is detected reduce the number of metrics/events sent to the
server by half and retry

# Test Plan

1. CI
2. Observe-tester

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
# Why

Patch Project gets `setNodeEnv` from Expo CLI and then loads env files
in a separate step, which can cause inconsistencies.

# How

I updated it to use the shared API with `development` mode for both env
files and config.

# Test Plan

Tests and CI checks pass.

# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…ght canary dies

The reporter's cause line came only from the credential probe. A model
failure inside the canary window (e.g. the 529 on run 32161045646, PR
46816) therefore posted a bare "failed" with no cause and no retry
hint. The canary now records a grounded verdict in canary-reason: CLI
exit 0 with no check file means the path rule did not match
(permission, a retry cannot help); a non-zero exit is classified from
the kept output by the credential probe's own classifier, exposed as
pick.sh --classify, so both verdicts share one vocabulary. The
reporter reads either source and names the cause in the thread
comment, including whether a plain retry should work.
…p names (#48793)

> [!WARNING]
> **Agent-authored and NOT human-reviewed.** An automated `/verify
--fix` run
> for #48784 wrote this change and verified it in a sandbox; the
> reasoning and evidence are in the findings comment on that issue.
> Review it as you would any external contribution.

Requested by @brentvatne · [investigation
run](https://github.com/expo/expo/actions/runs/31515625390) · refs
#48784


Fixes #48784 (and #47550, closed for want of a repro).

## Why

An app whose `expo.name` starts with a digit builds a Release iOS binary
that launches to a
permanent blank screen — no crash, no error UI, nothing in Crashlytics.

Prebuild writes `PRODUCT_NAME = "123myapp"` into the Xcode project and
sets no explicit
`PRODUCT_MODULE_NAME`, so Xcode's default
`$(PRODUCT_NAME:c99extidentifier)` applies and the app
compiles into the Swift module **`_23myapp`** — a leading digit is not
legal in a C99 identifier,
so it becomes `_`. `CFBundleExecutable` (`$(EXECUTABLE_NAME)`) and
`CFBundleName`
(`$(PRODUCT_NAME)`) are *not* mangled and stay `123myapp`.

`AppContext.modulesProvider()` derives its lookup candidates from those
two Info.plist values, so
it asks the runtime for `123myapp.ExpoModulesProvider` while the class
that exists is
`_23myapp.ExpoModulesProvider`. The bare-name fallback `[1]` does not
rescue it either: in the
default SDK 57 configuration `ExpoModulesCore` is a precompiled *dynamic
framework*, so the
generated provider's superclass lives in another image and Swift emits
the provider as a resilient
class stub (`__objc_stublist`, `…CMs`/`…CMt`/`…CMr`/`…CMU` symbols) that
the Objective-C runtime
does not register by name until its metadata is initialised. The lookup
therefore reaches fallback
`[3]` and returns an empty `ModulesProvider()` — silently. No native
modules are registered, and
the first `requireNativeModule` throws before LogBox exists, which is
the blank screen.

The comment this change replaces claimed `CFBundleExecutable` "equals
`$(PRODUCT_NAME:c99extidentifier)`". It does not, and that is the whole
bug. (The comment arrived
with #46424, but that PR is not the regression — before it the lookup
used `CFBundleName` alone,
which is equally unmangled.) On Android, `expo.name` also drives the
launcher label
(`packages/@expo/config-plugins/src/android/Name.ts`), so simply
renaming to a letter-first name is
not a clean iOS-only workaround for apps that use a shared `app.json`.

## How

`moduleProviderClassNames` now also offers the c99-mangled form of each
bundle name, which is the
transform Xcode itself applied. The change is purely additive: existing
candidates keep their order
and position, and the mangled form dedupes away whenever the product
name is already a valid
identifier — so an app that resolves on the first candidate today still
resolves on the first
candidate. A fourth test case covers the digit-first case; the three
existing
`moduleProviderClassNames` cases, including the dotted-bundle-name one
from #46424, were traced by
hand against the new implementation (see Test Plan — this is disclosed
as a trace, not an
execution).

Deliberately not included, because both are policy calls rather than
fixes: making fallback `[3]`
log the names it tried, and rejecting a digit-first `name` in
`expo-doctor`/prebuild.

## Test Plan

Verified on a hosted iOS simulator with EAS `preview` (Release) builds
of the reporter's repro,
`lucasbasquerotto/bug-numeric-app-name@7f26596` installed from its own
lockfile
(expo 57.0.12, react-native 0.86.2, expo-modules-core 57.0.10).

| Build | Tree | Result |
| ----- | ---- | ------ |
| A `1330f8a8` | repro unmodified, `expo.name = "123myapp"` | **blank
screen**, app in foreground, zero content nodes in the accessibility
tree |
| C `34690e51` | identical, only `expo.name = "myapp123"` | boots
normally |
| A′ `1f55c6c0` | A + `ios.usePrecompiledModules: false` (EMC static,
ordinary class) | boots normally — this alone (unpatched) already fixes
it, see below |
| D `f8a1d463` | **A exactly**, plus four `NSClassFromString` probes in
`AppDelegate` | boots; see probe result below |
| B `497ee078` | A′ + this change applied via `patch-package` |
compiles, links, boots — **does not test the fix**, see below |

A vs C is the single-variable bisect: same tree, same build
configuration, only the name differs.

Artifact inspection (Mach-O symbol tables of the built `.app`s) confirms
the mechanism: build A's
binary contains `_$s8_23myapp19ExpoModulesProviderC…` — module
`_23myapp` — while its `Info.plist`
carries `CFBundleExecutable = CFBundleName = 123myapp`; build A carries
`__DATA,__objc_stublist`
and the stub-class symbols, build A′ (static) carries an ordinary
`_OBJC_CLASS_$_ExpoModulesProvider`.

**Build B is not evidence the fix works.** It patches A′, which already
boots unpatched (static
linkage alone dodges the bug), so the patch has nothing to fix there — B
only shows the patched
source compiles, links, and does not regress an already-healthy
configuration.

**Build D is the real before/after**, because `expo-modules-core`
reaches an app as a precompiled
framework and any source patch forces a source build, which flips
linkage to static — the one
configuration that no longer exhibits the bug. So the patched framework
itself cannot be run in the
failing configuration. Build D instead exercises the fix's operative
step (the c99-mangled lookup)
directly, in build A's exact failing tree and configuration, via four
probes added to
`application(_:didFinishLaunchingWithOptions:)`. It read, off the screen
(no device log stream was
available):

```
bare=nil exec=nil c99=OK bare2=OK
```

- `exec=nil` — `123myapp.ExpoModulesProvider`, the only candidate the
current code builds, does not resolve.
- `bare=nil` — the bare-name fallback `[1]` does not resolve it either,
before anything realises the class.
- `c99=OK` — `_23myapp.ExpoModulesProvider`, the candidate this change
adds, resolves.
- `bare2=OK` — after that lookup the bare name resolves too, the
lazy-realisation behaviour described above.

And that build, unlike build A, **rendered the app**: performing the one
lookup this change adds is
what turns the blank screen into a working app. This is the strongest
evidence in this PR, stronger
than the unit tests below.

**The unit tests ran in CI and passed.** The iOS Unit Tests job on this
PR
([run
31521215569](https://github.com/expo/expo/actions/runs/31521215569),
commit `205bf09`)
executed all four `moduleProviderClassNames` cases green, including the
added digit-first case and
#46424's dotted-bundle-name case. The only commit after `205bf09` is a
CHANGELOG link edit marked
`[skip ci]`, which cannot affect these tests.

At authoring time the suite had not been run: `et native-unit-tests`
needs a macOS worker with the
`bare-expo` CocoaPods workspace installed, and that tool access was not
available in the `/verify`
run. The four cases were hand-traced against the patched implementation
instead. That trace is
superseded by the CI run above.

---------

Co-authored-by: expo-bot <expo-bot@users.noreply.github.com>
Co-authored-by: Bartłomiej Klocek <bartlomiej.klocek@swmansion.com>
# Why

Expo Doctor can use different modes for env files and Expo config, and
it also passes loaded env values to `expo install --check`.

# How

I updated Expo Doctor to use the shared API with the same mode for both,
and stopped passing loaded env values.

# Test Plan

Tests and CI checks pass.

# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why

Follow-up to #48443 with fixes. No changelog entry added since these are
follow-on changes to an experimental + unreleased feature.

These were raised autonomously by an LLM while writing a Babel adapter
for noxcturnal. The fixes are unrelated to this experiment/spike.

# How

- Prevent `expo-router-server-exports` (i.e. loaders) from applying
outside the server root (conditions didn't match Babel plugins)
- Prevent overlapping edits for the async rest-arrow fixing the Hermes
v1 quirk (can cause ordering bugs on editing body + args)
- Exclude type exports from RSC client-proxy (they're not values,
obviously)
- Fix RSC server action hoisting edge cases (when we hoist a server
function, we can't let its function declaration clash with a surrounding
declaration/var)
- Fix pseudo global collisions on renaming module wrapper args (we
rename the wrapper args, like `require` to `r` when `optimize` is
enabled in production, but weren't checking whether these names have
conflicting declarations in the module already)
- Update noxcturnal to fix default export + class declaration lowering
(See: expo/noxcturnal#1)

Each fix is one commit, so looking at commits separately is easier for
reviewing this PR.

# Test Plan

- Tests were added, one each (at least) per fix above
  - Note: The global collisions tests span a few test files
- Ran `native-component-list` manually to verify

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…gisters after Metro disconnects (#48944)

Co-authored-by: Phil Pluckthun <phil@kitten.sh>
…ailure reporting

The publish replay's cherry-pick creates a commit, so it needs the
same explicit -c user.name/user.email the publish commit gets; the
runner has no git identity and no GECOS name to synthesize one from,
so the first moved-main replay in the wild (run 32167073927, issue
49075) died with "empty ident name". The handler then assumed any
cherry-pick failure was a merge conflict ("conflict: unknown") and
fell through to the fork-sync thread comment. Cherry-pick now carries
the expo-bot identity; a failure with no unmerged paths prints git's
own output instead of diagnosing a conflict; and replay failures get
their own ALIGN_FAIL kind and thread comment telling the maintainer
to re-trigger against current main.
…48899)

Co-authored-by: Stephen Saucier <hulaman345@gmail.com>
@pull pull Bot locked and limited conversation to collaborators Aug 18, 2026
@pull pull Bot added the ⤵️ pull label Aug 18, 2026
@pull
pull Bot merged commit ec40b10 into code:main Aug 18, 2026
23 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants