Skip to content

[pull] main from expo:main - #1167

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

[pull] main from expo:main#1167
pull[bot] merged 14 commits into
code:mainfrom
expo:main

Conversation

@pull

@pull pull Bot commented Aug 20, 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 : )

Ubax and others added 14 commits August 20, 2026 11:59
# Why

For some actions properties were not added to the state, even though
they should

# How

In each router use `ensureStateType` function to add `type` if it is
missing.

For TabRouters ensure that history is always added

# Test Plan

1. CI
2. Router e2e

# 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

After changing how protected routes work, I often see agents claiming
that they still work based of `routeNames`. I face similar problem
regarding the `routeNames`, where agents claim users can change them by
hand - which they can't do in expo-router anymore.

# How

Update AGENTS.md 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.
-->

# 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)
#49147)

# Why

A prebuilt pod exposes its headers only inside its XCFramework, so a pod
that reads them from `Pods/Headers/Public/<dep>` cannot compile against
a prebuilt dependency.

`RNReanimated.podspec` hardcodes that path for RNWorklets, so a
source-built RNReanimated failed with `'worklets/Compat/StableApi.h'
file not found` whenever RNWorklets was prebuilt.

The reason for the source build of Reanimated and not Worklets happening
is that we only check dependencies one way - so if RNWorklets are source
built, Reanimated will also be - but the opposite is not detected.

# How

This PR fixes this by adding another map that contains the other
direction for the dependency graph - making sure we also build
RNWorklets as source if Reanimated is source built.

# Test Plan

Test | Assertion | Result
-- | -- | --
Control | stock versions all prebuilt | 9/9 📦, no fallbacks
Natural drift | reanimated 4.5.0 (404) + worklets 0.10.1 → cascade,
compiles | HTTP 404 → cascade → BUILD SUCCEEDED
@expo/ui | no cascade, list unchanged | 9/9 📦, identical to
control, BUILD SUCCEEDED

# 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)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
…amps. (#49141)

# Why

Similar to metrics, there is a problem with millisecond precision when
converting events' timestamps

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

# How

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

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

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>

---------

Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
…49121)

# Why

Follow-up to #49016 stack

# How

1. Batch events into 200 chunks
2. Retry the dispatch when 413 is received
3. Extract the dispatching logic into `DispatchLoop`

# 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>
…g item (#49128)

# Why

On iOS, `keychainAccessible` is silently ignored on every write to a key
that already exists. The item keeps the accessibility class it was first
created with, for the life of the install.

Because the default is `kSecAttrAccessibleWhenUnlocked`, an app that
later decides its data must survive a locked screen has no way to get
there through the public API. Calling `setItem(key, value, {
keychainAccessible: AFTER_FIRST_UNLOCK })` looks like it worked — it
returns normally and the value really is updated — but reads behind a
locked screen keep failing with `errSecInteractionNotAllowed` ("User
interaction is not allowed."). There is no error and no warning to
suggest the option did not take.

We hit this in an app that records audio in the background. With
`UIBackgroundModes: ["audio"]` the JS thread keeps running while the
phone is locked in someone's pocket, so a timer read the keychain, got
`errSecInteractionNotAllowed`, and the throw took the app down
mid-recording. Setting `keychainAccessible` was the obvious fix and it
changed nothing, which took a while to explain.

Closed issue #23924 reports the same `User interaction is not allowed`
symptom; I could not find an existing issue for this cause.

# How

`set(value:with:options:)` tries `SecItemAdd` first and puts
`kSecAttrAccessible` in that dictionary, so a **new** key gets the
requested class. An existing key returns `errSecDuplicateItem` and falls
through to `update(value:with:options:)`, whose `SecItemUpdate`
attributes dictionary contains `kSecValueData` and nothing else:

```swift
let updateDictionary = [kSecValueData as String: valueData]
```

`query(with:options:requireAuthentication:)` does not name
`kSecAttrAccessible` either, so the item is found by service/account,
the value is replaced, and the accessibility attribute is left exactly
as it was.

This keeps `keychainAccessible` optional in the native options record so
omission remains distinguishable from the default. New items still
default to `kSecAttrAccessibleWhenUnlocked`; updates include the
attribute only when the caller supplied it:

```swift
let updateDictionary = if !options.requireAuthentication, options.keychainAccessible != nil {
  [
    kSecValueData: valueData,
    kSecAttrAccessible: attributeWith(options: options)
  ] as CFDictionary
} else {
  [kSecValueData: valueData] as CFDictionary
}
```

Only the unauthenticated path applies it, mirroring `set`, where an
authenticated item carries its accessibility inside
`kSecAttrAccessControl` rather than `kSecAttrAccessible`. Updating that
in place would mean rebuilding a `SecAccessControl` and has
biometric-enrollment implications, so I left it alone — happy to follow
up if you would like it covered too.

Behaviour is unchanged for new keys, for reads, and for any call that
does not pass `keychainAccessible`: new items still receive the
documented `kSecAttrAccessibleWhenUnlocked` default, while existing
items retain the accessibility class with which they were stored.

# Test Plan

I want to be straightforward about what is and is not verified here: the
diagnosis comes from reading `SecureStoreModule.swift`, and I have not
yet run a build of this branch on a device. I am opening it because the
cause looks unambiguous in the source and the reproduction is cheap for
anyone with the module already set up. Happy to come back with device
output before you merge — say the word and I will.

The reproduction, on a physical device (a simulator will not do — it
needs a real lock screen):

```js
// create the item the default way, then try to upgrade it in place
await SecureStore.setItemAsync('probe', 'hello');
await SecureStore.setItemAsync('probe', 'hello', {
  keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
});
```

Then read `probe` from something that keeps running once the phone is
locked — an app with an active audio session and `UIBackgroundModes:
["audio"]` is the easiest, which is how we ran into it:

- **before this change** — `getValueWithKeySync` throws
`KeyChainException: User interaction is not allowed.`, because the item
is still `kSecAttrAccessibleWhenUnlocked` despite the second write.
- **after this change** — the read returns `hello`.

`deleteItemAsync` followed by a fresh `setItemAsync` with the same
option succeeds on both builds, which is the workaround this is meant to
make unnecessary, and is also the evidence that the class itself is
applied correctly on an add.

Worth a reviewer's eye specifically: whether a plain `setItemAsync(key,
value)` on an existing key is genuinely unaffected. It should be — the
omitted option remains `nil`, so the update dictionary contains only
`kSecValueData` and leaves the existing accessibility unchanged — but
that is reasoning, not a measurement.

# 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)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [x] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

---------

Co-authored-by: Wojciech Dróżdż <behenate@gmail.com>
# Why

Use hermes for all router-e2e apps

# How

Remove the `E2E_ROUTER_JS_ENGINE` env which was nearly always set to
`hermes` anyway

# Test Plan

CI

# 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

`doNotMix` uses transient audio focus, allowing interrupted audio from
other apps to resume after focus is released. Add a persistent mode for
apps that need exclusive focus without triggering automatic resumption.

# How

Add `doNotMixPersistent` across platforms. Android uses
`AUDIOFOCUS_GAIN` and refreshes active or delayed focus requests when
the mode changes. iOS uses the non-mixing category without
`.notifyOthersOnDeactivation`. Preserve playback intent, paused state,
and ducked volume throughout focus changes.

There is a lot more changes on Android - the automated review I was
running locally kept finding a lot of related or semi-related edge
cases. Right now the focus management seems to be working quite well.

# Test Plan

Tested manually in NCL on iOS and Android by interrupting other audio
source playing in the background.
# Why

Resolves ENG-26083
Resolves ENG-25696

We want to publish what we can with error reporting.

> [!IMPORTANT]
> Do not merge until we've enabled the errors page on the dash FYI
@entiendoNull

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

# How

Document setup instructions and mark the feature as "in preview".

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

# Test Plan

Review the docs.

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

# 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: Aman Mittal <amandeepmittal@live.com>
# Why

this adds missing docs for extra fields that we support in the push
service

# How

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

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

# 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)
@pull pull Bot locked and limited conversation to collaborators Aug 20, 2026
@pull pull Bot added the ⤵️ pull label Aug 20, 2026
@pull
pull Bot merged commit 172eb3f into code:main Aug 20, 2026
22 of 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.

8 participants