From 786e429732c1dff9774bef2858a4ba9a26f15562 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Norte?=
Date: Tue, 12 May 2026 10:55:06 +0100
Subject: [PATCH 01/11] [expo-router] Name suspense boundary used in useScreens
(#45501)
# Why
Naming suspense boundaries is generally a good practice for debugging.
Also, with transition tracing and other tracing solutions for React,
suspense boundary names can help identify the source of an async
operation.
# How
Just added the name in the suspense boundary, using the route name if
available.
# Test Plan
CI
# 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)
---
packages/expo-router/CHANGELOG.md | 2 ++
packages/expo-router/build/useScreens.d.ts.map | 2 +-
packages/expo-router/build/useScreens.js | 2 +-
packages/expo-router/build/useScreens.js.map | 2 +-
packages/expo-router/src/useScreens.tsx | 1 +
5 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md
index 3dd905f5b2a718..63ba07774d6607 100644
--- a/packages/expo-router/CHANGELOG.md
+++ b/packages/expo-router/CHANGELOG.md
@@ -6,6 +6,8 @@
### đ New features
+- Add a name to injected suspense boundaries for easier debugging ([#45501](https://github.com/expo/expo/pull/45501) by [@rubennorte](https://github.com/rubennorte))
+
### đ Bug fixes
### đĄ Others
diff --git a/packages/expo-router/build/useScreens.d.ts.map b/packages/expo-router/build/useScreens.d.ts.map
index 795ab229af2d57..7660d1e4f1a5c0 100644
--- a/packages/expo-router/build/useScreens.d.ts.map
+++ b/packages/expo-router/build/useScreens.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"useScreens.d.ts","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAEvD,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,SAAS,CAAC;AAetD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQnD,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1D,MAAM,SAAS,eAAe,GAAG,eAAe,EAChD,SAAS,SAAS,YAAY,GAAG,YAAY,IAC3C;IACF,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,OAAO,CAAC,EACJ,QAAQ,GACR,CAAC,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,KAAK,QAAQ,CAAC,CAAC;IAEvF,SAAS,CAAC,EACN,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,GAClC,CAAC,CAAC,IAAI,EAAE;QACN,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACxC,UAAU,EAAE,GAAG,CAAC;KACjB,KAAK,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAE9C,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IAE7E,mBAAmB,CAAC,EAAE,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;AA6FxE;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAAE,EACpB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,EAC7B,yBAAyB,GAAE,OAAe,GACzC,KAAK,CAAC,SAAS,EAAE,CA6BnB;AAsDD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS;sCAyCtD;QACD,KAAK,CAAC,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACzC,UAAU,EAAE,IAAI,CACd,cAAc,CACZ,aAAa,EACb,MAAM,EACN,SAAS,EACT,eAAe,EACf,MAAM,EACN,6BAA6B,GAAG,2BAA2B,CAC5D,EACD,UAAU,CACX,GAAG;YACF,QAAQ,IAAI,eAAe,GAAG,SAAS,CAAC;SACzC,CAAC;KACH;;EAsFF;AA+ED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,EAChB,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,GAC/B,WAAW,CAAC,SAAS,CAAC,CAqBxB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAE,OAAO,CAAC,WAAW,CAAM,2CAYxD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,UAa5E"}
\ No newline at end of file
+{"version":3,"file":"useScreens.d.ts","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAEvD,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,SAAS,CAAC;AAetD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQnD,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1D,MAAM,SAAS,eAAe,GAAG,eAAe,EAChD,SAAS,SAAS,YAAY,GAAG,YAAY,IAC3C;IACF,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,OAAO,CAAC,EACJ,QAAQ,GACR,CAAC,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,KAAK,QAAQ,CAAC,CAAC;IAEvF,SAAS,CAAC,EACN,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,GAClC,CAAC,CAAC,IAAI,EAAE;QACN,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACxC,UAAU,EAAE,GAAG,CAAC;KACjB,KAAK,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAE9C,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IAE7E,mBAAmB,CAAC,EAAE,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;AA6FxE;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAAE,EACpB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,EAC7B,yBAAyB,GAAE,OAAe,GACzC,KAAK,CAAC,SAAS,EAAE,CA6BnB;AAsDD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS;sCAyCtD;QACD,KAAK,CAAC,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACzC,UAAU,EAAE,IAAI,CACd,cAAc,CACZ,aAAa,EACb,MAAM,EACN,SAAS,EACT,eAAe,EACf,MAAM,EACN,6BAA6B,GAAG,2BAA2B,CAC5D,EACD,UAAU,CACX,GAAG;YACF,QAAQ,IAAI,eAAe,GAAG,SAAS,CAAC;SACzC,CAAC;KACH;;EAuFF;AA+ED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,EAChB,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,GAC/B,WAAW,CAAC,SAAS,CAAC,CAqBxB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAE,OAAO,CAAC,WAAW,CAAM,2CAYxD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,UAa5E"}
\ No newline at end of file
diff --git a/packages/expo-router/build/useScreens.js b/packages/expo-router/build/useScreens.js
index 3c9b5d74e88774..14801e7e8e47d5 100644
--- a/packages/expo-router/build/useScreens.js
+++ b/packages/expo-router/build/useScreens.js
@@ -248,7 +248,7 @@ function getQualifiedRouteComponent(value) {
}, [navigation]);
const isRouteType = value.type === 'route';
const hasRouteKey = !!route?.key;
- return ((0, jsx_runtime_1.jsx)(Route_1.Route, { node: value, params: route?.params, children: (0, jsx_runtime_1.jsxs)(Route_1.SuspenseFallbackContext, { value: providedSuspenseFallback, children: [navigationEvents_1.unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && ((0, jsx_runtime_1.jsx)(AnalyticsListeners, { navigation: navigation, screenId: route.key })), (0, jsx_runtime_1.jsxs)(zoom_transition_context_providers_1.ZoomTransitionTargetContextProvider, { route: route, children: [(0, jsx_runtime_1.jsx)(ZoomTransitionEnabler_1.ZoomTransitionEnabler, { route: route }), (0, jsx_runtime_1.jsx)(react_2.default.Suspense, { fallback: (0, jsx_runtime_1.jsx)(ResolvedSuspenseFallback, { route: value.contextKey, params: (route?.params ?? {}) }), children: (0, jsx_runtime_1.jsx)(WrappedScreenComponent, { ...props,
+ return ((0, jsx_runtime_1.jsx)(Route_1.Route, { node: value, params: route?.params, children: (0, jsx_runtime_1.jsxs)(Route_1.SuspenseFallbackContext, { value: providedSuspenseFallback, children: [navigationEvents_1.unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && ((0, jsx_runtime_1.jsx)(AnalyticsListeners, { navigation: navigation, screenId: route.key })), (0, jsx_runtime_1.jsxs)(zoom_transition_context_providers_1.ZoomTransitionTargetContextProvider, { route: route, children: [(0, jsx_runtime_1.jsx)(ZoomTransitionEnabler_1.ZoomTransitionEnabler, { route: route }), (0, jsx_runtime_1.jsx)(react_2.default.Suspense, { name: route ? `Route(${route.name})` : undefined, fallback: (0, jsx_runtime_1.jsx)(ResolvedSuspenseFallback, { route: value.contextKey, params: (route?.params ?? {}) }), children: (0, jsx_runtime_1.jsx)(WrappedScreenComponent, { ...props,
// Expose the template segment path, e.g. `(home)`, `[foo]`, `index`
// the intention is to make it possible to deduce shared routes.
segment: value.route }) })] })] }) }));
diff --git a/packages/expo-router/build/useScreens.js.map b/packages/expo-router/build/useScreens.js.map
index 3ffe1b7c4ba8ef..4f38cf8b380892 100644
--- a/packages/expo-router/build/useScreens.js.map
+++ b/packages/expo-router/build/useScreens.js.map
@@ -1 +1 @@
-{"version":3,"file":"useScreens.js","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport React, { use, useEffect, useMemo } from 'react';\n\nimport type { LoadedRoute, RouteNode } from './Route';\nimport { SuspenseFallbackContext, Route, sortRoutesWithInitial, useRouteNode } from './Route';\nimport { useExpoRouterStore } from './global-state/storeContext';\nimport { useColorSchemeChangesIfNeeded } from './global-state/utils';\nimport EXPO_ROUTER_IMPORT_MODE from './import-mode';\nimport { ZoomTransitionEnabler } from './link/zoom/ZoomTransitionEnabler';\nimport { ZoomTransitionTargetContextProvider } from './link/zoom/zoom-transition-context-providers';\nimport { unstable_navigationEvents } from './navigationEvents';\nimport { generateStringUrlForState, getPathAndParamsFromStringUrl } from './navigationEvents/utils';\nimport {\n hasParam,\n INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME,\n removeParams,\n} from './navigationParams';\nimport { Screen } from './primitives';\nimport type { BottomTabNavigationEventMap } from './react-navigation/bottom-tabs';\nimport {\n useStateForPath,\n type EventConsumer,\n type EventMapBase,\n type NavigationProp,\n type NavigationState,\n type ParamListBase,\n type RouteProp,\n type ScreenListeners,\n} from './react-navigation/native';\nimport type { NativeStackNavigationEventMap } from './react-navigation/native-stack';\nimport type { UnknownOutputParams } from './types';\nimport { EmptyRoute } from './views/EmptyRoute';\nimport {\n SuspenseFallback as DefaultSuspenseFallback,\n type SuspenseFallbackProps,\n} from './views/SuspenseFallback';\nimport { Try } from './views/Try';\n\nexport type ScreenProps<\n TOptions extends Record = Record,\n TState extends NavigationState = NavigationState,\n TEventMap extends EventMapBase = EventMapBase,\n> = {\n /** Name is required when used inside a Layout component. */\n name?: string;\n /**\n * Redirect to the nearest sibling route.\n * If all children are `redirect={true}`, the layout will render `null` as there are no children to render.\n */\n redirect?: boolean;\n initialParams?: Record;\n options?:\n | TOptions\n | ((prop: { route: RouteProp; navigation: any }) => TOptions);\n\n listeners?:\n | ScreenListeners\n | ((prop: {\n route: RouteProp;\n navigation: any;\n }) => ScreenListeners);\n\n getId?: ({ params }: { params?: Record }) => string | undefined;\n\n dangerouslySingular?: SingularOptions;\n};\n\nexport type SingularOptions =\n | boolean\n | ((name: string, params: UnknownOutputParams) => string | undefined);\n\nfunction getSortedChildren(\n children: RouteNode[],\n order: ScreenProps[] = [],\n initialRouteName?: string\n): { route: RouteNode; props: Partial }[] {\n if (!order?.length) {\n return children\n .sort(sortRoutesWithInitial(initialRouteName))\n .map((route) => ({ route, props: {} }));\n }\n const entries = [...children];\n\n const ordered = order\n .map(\n ({\n name,\n redirect,\n initialParams,\n listeners,\n options,\n getId,\n dangerouslySingular: singular,\n }) => {\n if (!entries.length) {\n console.warn(\n `[Layout children]: Too many screens defined. Route \"${name}\" is extraneous.`\n );\n return null;\n }\n const matchIndex = entries.findIndex(\n (child) => child.route === name || child.route === `${name}/index`\n );\n if (matchIndex === -1) {\n console.warn(\n `[Layout children]: No route named \"${name}\" exists in nested children:`,\n children.map(({ route }) => route)\n );\n return null;\n } else {\n // Get match and remove from entries\n const match = entries[matchIndex];\n entries.splice(matchIndex, 1);\n\n // Ensure to return null after removing from entries.\n if (redirect) {\n if (typeof redirect === 'string') {\n throw new Error(`Redirecting to a specific route is not supported yet.`);\n }\n return null;\n }\n\n if (getId) {\n console.warn(\n `Deprecated: prop 'getId' on screen ${name} is deprecated. Please rename the prop to 'dangerouslySingular'`\n );\n if (singular) {\n console.warn(\n `Screen ${name} cannot use both getId and dangerouslySingular together.`\n );\n }\n } else if (singular) {\n // If singular is set, use it as the getId function.\n if (typeof singular === 'string') {\n getId = () => singular;\n } else if (typeof singular === 'function' && name) {\n getId = (options) => singular(name, options.params || {});\n } else if (singular === true && name) {\n getId = (options) => getSingularId(name, options);\n }\n }\n\n return {\n route: match,\n props: { initialParams, listeners, options, getId },\n };\n }\n }\n )\n .filter(Boolean) as {\n route: RouteNode;\n props: Partial;\n }[];\n\n // Add any remaining children\n ordered.push(\n ...entries.sort(sortRoutesWithInitial(initialRouteName)).map((route) => ({ route, props: {} }))\n );\n\n return ordered;\n}\n\n/**\n * @returns React Navigation screens sorted by the `route` property.\n */\nexport function useSortedScreens(\n order: ScreenProps[],\n protectedScreens: Set,\n useOnlyUserDefinedScreens: boolean = false\n): React.ReactNode[] {\n const node = useRouteNode();\n\n const nodeChildren = node?.children ?? [];\n const children = useOnlyUserDefinedScreens\n ? nodeChildren.filter((child) =>\n order.some(\n (userDefinedScreen) =>\n userDefinedScreen.name === child.route ||\n `${userDefinedScreen.name}/index` === child.route\n )\n )\n : nodeChildren;\n\n const sorted = children.length ? getSortedChildren(children, order, node?.initialRouteName) : [];\n return React.useMemo(\n () =>\n sorted\n .filter((item) => {\n const route = item.route.route;\n return (\n !protectedScreens.has(route) && !protectedScreens.has(route.replace(/\\/index$/, ''))\n );\n })\n .map((value) => {\n return routeToScreen(value.route, value.props);\n }),\n [sorted, protectedScreens]\n );\n}\n\nfunction fromImport(\n value: RouteNode,\n { ErrorBoundary, SuspenseFallback, ...component }: LoadedRoute\n) {\n // If possible, add a more helpful display name for the component stack to improve debugging of React errors such as `Text strings must be rendered within a component.`.\n if (component?.default && __DEV__) {\n component.default.displayName ??= `${component.default.name ?? 'Route'}(${value.contextKey})`;\n }\n\n if (ErrorBoundary) {\n const Wrapped = React.forwardRef((props: any, ref: any) => {\n const children = React.createElement(component.default || EmptyRoute, {\n ...props,\n ref,\n });\n return {children};\n });\n\n if (__DEV__) {\n Wrapped.displayName = `ErrorBoundary(${value.contextKey})`;\n }\n\n return {\n default: Wrapped,\n SuspenseFallback,\n };\n }\n if (process.env.NODE_ENV !== 'production') {\n if (\n typeof component.default === 'object' &&\n component.default &&\n Object.keys(component.default).length === 0\n ) {\n return { default: EmptyRoute, SuspenseFallback };\n }\n }\n\n return { default: component.default, SuspenseFallback };\n}\n\nfunction fromLoadedRoute(value: RouteNode, res: LoadedRoute) {\n if (!(res instanceof Promise)) {\n return fromImport(value, res);\n }\n\n return res.then(fromImport.bind(null, value));\n}\n\n// TODO: Maybe there's a more React-y way to do this?\n// Without this store, the process enters a recursive loop.\nconst qualifiedStore = new WeakMap>();\n\n/** Wrap the component with various enhancements and add access to child routes. */\nexport function getQualifiedRouteComponent(value: RouteNode) {\n if (qualifiedStore.has(value)) {\n return qualifiedStore.get(value)!;\n }\n\n let ScreenComponent:\n | React.ForwardRefExoticComponent>\n | React.ComponentType<{ segment?: string }>;\n\n let LayoutSuspenseFallback: React.ComponentType | undefined;\n\n // TODO: This ensures sync doesn't use React.lazy, but it's not ideal.\n if (EXPO_ROUTER_IMPORT_MODE === 'lazy') {\n ScreenComponent = React.lazy(async () => {\n const res = value.loadRoute();\n return fromLoadedRoute(value, res) as Promise<{\n default: React.ComponentType;\n }>;\n });\n\n if (__DEV__) {\n ScreenComponent.displayName = `AsyncRoute(${value.route})`;\n }\n } else {\n const res = value.loadRoute();\n const result = fromImport(value, res);\n ScreenComponent = result.default!;\n LayoutSuspenseFallback = value.type === 'layout' ? result.SuspenseFallback : undefined;\n }\n const WrappedScreenComponent: typeof ScreenComponent = (props: object) => {\n useColorSchemeChangesIfNeeded();\n return ;\n };\n function BaseRoute({\n // Remove these React Navigation props to\n // enforce usage of expo-router hooks (where the query params are correct).\n route,\n navigation,\n\n // Pass all other props to the component\n ...props\n }: {\n route?: RouteProp;\n navigation: Omit<\n NavigationProp<\n ParamListBase,\n string,\n undefined,\n NavigationState,\n object,\n NativeStackNavigationEventMap | BottomTabNavigationEventMap\n >,\n 'getState'\n > & {\n getState(): NavigationState | undefined;\n };\n }) {\n const stateForPath = useStateForPath();\n const isFocused = navigation.isFocused();\n const store = useExpoRouterStore();\n const InheritedSuspenseFallback = use(SuspenseFallbackContext);\n\n const ResolvedSuspenseFallback =\n EXPO_ROUTER_IMPORT_MODE === 'lazy'\n ? DefaultSuspenseFallback\n : (LayoutSuspenseFallback ?? InheritedSuspenseFallback ?? DefaultSuspenseFallback);\n const providedSuspenseFallback =\n value.type === 'layout'\n ? (LayoutSuspenseFallback ?? InheritedSuspenseFallback)\n : InheritedSuspenseFallback;\n\n if (isFocused) {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }\n\n useEffect(\n () =>\n navigation.addListener('focus', () => {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n // Because setFocusedState caches the route info, this call will only trigger rerenders\n // if the component itself didnât rerender and the route info changed.\n // Otherwise, the update from the `if` above will handle it,\n // and this wonât cause a redundant second update.\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }),\n [navigation]\n );\n\n useEffect(() => {\n return navigation.addListener('transitionEnd', (e) => {\n if (!e?.data?.closing) {\n // When navigating to a screen, remove the no animation param to re-enable animations\n // Otherwise the navigation back would also have no animation\n if (hasParam(route?.params, INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME)) {\n navigation.replaceParams(\n removeParams(route?.params, [INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME])\n );\n }\n }\n });\n }, [navigation]);\n\n const isRouteType = value.type === 'route';\n const hasRouteKey = !!route?.key;\n\n return (\n \n \n {unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && (\n \n )}\n \n \n \n }>\n \n \n \n \n \n );\n }\n\n if (__DEV__) {\n BaseRoute.displayName = `Route(${value.route})`;\n }\n\n qualifiedStore.set(value, BaseRoute);\n return BaseRoute;\n}\n\nfunction AnalyticsListeners({\n navigation,\n screenId,\n}: {\n navigation: EventConsumer & {\n isFocused(): boolean;\n };\n screenId: string;\n}) {\n const stateForPath = useStateForPath();\n const isFirstRenderRef = React.useRef(true);\n const hasBlurredRef = React.useRef(true);\n const stringUrl = useMemo(() => generateStringUrlForState(stateForPath), [stateForPath]);\n\n if (isFirstRenderRef.current) {\n isFirstRenderRef.current = false;\n if (stringUrl) {\n unstable_navigationEvents.emit('pageWillRender', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n }\n }\n\n useEffect(() => {\n if (stringUrl) {\n return () => {\n unstable_navigationEvents.emit('pageRemoved', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n };\n }\n return () => {};\n }, [stringUrl, screenId]);\n\n const isFocused = navigation.isFocused();\n\n if (isFocused && stringUrl) {\n unstable_navigationEvents.emit('pageFocused', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = false;\n }\n\n useEffect(() => {\n if (stringUrl) {\n const cleanFocus = navigation.addListener('focus', () => {\n // If the screen was not blurred, don't emit focused again\n // hasBlurredRef will be false when the screen was initially focused\n if (hasBlurredRef.current) {\n unstable_navigationEvents.emit('pageFocused', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = false;\n }\n });\n const cleanBlur = navigation.addListener('blur', () => {\n unstable_navigationEvents.emit('pageBlurred', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = true;\n });\n return () => {\n cleanFocus();\n cleanBlur();\n };\n }\n return () => {};\n }, [navigation, stringUrl, screenId]);\n\n return null;\n}\n\nexport function screenOptionsFactory(\n route: RouteNode,\n options?: ScreenProps['options']\n): ScreenProps['options'] {\n return (args) => {\n // Only eager load generated components\n const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null;\n const staticResult = typeof staticOptions === 'function' ? staticOptions(args) : staticOptions;\n const dynamicResult = typeof options === 'function' ? options?.(args) : options;\n const output = {\n ...staticResult,\n ...dynamicResult,\n };\n\n // Prevent generated screens from showing up in the tab bar.\n if (route.internal) {\n output.tabBarItemStyle = { display: 'none' };\n output.tabBarButton = () => null;\n // TODO: React Navigation doesn't provide a way to prevent rendering the drawer item.\n output.drawerItemStyle = { height: 0, display: 'none' };\n }\n\n return output;\n };\n}\n\nexport function routeToScreen(\n route: RouteNode,\n { options, getId, ...props }: Partial = {}\n) {\n return (\n getQualifiedRouteComponent(route)}\n />\n );\n}\n\nexport function getSingularId(name: string, options: Record = {}) {\n return name\n .split('/')\n .map((segment) => {\n if (segment.startsWith('[...')) {\n return options.params?.[segment.slice(4, -1)]?.join('/') || segment;\n } else if (segment.startsWith('[') && segment.endsWith(']')) {\n return options.params?.[segment.slice(1, -1)] || segment;\n } else {\n return segment;\n }\n })\n .join('/');\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"useScreens.js","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport React, { use, useEffect, useMemo } from 'react';\n\nimport type { LoadedRoute, RouteNode } from './Route';\nimport { SuspenseFallbackContext, Route, sortRoutesWithInitial, useRouteNode } from './Route';\nimport { useExpoRouterStore } from './global-state/storeContext';\nimport { useColorSchemeChangesIfNeeded } from './global-state/utils';\nimport EXPO_ROUTER_IMPORT_MODE from './import-mode';\nimport { ZoomTransitionEnabler } from './link/zoom/ZoomTransitionEnabler';\nimport { ZoomTransitionTargetContextProvider } from './link/zoom/zoom-transition-context-providers';\nimport { unstable_navigationEvents } from './navigationEvents';\nimport { generateStringUrlForState, getPathAndParamsFromStringUrl } from './navigationEvents/utils';\nimport {\n hasParam,\n INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME,\n removeParams,\n} from './navigationParams';\nimport { Screen } from './primitives';\nimport type { BottomTabNavigationEventMap } from './react-navigation/bottom-tabs';\nimport {\n useStateForPath,\n type EventConsumer,\n type EventMapBase,\n type NavigationProp,\n type NavigationState,\n type ParamListBase,\n type RouteProp,\n type ScreenListeners,\n} from './react-navigation/native';\nimport type { NativeStackNavigationEventMap } from './react-navigation/native-stack';\nimport type { UnknownOutputParams } from './types';\nimport { EmptyRoute } from './views/EmptyRoute';\nimport {\n SuspenseFallback as DefaultSuspenseFallback,\n type SuspenseFallbackProps,\n} from './views/SuspenseFallback';\nimport { Try } from './views/Try';\n\nexport type ScreenProps<\n TOptions extends Record = Record,\n TState extends NavigationState = NavigationState,\n TEventMap extends EventMapBase = EventMapBase,\n> = {\n /** Name is required when used inside a Layout component. */\n name?: string;\n /**\n * Redirect to the nearest sibling route.\n * If all children are `redirect={true}`, the layout will render `null` as there are no children to render.\n */\n redirect?: boolean;\n initialParams?: Record;\n options?:\n | TOptions\n | ((prop: { route: RouteProp; navigation: any }) => TOptions);\n\n listeners?:\n | ScreenListeners\n | ((prop: {\n route: RouteProp;\n navigation: any;\n }) => ScreenListeners);\n\n getId?: ({ params }: { params?: Record }) => string | undefined;\n\n dangerouslySingular?: SingularOptions;\n};\n\nexport type SingularOptions =\n | boolean\n | ((name: string, params: UnknownOutputParams) => string | undefined);\n\nfunction getSortedChildren(\n children: RouteNode[],\n order: ScreenProps[] = [],\n initialRouteName?: string\n): { route: RouteNode; props: Partial }[] {\n if (!order?.length) {\n return children\n .sort(sortRoutesWithInitial(initialRouteName))\n .map((route) => ({ route, props: {} }));\n }\n const entries = [...children];\n\n const ordered = order\n .map(\n ({\n name,\n redirect,\n initialParams,\n listeners,\n options,\n getId,\n dangerouslySingular: singular,\n }) => {\n if (!entries.length) {\n console.warn(\n `[Layout children]: Too many screens defined. Route \"${name}\" is extraneous.`\n );\n return null;\n }\n const matchIndex = entries.findIndex(\n (child) => child.route === name || child.route === `${name}/index`\n );\n if (matchIndex === -1) {\n console.warn(\n `[Layout children]: No route named \"${name}\" exists in nested children:`,\n children.map(({ route }) => route)\n );\n return null;\n } else {\n // Get match and remove from entries\n const match = entries[matchIndex];\n entries.splice(matchIndex, 1);\n\n // Ensure to return null after removing from entries.\n if (redirect) {\n if (typeof redirect === 'string') {\n throw new Error(`Redirecting to a specific route is not supported yet.`);\n }\n return null;\n }\n\n if (getId) {\n console.warn(\n `Deprecated: prop 'getId' on screen ${name} is deprecated. Please rename the prop to 'dangerouslySingular'`\n );\n if (singular) {\n console.warn(\n `Screen ${name} cannot use both getId and dangerouslySingular together.`\n );\n }\n } else if (singular) {\n // If singular is set, use it as the getId function.\n if (typeof singular === 'string') {\n getId = () => singular;\n } else if (typeof singular === 'function' && name) {\n getId = (options) => singular(name, options.params || {});\n } else if (singular === true && name) {\n getId = (options) => getSingularId(name, options);\n }\n }\n\n return {\n route: match,\n props: { initialParams, listeners, options, getId },\n };\n }\n }\n )\n .filter(Boolean) as {\n route: RouteNode;\n props: Partial;\n }[];\n\n // Add any remaining children\n ordered.push(\n ...entries.sort(sortRoutesWithInitial(initialRouteName)).map((route) => ({ route, props: {} }))\n );\n\n return ordered;\n}\n\n/**\n * @returns React Navigation screens sorted by the `route` property.\n */\nexport function useSortedScreens(\n order: ScreenProps[],\n protectedScreens: Set,\n useOnlyUserDefinedScreens: boolean = false\n): React.ReactNode[] {\n const node = useRouteNode();\n\n const nodeChildren = node?.children ?? [];\n const children = useOnlyUserDefinedScreens\n ? nodeChildren.filter((child) =>\n order.some(\n (userDefinedScreen) =>\n userDefinedScreen.name === child.route ||\n `${userDefinedScreen.name}/index` === child.route\n )\n )\n : nodeChildren;\n\n const sorted = children.length ? getSortedChildren(children, order, node?.initialRouteName) : [];\n return React.useMemo(\n () =>\n sorted\n .filter((item) => {\n const route = item.route.route;\n return (\n !protectedScreens.has(route) && !protectedScreens.has(route.replace(/\\/index$/, ''))\n );\n })\n .map((value) => {\n return routeToScreen(value.route, value.props);\n }),\n [sorted, protectedScreens]\n );\n}\n\nfunction fromImport(\n value: RouteNode,\n { ErrorBoundary, SuspenseFallback, ...component }: LoadedRoute\n) {\n // If possible, add a more helpful display name for the component stack to improve debugging of React errors such as `Text strings must be rendered within a component.`.\n if (component?.default && __DEV__) {\n component.default.displayName ??= `${component.default.name ?? 'Route'}(${value.contextKey})`;\n }\n\n if (ErrorBoundary) {\n const Wrapped = React.forwardRef((props: any, ref: any) => {\n const children = React.createElement(component.default || EmptyRoute, {\n ...props,\n ref,\n });\n return {children};\n });\n\n if (__DEV__) {\n Wrapped.displayName = `ErrorBoundary(${value.contextKey})`;\n }\n\n return {\n default: Wrapped,\n SuspenseFallback,\n };\n }\n if (process.env.NODE_ENV !== 'production') {\n if (\n typeof component.default === 'object' &&\n component.default &&\n Object.keys(component.default).length === 0\n ) {\n return { default: EmptyRoute, SuspenseFallback };\n }\n }\n\n return { default: component.default, SuspenseFallback };\n}\n\nfunction fromLoadedRoute(value: RouteNode, res: LoadedRoute) {\n if (!(res instanceof Promise)) {\n return fromImport(value, res);\n }\n\n return res.then(fromImport.bind(null, value));\n}\n\n// TODO: Maybe there's a more React-y way to do this?\n// Without this store, the process enters a recursive loop.\nconst qualifiedStore = new WeakMap>();\n\n/** Wrap the component with various enhancements and add access to child routes. */\nexport function getQualifiedRouteComponent(value: RouteNode) {\n if (qualifiedStore.has(value)) {\n return qualifiedStore.get(value)!;\n }\n\n let ScreenComponent:\n | React.ForwardRefExoticComponent>\n | React.ComponentType<{ segment?: string }>;\n\n let LayoutSuspenseFallback: React.ComponentType | undefined;\n\n // TODO: This ensures sync doesn't use React.lazy, but it's not ideal.\n if (EXPO_ROUTER_IMPORT_MODE === 'lazy') {\n ScreenComponent = React.lazy(async () => {\n const res = value.loadRoute();\n return fromLoadedRoute(value, res) as Promise<{\n default: React.ComponentType;\n }>;\n });\n\n if (__DEV__) {\n ScreenComponent.displayName = `AsyncRoute(${value.route})`;\n }\n } else {\n const res = value.loadRoute();\n const result = fromImport(value, res);\n ScreenComponent = result.default!;\n LayoutSuspenseFallback = value.type === 'layout' ? result.SuspenseFallback : undefined;\n }\n const WrappedScreenComponent: typeof ScreenComponent = (props: object) => {\n useColorSchemeChangesIfNeeded();\n return ;\n };\n function BaseRoute({\n // Remove these React Navigation props to\n // enforce usage of expo-router hooks (where the query params are correct).\n route,\n navigation,\n\n // Pass all other props to the component\n ...props\n }: {\n route?: RouteProp;\n navigation: Omit<\n NavigationProp<\n ParamListBase,\n string,\n undefined,\n NavigationState,\n object,\n NativeStackNavigationEventMap | BottomTabNavigationEventMap\n >,\n 'getState'\n > & {\n getState(): NavigationState | undefined;\n };\n }) {\n const stateForPath = useStateForPath();\n const isFocused = navigation.isFocused();\n const store = useExpoRouterStore();\n const InheritedSuspenseFallback = use(SuspenseFallbackContext);\n\n const ResolvedSuspenseFallback =\n EXPO_ROUTER_IMPORT_MODE === 'lazy'\n ? DefaultSuspenseFallback\n : (LayoutSuspenseFallback ?? InheritedSuspenseFallback ?? DefaultSuspenseFallback);\n const providedSuspenseFallback =\n value.type === 'layout'\n ? (LayoutSuspenseFallback ?? InheritedSuspenseFallback)\n : InheritedSuspenseFallback;\n\n if (isFocused) {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }\n\n useEffect(\n () =>\n navigation.addListener('focus', () => {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n // Because setFocusedState caches the route info, this call will only trigger rerenders\n // if the component itself didnât rerender and the route info changed.\n // Otherwise, the update from the `if` above will handle it,\n // and this wonât cause a redundant second update.\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }),\n [navigation]\n );\n\n useEffect(() => {\n return navigation.addListener('transitionEnd', (e) => {\n if (!e?.data?.closing) {\n // When navigating to a screen, remove the no animation param to re-enable animations\n // Otherwise the navigation back would also have no animation\n if (hasParam(route?.params, INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME)) {\n navigation.replaceParams(\n removeParams(route?.params, [INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME])\n );\n }\n }\n });\n }, [navigation]);\n\n const isRouteType = value.type === 'route';\n const hasRouteKey = !!route?.key;\n\n return (\n \n \n {unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && (\n \n )}\n \n \n \n }>\n \n \n \n \n \n );\n }\n\n if (__DEV__) {\n BaseRoute.displayName = `Route(${value.route})`;\n }\n\n qualifiedStore.set(value, BaseRoute);\n return BaseRoute;\n}\n\nfunction AnalyticsListeners({\n navigation,\n screenId,\n}: {\n navigation: EventConsumer & {\n isFocused(): boolean;\n };\n screenId: string;\n}) {\n const stateForPath = useStateForPath();\n const isFirstRenderRef = React.useRef(true);\n const hasBlurredRef = React.useRef(true);\n const stringUrl = useMemo(() => generateStringUrlForState(stateForPath), [stateForPath]);\n\n if (isFirstRenderRef.current) {\n isFirstRenderRef.current = false;\n if (stringUrl) {\n unstable_navigationEvents.emit('pageWillRender', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n }\n }\n\n useEffect(() => {\n if (stringUrl) {\n return () => {\n unstable_navigationEvents.emit('pageRemoved', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n };\n }\n return () => {};\n }, [stringUrl, screenId]);\n\n const isFocused = navigation.isFocused();\n\n if (isFocused && stringUrl) {\n unstable_navigationEvents.emit('pageFocused', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = false;\n }\n\n useEffect(() => {\n if (stringUrl) {\n const cleanFocus = navigation.addListener('focus', () => {\n // If the screen was not blurred, don't emit focused again\n // hasBlurredRef will be false when the screen was initially focused\n if (hasBlurredRef.current) {\n unstable_navigationEvents.emit('pageFocused', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = false;\n }\n });\n const cleanBlur = navigation.addListener('blur', () => {\n unstable_navigationEvents.emit('pageBlurred', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = true;\n });\n return () => {\n cleanFocus();\n cleanBlur();\n };\n }\n return () => {};\n }, [navigation, stringUrl, screenId]);\n\n return null;\n}\n\nexport function screenOptionsFactory(\n route: RouteNode,\n options?: ScreenProps['options']\n): ScreenProps['options'] {\n return (args) => {\n // Only eager load generated components\n const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null;\n const staticResult = typeof staticOptions === 'function' ? staticOptions(args) : staticOptions;\n const dynamicResult = typeof options === 'function' ? options?.(args) : options;\n const output = {\n ...staticResult,\n ...dynamicResult,\n };\n\n // Prevent generated screens from showing up in the tab bar.\n if (route.internal) {\n output.tabBarItemStyle = { display: 'none' };\n output.tabBarButton = () => null;\n // TODO: React Navigation doesn't provide a way to prevent rendering the drawer item.\n output.drawerItemStyle = { height: 0, display: 'none' };\n }\n\n return output;\n };\n}\n\nexport function routeToScreen(\n route: RouteNode,\n { options, getId, ...props }: Partial = {}\n) {\n return (\n getQualifiedRouteComponent(route)}\n />\n );\n}\n\nexport function getSingularId(name: string, options: Record = {}) {\n return name\n .split('/')\n .map((segment) => {\n if (segment.startsWith('[...')) {\n return options.params?.[segment.slice(4, -1)]?.join('/') || segment;\n } else if (segment.startsWith('[') && segment.endsWith(']')) {\n return options.params?.[segment.slice(1, -1)] || segment;\n } else {\n return segment;\n }\n })\n .join('/');\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/src/useScreens.tsx b/packages/expo-router/src/useScreens.tsx
index 18e531010f20b7..54b17432749448 100644
--- a/packages/expo-router/src/useScreens.tsx
+++ b/packages/expo-router/src/useScreens.tsx
@@ -369,6 +369,7 @@ export function getQualifiedRouteComponent(value: RouteNode) {
Date: Tue, 12 May 2026 16:29:43 +0530
Subject: [PATCH 02/11] [docs] Fix Props rendering on API reference pages
(#45662)
# Why
The `Props` block on API reference pages renders as a bare list of rows
separated by thin top borders, without the rounded outer card that wraps
every other API section (Constants, Methods, Types, Enums). The mismatch
is visible on `/versions/v56.0.0/sdk/router/experimental-stack/`, where
the Props block sits right above a Types card and reads as visually
unfinished.
# How
Fix Props rendering on API reference pages by using `STYLES_APIBOX`.
# Test Plan
# Checklist
- [ ] 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)
---
.../plugins/__snapshots__/APISection.test.tsx.snap | 2 +-
docs/components/plugins/api/APISectionProps.tsx | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/docs/components/plugins/__snapshots__/APISection.test.tsx.snap b/docs/components/plugins/__snapshots__/APISection.test.tsx.snap
index 5099f98557cb8d..91c9ce2267c0ea 100644
--- a/docs/components/plugins/__snapshots__/APISection.test.tsx.snap
+++ b/docs/components/plugins/__snapshots__/APISection.test.tsx.snap
@@ -372,7 +372,7 @@ for more details.
arr.findIndex(t => t?.name === dec?.name) === i);
return (
-
+
*:last-child]:mb-0!')}>
{propsDeclarations?.map(prop =>
prop
? renderProp(
From 853c4af94e64d3a09a3e37427623e54d837103c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Szymon=20=C5=9Awierk?=
<58403334+sswrk@users.noreply.github.com>
Date: Tue, 12 May 2026 13:50:58 +0200
Subject: [PATCH 03/11] [docs] Correct the Gradle JVM args in build
infrastructure documentation (#45248)
# Why
This PR partly addresses this issue:
https://github.com/expo/eas-cli/issues/3682.
In the issue, the user reports that the documentation of Gradle JVM args
doesn't match the actual `GRADLE_OPTS` set in builds.
# How
I have compared the documentation with the worker code
(https://github.com/expo/eas-cli/blob/8229dc826513845d82ac4279d0b62394623935cf/packages/worker/src/env.ts#L84-L105),
and confirmed the discrepancy.
To clarify the Gradle opts, I've created a dedicated sub-section in
"Build reference -> Infrastructure" documentation. Apart from clarifying
the `GRADLE_OPTS` value, I have also added a warning about those opts
overriding JVM args set in a project's `gradle.properties`; I have also
provided an information about the possibility to override the
`GRADLE_OPTS` by specifying them in `env` manually.
# Test Plan
Tested manually

# Checklist
- [ ] 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).
- [x] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
---
docs/pages/build-reference/infrastructure.mdx | 38 ++++++++++++++-----
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/docs/pages/build-reference/infrastructure.mdx b/docs/pages/build-reference/infrastructure.mdx
index 3070d258b324ac..97bd80a2ae2e4f 100644
--- a/docs/pages/build-reference/infrastructure.mdx
+++ b/docs/pages/build-reference/infrastructure.mdx
@@ -1,5 +1,5 @@
---
-modificationDate: September 17th, 2025
+modificationDate: May 12th, 2026
title: Build server infrastructure
sidebar_title: Server infrastructure
maxHeadingDepth: 4
@@ -44,15 +44,7 @@ Android builders run on virtual machines in an isolated environment. Every build
- [npm cache deployed with Kubernetes](/build-reference/caching/#javascript-dependencies)
- [Maven cache deployed with Kubernetes](/build-reference/caching/#android-dependencies)
-- Global Gradle configuration in **~/.gradle/gradle.properties**:
-
- ```ini ~/.gradle/gradle.properties
- org.gradle.jvmargs=-Xmx14g -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
- org.gradle.parallel=true
- org.gradle.configureondemand=true
- org.gradle.daemon=false
- ```
-
+- Gradle JVM args are injected via the `GRADLE_OPTS` environment variable when the build environment is provisioned. See [Gradle JVM args](#gradle-jvm-args) below.
- Global npm configuration in **~/.npmrc**:
```ini ~/.npmrc
@@ -68,6 +60,32 @@ Android builders run on virtual machines in an isolated environment. Every build
enableImmutableInstalls: false
```
+### Gradle JVM args
+
+EAS Build sets the `GRADLE_OPTS` environment variable on the build VM (the worker) before Gradle runs. The values depend on the [resource class](/eas/json/#resourceclass) you select:
+
+| Resource class | `-Xmx` (max heap) |
+| -------------- | ----------------- |
+| `medium` | `4g` |
+| `large` | `8g` |
+
+In addition to `-Xmx`, the worker passes the following JVM args to the Gradle build JVM via `-Dorg.gradle.jvmargs`:
+
+- `-XX:MaxMetaspaceSize=1g`
+- `-XX:+HeapDumpOnOutOfMemoryError`
+- `-Dfile.encoding=UTF-8`
+
+The worker also sets these top-level Gradle properties on `GRADLE_OPTS`:
+
+- `-Dorg.gradle.parallel=true`
+- `-Dorg.gradle.daemon=false`
+
+> **warning** The worker sets `org.gradle.jvmargs` via `GRADLE_OPTS`, which overrides any `org.gradle.jvmargs` defined in your project's **gradle.properties**.
+
+#### Overriding `GRADLE_OPTS`
+
+You can replace the worker default by setting `GRADLE_OPTS` under a build profile's [`env`](/eas/json/#env) in **eas.json**, in a [workflow file](/eas/workflows/syntax/#jobsjob_idenv), or with [EAS Environment Variables](/eas/environment-variables/). Project environment values take precedence over the worker's default values.
+
### Android server images
####
`ubuntu-24.04-jdk-17-ndk-r27b-sdk-55` (`latest`, `sdk-55`)
From f6e4c1e291f6601ce96114258552d165abb49637 Mon Sep 17 00:00:00 2001
From: Jakub Tkacz <32908614+Ubax@users.noreply.github.com>
Date: Tue, 12 May 2026 14:02:03 +0200
Subject: [PATCH 04/11] [expo-observe] add expo-router integration (#45534)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Why
Wire `expo-observe` up to `expo-router` so navigation produces the
metrics App Metrics needs out of the box: time-to-render (TTR) for every
screen plus time-to-interactive (TTI) for screens that call
`useObserve().markInteractive()`. Previously the integration scaffolding
existed but emitted nothing â no listeners, no metrics.
# How
```mermaid
sequenceDiagram
participant App as App launch
participant Router as expo-router
participant Init as init.ts (listeners)
participant Hook as useObserveForRouter
participant Metrics as AppMetrics
App->>Init: appLaunchTime = Date.now()
Router->>Init: pageFocused (first)
Init->>Metrics: TTR { isAppLaunch: true }
Router->>Init: actionDispatched (NAVIGATE)
Note right of Init: store dispatchTime
Router->>Init: pageFocused
Init->>Metrics: TTR { isAppLaunch: false }
Hook->>Metrics: markInteractive() â TTI (now â dispatchTime)
```
# Test Plan
- Observe tester -> check session for navigation category
- Click house

# Checklist
- [ ] 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)
---
.../__e2e__/native-navigation/app/_layout.tsx | 1 -
.../modules/appmetrics/AppMetricsModule.kt | 29 +--
.../java/expo/modules/appmetrics/Metrics.kt | 3 +-
.../logevents/AttributeValidation.kt | 12 --
.../appmetrics/storage/SessionMappers.kt | 25 ++-
.../expo/modules/appmetrics/utils/JsonAny.kt | 10 +
.../modules/appmetrics/utils/JsonAnyTest.kt | 41 ++++
.../expo-app-metrics/build/module.web.d.ts | 8 +-
.../build/module.web.d.ts.map | 2 +-
packages/expo-app-metrics/build/module.web.js | 2 +-
.../expo-app-metrics/build/module.web.js.map | 2 +-
packages/expo-app-metrics/build/types.d.ts | 8 +-
.../expo-app-metrics/build/types.d.ts.map | 2 +-
packages/expo-app-metrics/build/types.js.map | 2 +-
packages/expo-app-metrics/src/module.web.ts | 7 +-
packages/expo-app-metrics/src/types.ts | 11 +-
.../expo/modules/observe/OpenTelemetry.kt | 4 +
.../expo/modules/observe/OpenTelemetryTest.kt | 32 +++
.../build/ObserveProvider.d.ts.map | 2 +-
.../expo-observe/build/ObserveProvider.js | 5 +-
.../expo-observe/build/ObserveProvider.js.map | 2 +-
.../ObserveRouterIntegrationProvider.d.ts | 5 +
.../ObserveRouterIntegrationProvider.d.ts.map | 1 +
.../ObserveRouterIntegrationProvider.js | 20 ++
.../ObserveRouterIntegrationProvider.js.map | 1 +
.../build/integrations/expo-router/index.d.ts | 1 +
.../integrations/expo-router/index.d.ts.map | 2 +-
.../build/integrations/expo-router/index.js | 1 +
.../integrations/expo-router/index.js.map | 2 +-
.../build/integrations/expo-router/init.d.ts | 5 +
.../integrations/expo-router/init.d.ts.map | 2 +-
.../build/integrations/expo-router/init.js | 76 +++++++
.../integrations/expo-router/init.js.map | 2 +-
.../integrations/expo-router/storage.d.ts | 27 +++
.../integrations/expo-router/storage.d.ts.map | 1 +
.../build/integrations/expo-router/storage.js | 10 +
.../integrations/expo-router/storage.js.map | 1 +
.../expo-router/useObserveForRouter.d.ts.map | 2 +-
.../expo-router/useObserveForRouter.js | 52 ++++-
.../expo-router/useObserveForRouter.js.map | 2 +-
packages/expo-observe/src/ObserveProvider.tsx | 4 +-
.../ObserveRouterIntegrationProvider.tsx | 31 +++
...eRouterIntegrationProvider.test.native.tsx | 130 +++++++++++
.../__tests__/init.test.android.ts | 203 ++++++++++++++++++
.../expo-router/__tests__/init.test.ios.ts | 85 ++++++++
.../expo-router/__tests__/storage.test.ts | 22 ++
.../useObserveForRouter.test.android.tsx | 67 +++++-
.../useObserveForRouter.test.ios.tsx | 86 ++++++++
.../src/integrations/expo-router/index.ts | 1 +
.../src/integrations/expo-router/init.ts | 89 ++++++++
.../src/integrations/expo-router/storage.ts | 38 ++++
.../expo-router/useObserveForRouter.ts | 61 +++++-
packages/expo-router/build/exports.d.ts | 1 +
packages/expo-router/build/exports.d.ts.map | 2 +-
packages/expo-router/build/exports.js.map | 2 +-
.../build/global-state/store.d.ts.map | 2 +-
.../expo-router/build/global-state/store.js | 2 +
.../build/global-state/store.js.map | 2 +-
.../native-tabs/NativeTabsView.shared.d.ts | 4 +-
.../NativeTabsView.shared.d.ts.map | 2 +-
.../build/navigationEvents/index.d.ts | 9 +-
.../build/navigationEvents/index.d.ts.map | 2 +-
.../build/navigationEvents/index.js | 43 +---
.../build/navigationEvents/index.js.map | 2 +-
.../build/navigationEvents/navigation.d.ts | 2 +
.../navigationEvents/navigation.d.ts.map | 1 +
.../build/navigationEvents/navigation.js | 21 ++
.../build/navigationEvents/navigation.js.map | 1 +
.../build/navigationEvents/types.d.ts | 11 +-
.../build/navigationEvents/types.d.ts.map | 2 +-
.../build/navigationEvents/types.js.map | 2 +-
.../expo-router/build/useScreens.d.ts.map | 2 +-
packages/expo-router/build/useScreens.js | 32 +--
packages/expo-router/build/useScreens.js.map | 2 +-
packages/expo-router/src/exports.ts | 8 +
.../expo-router/src/global-state/store.ts | 2 +
.../expo-router/src/navigationEvents/index.ts | 68 ++----
.../src/navigationEvents/navigation.ts | 18 ++
.../expo-router/src/navigationEvents/types.ts | 13 +-
packages/expo-router/src/useScreens.tsx | 34 +--
80 files changed, 1296 insertions(+), 236 deletions(-)
create mode 100644 packages/expo-app-metrics/android/src/test/java/expo/modules/appmetrics/utils/JsonAnyTest.kt
create mode 100644 packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts
create mode 100644 packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts.map
create mode 100644 packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js
create mode 100644 packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js.map
create mode 100644 packages/expo-observe/build/integrations/expo-router/storage.d.ts
create mode 100644 packages/expo-observe/build/integrations/expo-router/storage.d.ts.map
create mode 100644 packages/expo-observe/build/integrations/expo-router/storage.js
create mode 100644 packages/expo-observe/build/integrations/expo-router/storage.js.map
create mode 100644 packages/expo-observe/src/integrations/expo-router/ObserveRouterIntegrationProvider.tsx
create mode 100644 packages/expo-observe/src/integrations/expo-router/__tests__/ObserveRouterIntegrationProvider.test.native.tsx
create mode 100644 packages/expo-observe/src/integrations/expo-router/__tests__/init.test.android.ts
create mode 100644 packages/expo-observe/src/integrations/expo-router/__tests__/init.test.ios.ts
create mode 100644 packages/expo-observe/src/integrations/expo-router/__tests__/storage.test.ts
create mode 100644 packages/expo-observe/src/integrations/expo-router/storage.ts
create mode 100644 packages/expo-router/build/navigationEvents/navigation.d.ts
create mode 100644 packages/expo-router/build/navigationEvents/navigation.d.ts.map
create mode 100644 packages/expo-router/build/navigationEvents/navigation.js
create mode 100644 packages/expo-router/build/navigationEvents/navigation.js.map
create mode 100644 packages/expo-router/src/navigationEvents/navigation.ts
diff --git a/apps/router-e2e/__e2e__/native-navigation/app/_layout.tsx b/apps/router-e2e/__e2e__/native-navigation/app/_layout.tsx
index d85425ee4f334e..cccc8e148960af 100644
--- a/apps/router-e2e/__e2e__/native-navigation/app/_layout.tsx
+++ b/apps/router-e2e/__e2e__/native-navigation/app/_layout.tsx
@@ -14,7 +14,6 @@ unstable_navigationEvents.enable();
);
});
});
-unstable_navigationEvents.saveCurrentPathname();
export default function Layout() {
return (
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt
index 3793021a44b75d..9eba8a9804bb5d 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt
@@ -4,17 +4,18 @@ import android.content.Context
import expo.modules.appmetrics.appstartup.AppStartupManager
import expo.modules.appmetrics.logevents.LogEventOptions
import expo.modules.appmetrics.logevents.Severity
-import expo.modules.appmetrics.logevents.attributesToJsonObject
import expo.modules.appmetrics.logevents.sanitizeLogEventAttributes
import expo.modules.appmetrics.logevents.validateEventBody
import expo.modules.appmetrics.logevents.validateEventName
import expo.modules.appmetrics.memory.MemoryMetricsManager
+import expo.modules.appmetrics.storage.JsMetric
import expo.modules.appmetrics.storage.JsSession
import expo.modules.appmetrics.storage.LogRecord
import expo.modules.appmetrics.storage.Metric
import expo.modules.appmetrics.storage.SessionManager
import expo.modules.appmetrics.updates.UpdatesMonitoring
import expo.modules.appmetrics.updates.UpdatesStateEvent
+import expo.modules.appmetrics.utils.JsonAny
import expo.modules.appmetrics.utils.TimeUtils
import expo.modules.interfaces.constants.ConstantsInterface
import expo.modules.kotlin.exception.Exceptions
@@ -30,7 +31,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
-import kotlinx.serialization.json.Json
class AppMetricsModule : Module(), UpdatesStateChangeListener {
private val context: Context
@@ -97,7 +97,7 @@ class AppMetricsModule : Module(), UpdatesStateChangeListener {
name = validatedName,
body = validatedBody,
severity = severity.rawValue,
- attributes = sanitized.attributes?.let { Json.encodeToString(attributesToJsonObject(it)) },
+ attributes = sanitized.attributes?.let { JsonAny.encodeMapToJsonString(it) },
droppedAttributesCount = sanitized.droppedCount
)
),
@@ -192,8 +192,8 @@ class AppMetricsModule : Module(), UpdatesStateChangeListener {
}
}
- AsyncFunction("addCustomMetricToSession") Coroutine { sessionId: String, metric: PartialMetric ->
- sessionManager.addMetrics(listOf(metric.toMetric(sessionId)), sessionId = sessionId)
+ AsyncFunction("addCustomMetricToSession") Coroutine { metric: JsMetric ->
+ sessionManager.addMetrics(listOf(metric.toMetric()), sessionId = metric.sessionId)
}
AsyncFunction("getMainSession") Coroutine { ->
@@ -237,25 +237,6 @@ class AppMetricsModule : Module(), UpdatesStateChangeListener {
}
}
-data class PartialMetric(
- @Field val category: String,
- @Field val name: String,
- @Field val value: Double,
- @Field val routeName: String? = null,
- @Field val params: Map
? = null
-) : Record {
- fun toMetric(sessionId: String): Metric =
- Metric(
- sessionId = sessionId,
- timestamp = TimeUtils.getCurrentTimestampInISOFormat(),
- category = category,
- name = name,
- value = value,
- routeName = routeName,
- params = params?.let { Json.encodeToString(it) }
- )
-}
-
data class MetricAttributes(
@Field val routeName: String? = null,
@Field val params: Map? = null
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/Metrics.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/Metrics.kt
index 0483ba1f868699..1c0becc9734b05 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/Metrics.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/Metrics.kt
@@ -48,5 +48,6 @@ enum class MetricCategory(
AppStartup("appStartup"),
FrameRate("frameRate"),
Memory("memory"),
- Updates("updates")
+ Updates("updates"),
+ Navigation("navigation")
}
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/logevents/AttributeValidation.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/logevents/AttributeValidation.kt
index 8a40ffed392702..05ed57b275d0ca 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/logevents/AttributeValidation.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/logevents/AttributeValidation.kt
@@ -2,8 +2,6 @@ package expo.modules.appmetrics.logevents
import android.util.Log
import expo.modules.appmetrics.TAG
-import expo.modules.appmetrics.utils.JsonAny
-import kotlinx.serialization.json.JsonObject
/**
* Patterns that match attribute keys reserved by the SDK. Caller-provided
@@ -115,13 +113,3 @@ internal fun sanitizeLogEventAttributes(attributes: Map?): Sanitiz
droppedCount = emptyKeyDrops + reservedKeyDrops.size + overflowDrops
)
}
-
-/**
- * Converts a sanitized attribute map to a `JsonObject` for storage. Values
- * whose type cannot be represented in OTLP (e.g. `Date`, NaN/Infinity doubles)
- * are encoded as JSON `null` here; the typed-attribute encoder at dispatch
- * time will skip them and add to `droppedAttributesCount` accordingly.
- */
-internal fun attributesToJsonObject(attributes: Map): JsonObject {
- return JsonObject(attributes.mapValues { (_, value) -> JsonAny.toElement(value) })
-}
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/storage/SessionMappers.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/storage/SessionMappers.kt
index 43b8bac389e6cd..6cdcf45579e894 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/storage/SessionMappers.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/storage/SessionMappers.kt
@@ -1,8 +1,10 @@
package expo.modules.appmetrics.storage
import expo.modules.appmetrics.utils.JsonAny
+import expo.modules.appmetrics.utils.TimeUtils
import expo.modules.kotlin.records.Field
import expo.modules.kotlin.records.Record
+import java.util.UUID
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
@@ -39,16 +41,29 @@ data class JsSession(
}
data class JsMetric(
- @Field val metricId: String,
@Field val sessionId: String,
- @Field val timestamp: String,
@Field val category: String,
@Field val name: String,
@Field val value: Double,
- @Field val routeName: String?,
- @Field val updateId: String?,
- @Field val params: Map?
+ @Field val metricId: String? = UUID.randomUUID().toString(),
+ @Field val timestamp: String = TimeUtils.getCurrentTimestampInISOFormat(),
+ @Field val routeName: String? = null,
+ @Field val updateId: String? = null,
+ @Field val params: Map? = null
) : Record {
+ fun toMetric(): Metric =
+ Metric(
+ metricId = metricId ?: UUID.randomUUID().toString(),
+ sessionId = sessionId,
+ timestamp = timestamp,
+ category = category,
+ name = name,
+ value = value,
+ routeName = routeName,
+ updateId = updateId,
+ params = params?.let { JsonAny.encodeMapToJsonString(it) }
+ )
+
companion object {
fun fromMetric(metric: Metric): JsMetric =
JsMetric(
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/utils/JsonAny.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/utils/JsonAny.kt
index 3e265d086b1da1..89286e61200eba 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/utils/JsonAny.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/utils/JsonAny.kt
@@ -1,5 +1,6 @@
package expo.modules.appmetrics.utils
+import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
@@ -56,4 +57,13 @@ object JsonAny {
is JsonArray -> element.map { fromElement(it) }
}
}
+
+ // `Json.encodeToString(map)` would fail at runtime with "Serializer for class
+ // 'Any' is not found" â kotlinx.serialization has no built-in `Any`
+ // serializer, so we route every value through `toElement` first.
+ fun encodeMapToJsonString(map: Map): String =
+ Json.encodeToString(
+ JsonObject.serializer(),
+ JsonObject(map.mapValues { (_, v) -> toElement(v) })
+ )
}
diff --git a/packages/expo-app-metrics/android/src/test/java/expo/modules/appmetrics/utils/JsonAnyTest.kt b/packages/expo-app-metrics/android/src/test/java/expo/modules/appmetrics/utils/JsonAnyTest.kt
new file mode 100644
index 00000000000000..407194043b005a
--- /dev/null
+++ b/packages/expo-app-metrics/android/src/test/java/expo/modules/appmetrics/utils/JsonAnyTest.kt
@@ -0,0 +1,41 @@
+package expo.modules.appmetrics.utils
+
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonElement
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(manifest = Config.NONE, sdk = [28])
+class JsonAnyTest {
+ @Test
+ fun `encodeMapToJsonString round-trips primitives, booleans and nested structures`() {
+ val encoded = JsonAny.encodeMapToJsonString(
+ mapOf(
+ "isInitial" to true,
+ "isAppLaunch" to false,
+ "screen" to "Home",
+ "attempt" to 3,
+ "ratio" to 1.5,
+ "missing" to null,
+ "tags" to listOf("a", "b"),
+ "nested" to mapOf("k" to true)
+ )
+ )
+
+ @Suppress("UNCHECKED_CAST")
+ val decoded = JsonAny.fromElement(Json.decodeFromString(encoded)) as Map
+ assertEquals(true, decoded["isInitial"])
+ assertEquals(false, decoded["isAppLaunch"])
+ assertEquals("Home", decoded["screen"])
+ assertEquals(3L, decoded["attempt"])
+ assertEquals(1.5, decoded["ratio"])
+ assertNull(decoded["missing"])
+ assertEquals(listOf("a", "b"), decoded["tags"])
+ assertEquals(mapOf("k" to true), decoded["nested"])
+ }
+}
diff --git a/packages/expo-app-metrics/build/module.web.d.ts b/packages/expo-app-metrics/build/module.web.d.ts
index bec19e61032dd8..27eab1950936b8 100644
--- a/packages/expo-app-metrics/build/module.web.d.ts
+++ b/packages/expo-app-metrics/build/module.web.d.ts
@@ -1,12 +1,8 @@
import { NativeModule } from 'expo';
-import type { ExpoAppMetricsModuleType, LogEventOptions, MetricAttributes } from './types';
+import type { ExpoAppMetricsModuleType, LogEventOptions, Metric, MetricAttributes } from './types';
export * from './types';
declare class ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleType {
- addCustomMetricToSession(sessionId: string, metric: {
- category: string;
- name: string;
- value: number;
- }): Promise;
+ addCustomMetricToSession(metric: Metric): Promise;
markFirstRender(): Promise;
markInteractive(attributes?: MetricAttributes): Promise;
logEvent(name: string, options?: LogEventOptions): void;
diff --git a/packages/expo-app-metrics/build/module.web.d.ts.map b/packages/expo-app-metrics/build/module.web.d.ts.map
index e972f55bbf4361..abc89bc923cf30 100644
--- a/packages/expo-app-metrics/build/module.web.d.ts.map
+++ b/packages/expo-app-metrics/build/module.web.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"module.web.d.ts","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,MAAM,CAAC;AAEvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3F,cAAc,SAAS,CAAC;AAExB,cAAM,oBAAqB,SAAQ,YAAa,YAAW,wBAAwB;IACjF,wBAAwB,CACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GACxD,OAAO,CAAC,IAAI,CAAC;IAGV,eAAe;IACf,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe;IAC1C,gBAAgB;IAGhB,kBAAkB;IAClB,cAAc;IAGpB,mBAAmB;IACnB,YAAY;IACZ,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM;IAG9B,WAAW,CAAC,SAAS,EAAE,MAAM;IACvB,cAAc;CAGrB;;AAED,wBAAyE"}
\ No newline at end of file
+{"version":3,"file":"module.web.d.ts","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,MAAM,CAAC;AAEvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEnG,cAAc,SAAS,CAAC;AAExB,cAAM,oBAAqB,SAAQ,YAAa,YAAW,wBAAwB;IACjF,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAGjD,eAAe;IACf,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe;IAC1C,gBAAgB;IAGhB,kBAAkB;IAClB,cAAc;IAGpB,mBAAmB;IACnB,YAAY;IACZ,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM;IAG9B,WAAW,CAAC,SAAS,EAAE,MAAM;IACvB,cAAc;CAGrB;;AAED,wBAAyE"}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/build/module.web.js b/packages/expo-app-metrics/build/module.web.js
index 32e3ab37c8acd5..e3a64647e81134 100644
--- a/packages/expo-app-metrics/build/module.web.js
+++ b/packages/expo-app-metrics/build/module.web.js
@@ -1,7 +1,7 @@
import { NativeModule, registerWebModule } from 'expo';
export * from './types';
class ExpoAppMetricsModule extends NativeModule {
- addCustomMetricToSession(sessionId, metric) {
+ addCustomMetricToSession(metric) {
throw new Error('Method not implemented.');
}
async markFirstRender() { }
diff --git a/packages/expo-app-metrics/build/module.web.js.map b/packages/expo-app-metrics/build/module.web.js.map
index b9322b23d36867..835d197cc84e78 100644
--- a/packages/expo-app-metrics/build/module.web.js.map
+++ b/packages/expo-app-metrics/build/module.web.js.map
@@ -1 +1 @@
-{"version":3,"file":"module.web.js","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAIvD,cAAc,SAAS,CAAC;AAExB,MAAM,oBAAqB,SAAQ,YAAY;IAC7C,wBAAwB,CACtB,SAAiB,EACjB,MAAyD;QAEzD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,CAAC,eAAe,KAAI,CAAC;IAC1B,KAAK,CAAC,eAAe,CAAC,UAA6B,IAAG,CAAC;IACvD,QAAQ,CAAC,IAAY,EAAE,OAAyB,IAAG,CAAC;IACpD,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,CAAC,kBAAkB,KAAI,CAAC;IAC7B,KAAK,CAAC,cAAc;QAClB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,mBAAmB,KAAI,CAAC;IACxB,YAAY,KAAI,CAAC;IACjB,YAAY,CAAC,QAAiB;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,WAAW,CAAC,SAAiB,IAAG,CAAC;IACjC,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC","sourcesContent":["import { NativeModule, registerWebModule } from 'expo';\n\nimport type { ExpoAppMetricsModuleType, LogEventOptions, MetricAttributes } from './types';\n\nexport * from './types';\n\nclass ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleType {\n addCustomMetricToSession(\n sessionId: string,\n metric: { category: string; name: string; value: number }\n ): Promise {\n throw new Error('Method not implemented.');\n }\n async markFirstRender() {}\n async markInteractive(attributes?: MetricAttributes) {}\n logEvent(name: string, options?: LogEventOptions) {}\n async getStoredEntries() {\n return [];\n }\n async clearStoredEntries() {}\n async getAllSessions() {\n return [];\n }\n simulateCrashReport() {}\n triggerCrash() {}\n startSession(metadata?: string) {\n return '';\n }\n stopSession(sessionId: string) {}\n async getMainSession() {\n return null;\n }\n}\n\nexport default registerWebModule(ExpoAppMetricsModule, 'ExpoAppMetrics');\n"]}
\ No newline at end of file
+{"version":3,"file":"module.web.js","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAIvD,cAAc,SAAS,CAAC;AAExB,MAAM,oBAAqB,SAAQ,YAAY;IAC7C,wBAAwB,CAAC,MAAc;QACrC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,CAAC,eAAe,KAAI,CAAC;IAC1B,KAAK,CAAC,eAAe,CAAC,UAA6B,IAAG,CAAC;IACvD,QAAQ,CAAC,IAAY,EAAE,OAAyB,IAAG,CAAC;IACpD,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,CAAC,kBAAkB,KAAI,CAAC;IAC7B,KAAK,CAAC,cAAc;QAClB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,mBAAmB,KAAI,CAAC;IACxB,YAAY,KAAI,CAAC;IACjB,YAAY,CAAC,QAAiB;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,WAAW,CAAC,SAAiB,IAAG,CAAC;IACjC,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC","sourcesContent":["import { NativeModule, registerWebModule } from 'expo';\n\nimport type { ExpoAppMetricsModuleType, LogEventOptions, Metric, MetricAttributes } from './types';\n\nexport * from './types';\n\nclass ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleType {\n addCustomMetricToSession(metric: Metric): Promise {\n throw new Error('Method not implemented.');\n }\n async markFirstRender() {}\n async markInteractive(attributes?: MetricAttributes) {}\n logEvent(name: string, options?: LogEventOptions) {}\n async getStoredEntries() {\n return [];\n }\n async clearStoredEntries() {}\n async getAllSessions() {\n return [];\n }\n simulateCrashReport() {}\n triggerCrash() {}\n startSession(metadata?: string) {\n return '';\n }\n stopSession(sessionId: string) {}\n async getMainSession() {\n return null;\n }\n}\n\nexport default registerWebModule(ExpoAppMetricsModule, 'ExpoAppMetrics');\n"]}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/build/types.d.ts b/packages/expo-app-metrics/build/types.d.ts
index a4ee6610e361e2..9fd82b23de3d51 100644
--- a/packages/expo-app-metrics/build/types.d.ts
+++ b/packages/expo-app-metrics/build/types.d.ts
@@ -294,13 +294,7 @@ export interface ExpoAppMetricsModuleType {
* @private This API is unstable and may change without notice.
* @platform android
*/
- addCustomMetricToSession(sessionId: string, metric: {
- category: string;
- name: string;
- value: number;
- routeName?: string;
- params?: Record;
- }): Promise;
+ addCustomMetricToSession(metric: Metric): Promise;
/**
* Returns the current main session, including its metrics. Resolves to
* `null` if the session row hasn't been persisted yet.
diff --git a/packages/expo-app-metrics/build/types.d.ts.map b/packages/expo-app-metrics/build/types.d.ts.map
index b919c2e4dae608..9dc6d1589c26ae 100644
--- a/packages/expo-app-metrics/build/types.d.ts.map
+++ b/packages/expo-app-metrics/build/types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/build/types.js.map b/packages/expo-app-metrics/build/types.js.map
index 7aa98299ad3c92..b4f2ab0325129c 100644
--- a/packages/expo-app-metrics/build/types.js.map
+++ b/packages/expo-app-metrics/build/types.js.map
@@ -1 +1 @@
-{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type AppStartupTimes = {\n /**\n * Time from when the user taps the app to the moment the app starts executing the main code.\n * It includes loading native dynamic libraries, executing C++ static constructors\n * and Objective-C `+load` methods defined in classes or categories.\n *\n * @platform iOS\n */\n loadTime?: number;\n /**\n * Full time from process start until the root view of the React Native instance is created,\n * recorded when the app is launched fresh by the user.\n */\n coldLaunchTime?: number;\n /**\n * Launch time recorded when the process was already running in the background\n * before the user launches the app.\n */\n warmLaunchTime?: number;\n /**\n * Duration to evaluate the JavaScript bundle by the runtime.\n */\n bundleLoadTime?: number;\n /**\n * Time until the first React render occurs.\n */\n timeToFirstRender?: number;\n /**\n * Time until the app is interactive (after first render).\n */\n timeToInteractive?: number;\n};\n\nexport type MemoryUsageSnapshot = {\n /**\n * Memory in bytes allocated by the app, including both the physical memory and additional memory that the app might be using,\n * such as memory that has been paged out (swapped) to disk or memory that is shared with other processes.\n *\n * @platform iOS\n */\n allocated?: number;\n /**\n * Physical memory in bytes pages currently in use (resident size).\n */\n physical: number;\n /**\n * The amount of available memory in bytes that app can still allocate.\n */\n available: number;\n /**\n * The amount of memory in bytes currently used by the Java heap.\n *\n * @platform android\n */\n javaHeap?: number;\n};\n\nexport type FrameRateMetrics = {\n /**\n * Total amount of frames rendered.\n */\n renderedFrames: number;\n /**\n * Expected amount of frames rendered if everything renders without any delay.\n */\n expectedFrames: number;\n /**\n * Number of frames which were skipped because the main thread was busy with some work.\n */\n droppedFrames: number;\n /**\n * Total amount of frozen frames. Frozen frame is frame that takes at least 700ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/frozen).\n */\n frozenFrames: number;\n /**\n * Total amount of slow frames. Slow frame is frame that takes at least 17ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/render).\n */\n slowFrames: number;\n /**\n * Total amount of freeze durations, in seconds. Freeze is an amount of time every frame rendering was delayed by in comparison with the ideal performant frame.\n * For example if expected frame duration was 16ms, but in reality we've rendered this frame in 320ms, we have a freeze with 304ms duration.\n */\n freezeTime: number;\n /**\n * Total duration of the screen session, in seconds. It is counted by summing up all rendered frames duration.\n */\n sessionDuration: number;\n};\n\nexport interface Metric {\n timestamp: string;\n category: string;\n name: string;\n value: number;\n sessionId: string;\n routeName?: string;\n params?: Record;\n}\n\nexport type MetricAttributes = {\n /**\n * Name of the route associated with the metric. Some metrics populate this\n * with a sensible default when omitted â for example, the TTI metric falls\n * back to the initial route name detected from the router.\n */\n routeName?: string;\n /**\n * Custom parameters to attach to the metric.\n */\n params?: Record;\n};\n\n/**\n * Severity of a log event, ordered from least to most severe:\n *\n * - `\"trace\"` â Fine-grained tracing, typically only useful while reproducing\n * a specific issue.\n * - `\"debug\"` â Diagnostic detail useful during development; usually filtered\n * out in production.\n * - `\"info\"` â Routine, expected events that record normal app behavior.\n * - `\"warn\"` â Unexpected but recoverable conditions worth investigating.\n * - `\"error\"` â An operation failed; the app continues running but is in a\n * degraded state.\n * - `\"fatal\"` â A severe failure, often immediately followed by app termination.\n */\nexport type LogSeverity = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\n/**\n * Value types accepted in a log event's `attributes` map. Strings, numbers,\n * and booleans are stored as typed primitives; arrays and nested maps preserve\n * their structure. Other JS values (functions, `Date`, `undefined`, etc.) are\n * not supported and may be dropped by downstream consumers.\n */\nexport type LogAttributeValue =\n | string\n | number\n | boolean\n | LogAttributeValue[]\n | { [key: string]: LogAttributeValue };\n\n/**\n * A single log event collected during a session.\n */\nexport type LogRecord = {\n /**\n * ISO 8601 timestamp of when the record was created.\n */\n timestamp: string;\n /**\n * Event name.\n */\n name: string;\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n */\n severity: LogSeverity;\n};\n\n/**\n * Optional configuration accepted by `logEvent`. The event name is passed as\n * the first positional argument since it's required and the only field most\n * callers set.\n */\nexport type LogEventOptions = {\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n *\n * @default \"info\"\n */\n severity?: LogSeverity | null;\n};\n\nexport type SessionType = 'main' | 'foreground' | 'screen' | 'custom' | 'unknown';\n\nexport type CrashKind =\n | 'badAccess'\n | 'fatalError'\n | 'divideByZero'\n | 'forceUnwrapNil'\n | 'arrayOutOfBounds'\n | 'objcException'\n | 'stackOverflow';\n\ntype SessionBase = {\n id: string;\n startDate: string;\n endDate?: string | null;\n metrics: Metric[];\n logs: LogRecord[];\n};\n\nexport type MainSession = SessionBase & {\n type: 'main';\n crashReport?: CrashReport | null;\n};\n\nexport type GenericSession = SessionBase & {\n type: Exclude;\n};\n\nexport type Session = MainSession | GenericSession;\n\nexport type CallStackFrame = {\n binaryName?: string | null;\n binaryUUID?: string | null;\n address?: number | null;\n offsetIntoBinaryTextSegment?: number | null;\n sampleCount?: number | null;\n subFrames?: CallStackFrame[] | null;\n /**\n * Resolved symbol from on-device `dladdr` symbolication. Swift and Itanium-ABI C++\n * names are demangled; Objective-C selectors and plain C symbols are returned as-is.\n * `null` when the binary is not loaded in this process or `dladdr` could not resolve it.\n *\n * @platform ios\n */\n symbol?: string | null;\n};\n\nexport type CallStack = {\n threadAttributed?: boolean | null;\n callStackRootFrames?: CallStackFrame[] | null;\n};\n\nexport type CallStackTree = {\n callStacks?: CallStack[] | null;\n};\n\nexport type CrashReport = {\n exceptionType?: number | null;\n exceptionCode?: number | null;\n signal?: number | null;\n terminationReason?: string | null;\n virtualMemoryRegionInfo?: string | null;\n exceptionReason?: {\n composedMessage: string;\n formatString: string;\n arguments: string[];\n exceptionType: string;\n className: string;\n exceptionName: string;\n } | null;\n callStackTree?: CallStackTree | null;\n timestampBegin: string;\n timestampEnd: string;\n ingestedAt: string;\n};\n\nexport interface ExpoAppMetricsModuleType {\n markFirstRender(): void;\n markInteractive(attributes?: MetricAttributes): void;\n /**\n * Records a log event against the current main session. The event is\n * persisted locally and dispatched on the next `dispatchEvents()` flush as an\n * OpenTelemetry log record sent to the `/v1/logs` endpoint.\n *\n * Severity defaults to `\"info\"` when not provided.\n *\n * @param name Event name. Maps to the OpenTelemetry `event.name` attribute.\n * @param options Optional body, attributes, and severity overrides.\n */\n logEvent(name: string, options?: LogEventOptions): void;\n getStoredEntries(): Promise;\n clearStoredEntries(): Promise;\n /**\n * Returns all sessions across the current and historical entries,\n * ordered with the current launch first.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n getAllSessions(): Promise;\n\n /**\n * Simulates a crash report, attributing it to the current main session.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n simulateCrashReport(): void;\n\n /**\n * Intentionally crashes the app to produce a real MetricKit diagnostic.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n triggerCrash(kind: CrashKind): void;\n\n /**\n * Starts a new app metrics session. Returns the session ID.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n startSession(): string;\n /**\n * Stops the app metrics session with the given session ID.\n *\n * @platform android\n */\n stopSession(sessionId: string): void;\n /**\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n addCustomMetricToSession(\n sessionId: string,\n metric: {\n category: string;\n name: string;\n value: number;\n routeName?: string;\n params?: Record;\n }\n ): Promise;\n /**\n * Returns the current main session, including its metrics. Resolves to\n * `null` if the session row hasn't been persisted yet.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n getMainSession(): Promise;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type AppStartupTimes = {\n /**\n * Time from when the user taps the app to the moment the app starts executing the main code.\n * It includes loading native dynamic libraries, executing C++ static constructors\n * and Objective-C `+load` methods defined in classes or categories.\n *\n * @platform iOS\n */\n loadTime?: number;\n /**\n * Full time from process start until the root view of the React Native instance is created,\n * recorded when the app is launched fresh by the user.\n */\n coldLaunchTime?: number;\n /**\n * Launch time recorded when the process was already running in the background\n * before the user launches the app.\n */\n warmLaunchTime?: number;\n /**\n * Duration to evaluate the JavaScript bundle by the runtime.\n */\n bundleLoadTime?: number;\n /**\n * Time until the first React render occurs.\n */\n timeToFirstRender?: number;\n /**\n * Time until the app is interactive (after first render).\n */\n timeToInteractive?: number;\n};\n\nexport type MemoryUsageSnapshot = {\n /**\n * Memory in bytes allocated by the app, including both the physical memory and additional memory that the app might be using,\n * such as memory that has been paged out (swapped) to disk or memory that is shared with other processes.\n *\n * @platform iOS\n */\n allocated?: number;\n /**\n * Physical memory in bytes pages currently in use (resident size).\n */\n physical: number;\n /**\n * The amount of available memory in bytes that app can still allocate.\n */\n available: number;\n /**\n * The amount of memory in bytes currently used by the Java heap.\n *\n * @platform android\n */\n javaHeap?: number;\n};\n\nexport type FrameRateMetrics = {\n /**\n * Total amount of frames rendered.\n */\n renderedFrames: number;\n /**\n * Expected amount of frames rendered if everything renders without any delay.\n */\n expectedFrames: number;\n /**\n * Number of frames which were skipped because the main thread was busy with some work.\n */\n droppedFrames: number;\n /**\n * Total amount of frozen frames. Frozen frame is frame that takes at least 700ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/frozen).\n */\n frozenFrames: number;\n /**\n * Total amount of slow frames. Slow frame is frame that takes at least 17ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/render).\n */\n slowFrames: number;\n /**\n * Total amount of freeze durations, in seconds. Freeze is an amount of time every frame rendering was delayed by in comparison with the ideal performant frame.\n * For example if expected frame duration was 16ms, but in reality we've rendered this frame in 320ms, we have a freeze with 304ms duration.\n */\n freezeTime: number;\n /**\n * Total duration of the screen session, in seconds. It is counted by summing up all rendered frames duration.\n */\n sessionDuration: number;\n};\n\nexport interface Metric {\n timestamp: string;\n category: string;\n name: string;\n value: number;\n sessionId: string;\n routeName?: string;\n params?: Record;\n}\n\nexport type MetricAttributes = {\n /**\n * Name of the route associated with the metric. Some metrics populate this\n * with a sensible default when omitted â for example, the TTI metric falls\n * back to the initial route name detected from the router.\n */\n routeName?: string;\n /**\n * Custom parameters to attach to the metric.\n */\n params?: Record;\n};\n\n/**\n * Severity of a log event, ordered from least to most severe:\n *\n * - `\"trace\"` â Fine-grained tracing, typically only useful while reproducing\n * a specific issue.\n * - `\"debug\"` â Diagnostic detail useful during development; usually filtered\n * out in production.\n * - `\"info\"` â Routine, expected events that record normal app behavior.\n * - `\"warn\"` â Unexpected but recoverable conditions worth investigating.\n * - `\"error\"` â An operation failed; the app continues running but is in a\n * degraded state.\n * - `\"fatal\"` â A severe failure, often immediately followed by app termination.\n */\nexport type LogSeverity = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\n/**\n * Value types accepted in a log event's `attributes` map. Strings, numbers,\n * and booleans are stored as typed primitives; arrays and nested maps preserve\n * their structure. Other JS values (functions, `Date`, `undefined`, etc.) are\n * not supported and may be dropped by downstream consumers.\n */\nexport type LogAttributeValue =\n | string\n | number\n | boolean\n | LogAttributeValue[]\n | { [key: string]: LogAttributeValue };\n\n/**\n * A single log event collected during a session.\n */\nexport type LogRecord = {\n /**\n * ISO 8601 timestamp of when the record was created.\n */\n timestamp: string;\n /**\n * Event name.\n */\n name: string;\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n */\n severity: LogSeverity;\n};\n\n/**\n * Optional configuration accepted by `logEvent`. The event name is passed as\n * the first positional argument since it's required and the only field most\n * callers set.\n */\nexport type LogEventOptions = {\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n *\n * @default \"info\"\n */\n severity?: LogSeverity | null;\n};\n\nexport type SessionType = 'main' | 'foreground' | 'screen' | 'custom' | 'unknown';\n\nexport type CrashKind =\n | 'badAccess'\n | 'fatalError'\n | 'divideByZero'\n | 'forceUnwrapNil'\n | 'arrayOutOfBounds'\n | 'objcException'\n | 'stackOverflow';\n\ntype SessionBase = {\n id: string;\n startDate: string;\n endDate?: string | null;\n metrics: Metric[];\n logs: LogRecord[];\n};\n\nexport type MainSession = SessionBase & {\n type: 'main';\n crashReport?: CrashReport | null;\n};\n\nexport type GenericSession = SessionBase & {\n type: Exclude;\n};\n\nexport type Session = MainSession | GenericSession;\n\nexport type CallStackFrame = {\n binaryName?: string | null;\n binaryUUID?: string | null;\n address?: number | null;\n offsetIntoBinaryTextSegment?: number | null;\n sampleCount?: number | null;\n subFrames?: CallStackFrame[] | null;\n /**\n * Resolved symbol from on-device `dladdr` symbolication. Swift and Itanium-ABI C++\n * names are demangled; Objective-C selectors and plain C symbols are returned as-is.\n * `null` when the binary is not loaded in this process or `dladdr` could not resolve it.\n *\n * @platform ios\n */\n symbol?: string | null;\n};\n\nexport type CallStack = {\n threadAttributed?: boolean | null;\n callStackRootFrames?: CallStackFrame[] | null;\n};\n\nexport type CallStackTree = {\n callStacks?: CallStack[] | null;\n};\n\nexport type CrashReport = {\n exceptionType?: number | null;\n exceptionCode?: number | null;\n signal?: number | null;\n terminationReason?: string | null;\n virtualMemoryRegionInfo?: string | null;\n exceptionReason?: {\n composedMessage: string;\n formatString: string;\n arguments: string[];\n exceptionType: string;\n className: string;\n exceptionName: string;\n } | null;\n callStackTree?: CallStackTree | null;\n timestampBegin: string;\n timestampEnd: string;\n ingestedAt: string;\n};\n\nexport interface ExpoAppMetricsModuleType {\n markFirstRender(): void;\n markInteractive(attributes?: MetricAttributes): void;\n /**\n * Records a log event against the current main session. The event is\n * persisted locally and dispatched on the next `dispatchEvents()` flush as an\n * OpenTelemetry log record sent to the `/v1/logs` endpoint.\n *\n * Severity defaults to `\"info\"` when not provided.\n *\n * @param name Event name. Maps to the OpenTelemetry `event.name` attribute.\n * @param options Optional body, attributes, and severity overrides.\n */\n logEvent(name: string, options?: LogEventOptions): void;\n getStoredEntries(): Promise;\n clearStoredEntries(): Promise;\n /**\n * Returns all sessions across the current and historical entries,\n * ordered with the current launch first.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n getAllSessions(): Promise;\n\n /**\n * Simulates a crash report, attributing it to the current main session.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n simulateCrashReport(): void;\n\n /**\n * Intentionally crashes the app to produce a real MetricKit diagnostic.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n triggerCrash(kind: CrashKind): void;\n\n /**\n * Starts a new app metrics session. Returns the session ID.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n startSession(): string;\n /**\n * Stops the app metrics session with the given session ID.\n *\n * @platform android\n */\n stopSession(sessionId: string): void;\n /**\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n addCustomMetricToSession(metric: Metric): Promise;\n /**\n * Returns the current main session, including its metrics. Resolves to\n * `null` if the session row hasn't been persisted yet.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n getMainSession(): Promise;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/src/module.web.ts b/packages/expo-app-metrics/src/module.web.ts
index 2d7ad6db5962fa..6c03faea8e5cdc 100644
--- a/packages/expo-app-metrics/src/module.web.ts
+++ b/packages/expo-app-metrics/src/module.web.ts
@@ -1,14 +1,11 @@
import { NativeModule, registerWebModule } from 'expo';
-import type { ExpoAppMetricsModuleType, LogEventOptions, MetricAttributes } from './types';
+import type { ExpoAppMetricsModuleType, LogEventOptions, Metric, MetricAttributes } from './types';
export * from './types';
class ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleType {
- addCustomMetricToSession(
- sessionId: string,
- metric: { category: string; name: string; value: number }
- ): Promise {
+ addCustomMetricToSession(metric: Metric): Promise {
throw new Error('Method not implemented.');
}
async markFirstRender() {}
diff --git a/packages/expo-app-metrics/src/types.ts b/packages/expo-app-metrics/src/types.ts
index 1d8003d27e6cd4..c8f539301b295e 100644
--- a/packages/expo-app-metrics/src/types.ts
+++ b/packages/expo-app-metrics/src/types.ts
@@ -326,16 +326,7 @@ export interface ExpoAppMetricsModuleType {
* @private This API is unstable and may change without notice.
* @platform android
*/
- addCustomMetricToSession(
- sessionId: string,
- metric: {
- category: string;
- name: string;
- value: number;
- routeName?: string;
- params?: Record;
- }
- ): Promise;
+ addCustomMetricToSession(metric: Metric): Promise;
/**
* Returns the current main session, including its metrics. Resolves to
* `null` if the session row hasn't been persisted yet.
diff --git a/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt b/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt
index dd9d3e5408dbc8..0bdbf2cfdee72b 100644
--- a/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt
+++ b/packages/expo-observe/android/src/main/java/expo/modules/observe/OpenTelemetry.kt
@@ -189,6 +189,10 @@ private val metricNameMap = mapOf(
// Updates
(MetricCategory.Updates.categoryName to "updateDownloadTime") to "expo.updates.download_time",
+
+ // Navigation
+ (MetricCategory.Navigation.categoryName to "ttr") to "expo.navigation.ttr",
+ (MetricCategory.Navigation.categoryName to "tti") to "expo.navigation.tti"
)
fun EASMetric.toOTMetric(): OTMetric {
diff --git a/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt b/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt
index b2049d79bff944..f02bc2007aeec8 100644
--- a/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt
+++ b/packages/expo-observe/android/src/test/java/expo/modules/observe/OpenTelemetryTest.kt
@@ -106,6 +106,14 @@ class OpenTelemetryTest {
)
}
+ @Test
+ fun `toOTMetric maps every known navigation pair to its OTel name`() {
+ val navigation = MetricCategory.Navigation.categoryName
+
+ assertEquals("expo.navigation.tti", nameFor(navigation, "tti"))
+ assertEquals("expo.navigation.ttr", nameFor(navigation, "ttr"))
+ }
+
@Test
fun `toOTMetric falls back to expo_unknown for an unmapped name in a known category`() {
// Known category, name is not in the map.
@@ -139,6 +147,30 @@ class OpenTelemetryTest {
)
}
+ fun `navigation metric carries route name and custom params attributes`() {
+ val metric = EASMetric(
+ sessionId = testSessionId,
+ timestamp = "2026-01-01T00:00:00.000Z",
+ category = "navigation",
+ name = "ttr",
+ value = 0.25,
+ routeName = "/home",
+ customParams = JsonObject(
+ mapOf(
+ "isInitial" to JsonPrimitive(true),
+ "isAppLaunch" to JsonPrimitive(false)
+ )
+ )
+ )
+ val attrs = metric.toOTMetric().gauge.dataPoints[0].attributes
+ .associate { it.key to it.value.stringValue }
+
+ assertEquals("/home", attrs["expo.route_name"])
+ val parsed = Json.parseToJsonElement(attrs["expo.custom_params"]!!).jsonObject
+ assertEquals(true, parsed["isInitial"]!!.jsonPrimitive.content.toBoolean())
+ assertEquals(false, parsed["isAppLaunch"]!!.jsonPrimitive.content.toBoolean())
+ }
+
// -- Metric structure --
@Test
diff --git a/packages/expo-observe/build/ObserveProvider.d.ts.map b/packages/expo-observe/build/ObserveProvider.d.ts.map
index e06d4bb392979e..757f2c5f14a4c8 100644
--- a/packages/expo-observe/build/ObserveProvider.d.ts.map
+++ b/packages/expo-observe/build/ObserveProvider.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"ObserveProvider.d.ts","sourceRoot":"","sources":["../src/ObserveProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAE9D"}
\ No newline at end of file
+{"version":3,"file":"ObserveProvider.d.ts","sourceRoot":"","sources":["../src/ObserveProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAI/C,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAE9D"}
\ No newline at end of file
diff --git a/packages/expo-observe/build/ObserveProvider.js b/packages/expo-observe/build/ObserveProvider.js
index 71d0902067a750..7fe3fc9c03e4fb 100644
--- a/packages/expo-observe/build/ObserveProvider.js
+++ b/packages/expo-observe/build/ObserveProvider.js
@@ -1,6 +1,7 @@
-import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
+import { jsx as _jsx } from "react/jsx-runtime";
import {} from 'react';
+import { ObserveRouterIntegrationProvider } from './integrations/expo-router/ObserveRouterIntegrationProvider';
export function ObserveProvider({ children }) {
- return _jsx(_Fragment, { children: children });
+ return _jsx(ObserveRouterIntegrationProvider, { children: children });
}
//# sourceMappingURL=ObserveProvider.js.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/ObserveProvider.js.map b/packages/expo-observe/build/ObserveProvider.js.map
index ee8dc503ea2d21..05294d58af21ca 100644
--- a/packages/expo-observe/build/ObserveProvider.js.map
+++ b/packages/expo-observe/build/ObserveProvider.js.map
@@ -1 +1 @@
-{"version":3,"file":"ObserveProvider.js","sourceRoot":"","sources":["../src/ObserveProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAA0B,MAAM,OAAO,CAAC;AAE/C,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAqB;IAC7D,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC","sourcesContent":["import { type PropsWithChildren } from 'react';\n\nexport function ObserveProvider({ children }: PropsWithChildren) {\n return <>{children}>;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"ObserveProvider.js","sourceRoot":"","sources":["../src/ObserveProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAA0B,MAAM,OAAO,CAAC;AAE/C,OAAO,EAAE,gCAAgC,EAAE,MAAM,6DAA6D,CAAC;AAE/G,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAqB;IAC7D,OAAO,KAAC,gCAAgC,cAAE,QAAQ,GAAoC,CAAC;AACzF,CAAC","sourcesContent":["import { type PropsWithChildren } from 'react';\n\nimport { ObserveRouterIntegrationProvider } from './integrations/expo-router/ObserveRouterIntegrationProvider';\n\nexport function ObserveProvider({ children }: PropsWithChildren) {\n return {children};\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts
new file mode 100644
index 00000000000000..a67453c281f846
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts
@@ -0,0 +1,5 @@
+import { type PropsWithChildren } from 'react';
+import { type RouterIntegrationStorage } from './storage';
+export declare const ObserveRouterIntegrationContext: import("react").Context;
+export declare function ObserveRouterIntegrationProvider({ children }: PropsWithChildren): import("react/jsx-runtime").JSX.Element;
+//# sourceMappingURL=ObserveRouterIntegrationProvider.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts.map b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts.map
new file mode 100644
index 00000000000000..769b1f09feb0fe
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"ObserveRouterIntegrationProvider.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/ObserveRouterIntegrationProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,iBAAiB,EAA+B,MAAM,OAAO,CAAC;AAI3F,OAAO,EAAkC,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE1F,eAAO,MAAM,+BAA+B,0DAAuD,CAAC;AAEpG,wBAAgB,gCAAgC,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAsB/E"}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js
new file mode 100644
index 00000000000000..831bd494b7c60a
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js
@@ -0,0 +1,20 @@
+import { jsx as _jsx } from "react/jsx-runtime";
+import { createContext, useEffect, useRef, useState } from 'react';
+import { initListeners, isInitialized } from './init';
+import { optionalRouter } from './router';
+import { createRouterIntegrationStorage } from './storage';
+export const ObserveRouterIntegrationContext = createContext(null);
+export function ObserveRouterIntegrationProvider({ children }) {
+ const [storage] = useState(() => isInitialized() ? createRouterIntegrationStorage() : null);
+ const prevInitialized = useRef(isInitialized());
+ if (prevInitialized.current !== isInitialized()) {
+ throw new Error(`[expo-observe] Router integration was ${isInitialized() ? 'enabled' : 'disabled'} after application mounted. Call ExpoObserve.configure() before mounting AppMetricsRoot.`);
+ }
+ useEffect(() => {
+ if (!storage || !optionalRouter)
+ return;
+ return initListeners(storage, optionalRouter.unstable_navigationEvents);
+ }, [storage]);
+ return (_jsx(ObserveRouterIntegrationContext.Provider, { value: storage, children: children }));
+}
+//# sourceMappingURL=ObserveRouterIntegrationProvider.js.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js.map b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js.map
new file mode 100644
index 00000000000000..d4a5459720d730
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/ObserveRouterIntegrationProvider.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ObserveRouterIntegrationProvider.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/ObserveRouterIntegrationProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAA0B,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE3F,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,8BAA8B,EAAiC,MAAM,WAAW,CAAC;AAE1F,MAAM,CAAC,MAAM,+BAA+B,GAAG,aAAa,CAAkC,IAAI,CAAC,CAAC;AAEpG,MAAM,UAAU,gCAAgC,CAAC,EAAE,QAAQ,EAAqB;IAC9E,MAAM,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAkC,GAAG,EAAE,CAC/D,aAAa,EAAE,CAAC,CAAC,CAAC,8BAA8B,EAAE,CAAC,CAAC,CAAC,IAAI,CAC1D,CAAC;IAEF,MAAM,eAAe,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IAChD,IAAI,eAAe,CAAC,OAAO,KAAK,aAAa,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,yCAAyC,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,0FAA0F,CAC5K,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc;YAAE,OAAO;QACxC,OAAO,aAAa,CAAC,OAAO,EAAE,cAAc,CAAC,yBAAyB,CAAC,CAAC;IAC1E,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,OAAO,CACL,KAAC,+BAA+B,CAAC,QAAQ,IAAC,KAAK,EAAE,OAAO,YACrD,QAAQ,GACgC,CAC5C,CAAC;AACJ,CAAC","sourcesContent":["import { createContext, type PropsWithChildren, useEffect, useRef, useState } from 'react';\n\nimport { initListeners, isInitialized } from './init';\nimport { optionalRouter } from './router';\nimport { createRouterIntegrationStorage, type RouterIntegrationStorage } from './storage';\n\nexport const ObserveRouterIntegrationContext = createContext(null);\n\nexport function ObserveRouterIntegrationProvider({ children }: PropsWithChildren) {\n const [storage] = useState(() =>\n isInitialized() ? createRouterIntegrationStorage() : null\n );\n\n const prevInitialized = useRef(isInitialized());\n if (prevInitialized.current !== isInitialized()) {\n throw new Error(\n `[expo-observe] Router integration was ${isInitialized() ? 'enabled' : 'disabled'} after application mounted. Call ExpoObserve.configure() before mounting AppMetricsRoot.`\n );\n }\n\n useEffect(() => {\n if (!storage || !optionalRouter) return;\n return initListeners(storage, optionalRouter.unstable_navigationEvents);\n }, [storage]);\n\n return (\n \n {children}\n \n );\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/index.d.ts b/packages/expo-observe/build/integrations/expo-router/index.d.ts
index 4cf39199526ce0..851f6163d06a63 100644
--- a/packages/expo-observe/build/integrations/expo-router/index.d.ts
+++ b/packages/expo-observe/build/integrations/expo-router/index.d.ts
@@ -1,4 +1,5 @@
export { isRouterInstalled } from './router';
export { useObserveForRouter } from './useObserveForRouter';
export { initRouterIntegration } from './init';
+export { ObserveRouterIntegrationProvider } from './ObserveRouterIntegrationProvider';
//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/index.d.ts.map b/packages/expo-observe/build/integrations/expo-router/index.d.ts.map
index 772d45ae9ee301..bb57eb4aa0af4f 100644
--- a/packages/expo-observe/build/integrations/expo-router/index.d.ts.map
+++ b/packages/expo-observe/build/integrations/expo-router/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/index.js b/packages/expo-observe/build/integrations/expo-router/index.js
index f08d54773c01ea..8f470bbd516dd0 100644
--- a/packages/expo-observe/build/integrations/expo-router/index.js
+++ b/packages/expo-observe/build/integrations/expo-router/index.js
@@ -1,4 +1,5 @@
export { isRouterInstalled } from './router';
export { useObserveForRouter } from './useObserveForRouter';
export { initRouterIntegration } from './init';
+export { ObserveRouterIntegrationProvider } from './ObserveRouterIntegrationProvider';
//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/index.js.map b/packages/expo-observe/build/integrations/expo-router/index.js.map
index 612a8fb4675e74..6e5092ecf61b11 100644
--- a/packages/expo-observe/build/integrations/expo-router/index.js.map
+++ b/packages/expo-observe/build/integrations/expo-router/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC","sourcesContent":["export { isRouterInstalled } from './router';\nexport { useObserveForRouter } from './useObserveForRouter';\nexport { initRouterIntegration } from './init';\n"]}
\ No newline at end of file
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC","sourcesContent":["export { isRouterInstalled } from './router';\nexport { useObserveForRouter } from './useObserveForRouter';\nexport { initRouterIntegration } from './init';\nexport { ObserveRouterIntegrationProvider } from './ObserveRouterIntegrationProvider';\n"]}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/init.d.ts b/packages/expo-observe/build/integrations/expo-router/init.d.ts
index a155cac7c92683..53f6f5748b7c36 100644
--- a/packages/expo-observe/build/integrations/expo-router/init.d.ts
+++ b/packages/expo-observe/build/integrations/expo-router/init.d.ts
@@ -1,3 +1,8 @@
+import { optionalRouter } from './router';
+import { type RouterIntegrationStorage } from './storage';
export declare const isInitialized: () => boolean;
export declare function initRouterIntegration(): void;
+type NavigationEvents = NonNullable['unstable_navigationEvents'];
+export declare function initListeners(storage: RouterIntegrationStorage, navigationEvents: NavigationEvents): () => void;
+export {};
//# sourceMappingURL=init.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/init.d.ts.map b/packages/expo-observe/build/integrations/expo-router/init.d.ts.map
index c739b1bfa334e7..e418e2d432b462 100644
--- a/packages/expo-observe/build/integrations/expo-router/init.d.ts.map
+++ b/packages/expo-observe/build/integrations/expo-router/init.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/init.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,eAAoB,CAAC;AAE/C,wBAAgB,qBAAqB,SAEpC"}
\ No newline at end of file
+{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/init.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAI1D,eAAO,MAAM,aAAa,eAAoB,CAAC;AAE/C,wBAAgB,qBAAqB,SAGpC;AAED,KAAK,gBAAgB,GAAG,WAAW,CAAC,OAAO,cAAc,CAAC,CAAC,2BAA2B,CAAC,CAAC;AAExF,wBAAgB,aAAa,CAC3B,OAAO,EAAE,wBAAwB,EACjC,gBAAgB,EAAE,gBAAgB,GACjC,MAAM,IAAI,CA4EZ"}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/init.js b/packages/expo-observe/build/integrations/expo-router/init.js
index d32e90536a2f0f..dfdb0cc9cb3cb9 100644
--- a/packages/expo-observe/build/integrations/expo-router/init.js
+++ b/packages/expo-observe/build/integrations/expo-router/init.js
@@ -1,6 +1,82 @@
+import AppMetrics from 'expo-app-metrics';
+import { optionalRouter } from './router';
+import {} from './storage';
let initialized = false;
export const isInitialized = () => initialized;
export function initRouterIntegration() {
initialized = true;
+ optionalRouter?.unstable_navigationEvents.enable();
+}
+export function initListeners(storage, navigationEvents) {
+ const appLaunchTime = performance.now();
+ const cleanup = new Set();
+ const unsubscribeAction = navigationEvents.addListener('actionDispatched', (event) => {
+ // TODO(@ubax): Handle screen preloading
+ // PRELOAD comes from router.prefetch() â a route warm-up, not a user
+ // navigation â so it must not seed dispatchTime.
+ if (event.actionType === 'PRELOAD')
+ return;
+ storage.pendingActions.push({
+ actionType: event.actionType,
+ dispatchTime: performance.now(),
+ });
+ });
+ cleanup.add(unsubscribeAction);
+ const unsubscribeFocus = navigationEvents.addListener('pageFocused', async (e) => {
+ // Snapshot both clocks once so every metric written below is stamped with
+ // the moment the focus event fired, not the moment `addCustomMetricToSession`
+ // happens to run after the awaited `getMainSession()` round-trip.
+ const now = performance.now();
+ const timestamp = new Date().toISOString();
+ const isInitial = !storage.renderedScreensIds.has(e.screenId);
+ storage.renderedScreensIds.add(e.screenId);
+ if (process.env.EXPO_OS !== 'android') {
+ return;
+ }
+ const mainSessionId = (await AppMetrics.getMainSession())?.id;
+ if (!mainSessionId) {
+ return;
+ }
+ if (!storage.hasRecordedInitialTtr) {
+ // Stored in seconds to match the OTel `unit = "s"` convention
+ const appLaunchTtrSeconds = (now - appLaunchTime) / 1000;
+ storage.hasRecordedInitialTtr = true;
+ AppMetrics.addCustomMetricToSession({
+ sessionId: mainSessionId,
+ timestamp,
+ category: 'navigation',
+ name: 'ttr',
+ routeName: e.pathname,
+ value: appLaunchTtrSeconds,
+ params: { isInitial, isAppLaunch: true, routeParams: e.params },
+ });
+ return;
+ }
+ if (storage.pendingActions.length === 0)
+ return;
+ const last = storage.pendingActions[storage.pendingActions.length - 1];
+ if (last) {
+ const dispatchTime = last.dispatchTime;
+ storage.screenTimes[e.screenId] = {
+ ...storage.screenTimes[e.screenId],
+ dispatchTime,
+ };
+ AppMetrics.addCustomMetricToSession({
+ sessionId: mainSessionId,
+ timestamp,
+ category: 'navigation',
+ name: 'ttr',
+ routeName: e.pathname,
+ value: (now - dispatchTime) / 1000,
+ params: { isInitial, isAppLaunch: false, routeParams: e.params },
+ });
+ }
+ storage.pendingActions.length = 0;
+ });
+ cleanup.add(unsubscribeFocus);
+ return () => {
+ cleanup.forEach((c) => c());
+ cleanup.clear();
+ };
}
//# sourceMappingURL=init.js.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/init.js.map b/packages/expo-observe/build/integrations/expo-router/init.js.map
index bd35f87018a822..ab99b9effcea18 100644
--- a/packages/expo-observe/build/integrations/expo-router/init.js.map
+++ b/packages/expo-observe/build/integrations/expo-router/init.js.map
@@ -1 +1 @@
-{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/init.ts"],"names":[],"mappings":"AAAA,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC;AAE/C,MAAM,UAAU,qBAAqB;IACnC,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC","sourcesContent":["let initialized = false;\n\nexport const isInitialized = () => initialized;\n\nexport function initRouterIntegration() {\n initialized = true;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/init.ts"],"names":[],"mappings":"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","sourcesContent":["import AppMetrics from 'expo-app-metrics';\n\nimport { optionalRouter } from './router';\nimport { type RouterIntegrationStorage } from './storage';\n\nlet initialized = false;\n\nexport const isInitialized = () => initialized;\n\nexport function initRouterIntegration() {\n initialized = true;\n optionalRouter?.unstable_navigationEvents.enable();\n}\n\ntype NavigationEvents = NonNullable['unstable_navigationEvents'];\n\nexport function initListeners(\n storage: RouterIntegrationStorage,\n navigationEvents: NavigationEvents\n): () => void {\n const appLaunchTime = performance.now();\n const cleanup = new Set<() => void>();\n\n const unsubscribeAction = navigationEvents.addListener('actionDispatched', (event) => {\n // TODO(@ubax): Handle screen preloading\n // PRELOAD comes from router.prefetch() â a route warm-up, not a user\n // navigation â so it must not seed dispatchTime.\n if (event.actionType === 'PRELOAD') return;\n storage.pendingActions.push({\n actionType: event.actionType,\n dispatchTime: performance.now(),\n });\n });\n cleanup.add(unsubscribeAction);\n\n const unsubscribeFocus = navigationEvents.addListener('pageFocused', async (e) => {\n // Snapshot both clocks once so every metric written below is stamped with\n // the moment the focus event fired, not the moment `addCustomMetricToSession`\n // happens to run after the awaited `getMainSession()` round-trip.\n const now = performance.now();\n const timestamp = new Date().toISOString();\n const isInitial = !storage.renderedScreensIds.has(e.screenId);\n storage.renderedScreensIds.add(e.screenId);\n if (process.env.EXPO_OS !== 'android') {\n return;\n }\n const mainSessionId = (await AppMetrics.getMainSession())?.id;\n if (!mainSessionId) {\n return;\n }\n\n if (!storage.hasRecordedInitialTtr) {\n // Stored in seconds to match the OTel `unit = \"s\"` convention\n const appLaunchTtrSeconds = (now - appLaunchTime) / 1000;\n storage.hasRecordedInitialTtr = true;\n AppMetrics.addCustomMetricToSession({\n sessionId: mainSessionId,\n timestamp,\n category: 'navigation',\n name: 'ttr',\n routeName: e.pathname,\n value: appLaunchTtrSeconds,\n params: { isInitial, isAppLaunch: true, routeParams: e.params },\n });\n return;\n }\n\n if (storage.pendingActions.length === 0) return;\n\n const last = storage.pendingActions[storage.pendingActions.length - 1];\n if (last) {\n const dispatchTime = last.dispatchTime;\n storage.screenTimes[e.screenId] = {\n ...storage.screenTimes[e.screenId],\n dispatchTime,\n };\n\n AppMetrics.addCustomMetricToSession({\n sessionId: mainSessionId,\n timestamp,\n category: 'navigation',\n name: 'ttr',\n routeName: e.pathname,\n value: (now - dispatchTime) / 1000,\n params: { isInitial, isAppLaunch: false, routeParams: e.params },\n });\n }\n storage.pendingActions.length = 0;\n });\n cleanup.add(unsubscribeFocus);\n\n return () => {\n cleanup.forEach((c) => c());\n cleanup.clear();\n };\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/storage.d.ts b/packages/expo-observe/build/integrations/expo-router/storage.d.ts
new file mode 100644
index 00000000000000..6beb2565038d99
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/storage.d.ts
@@ -0,0 +1,27 @@
+import type { ActionDispatchedEvent } from 'expo-router';
+export interface ScreenTimes {
+ dispatchTime: number;
+ lastInteractiveCall?: number;
+}
+export interface PendingAction {
+ actionType: ActionDispatchedEvent['actionType'];
+ dispatchTime: number;
+}
+export interface RouterIntegrationStorage {
+ /**
+ * Actions dispatched, but not yet processed by the integration
+ */
+ pendingActions: PendingAction[];
+ renderedScreensIds: Set;
+ /**
+ * Wether the app had already recorded the first render of the screen
+ */
+ hasRecordedInitialTtr: boolean;
+ /**
+ * Times used to calculate spans from dispatch to certain event
+ */
+ screenTimes: Record;
+ interactiveScreensIds: Set;
+}
+export declare function createRouterIntegrationStorage(): RouterIntegrationStorage;
+//# sourceMappingURL=storage.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/storage.d.ts.map b/packages/expo-observe/build/integrations/expo-router/storage.d.ts.map
new file mode 100644
index 00000000000000..718b9a4d2056dc
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/storage.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAChD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC;;OAEG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACzC,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpC;AAED,wBAAgB,8BAA8B,IAAI,wBAAwB,CAQzE"}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/storage.js b/packages/expo-observe/build/integrations/expo-router/storage.js
new file mode 100644
index 00000000000000..4afe519b4a7069
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/storage.js
@@ -0,0 +1,10 @@
+export function createRouterIntegrationStorage() {
+ return {
+ pendingActions: [],
+ renderedScreensIds: new Set(),
+ hasRecordedInitialTtr: false,
+ screenTimes: {},
+ interactiveScreensIds: new Set(),
+ };
+}
+//# sourceMappingURL=storage.js.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/storage.js.map b/packages/expo-observe/build/integrations/expo-router/storage.js.map
new file mode 100644
index 00000000000000..455bfab2925956
--- /dev/null
+++ b/packages/expo-observe/build/integrations/expo-router/storage.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/storage.ts"],"names":[],"mappings":"AA6BA,MAAM,UAAU,8BAA8B;IAC5C,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,kBAAkB,EAAE,IAAI,GAAG,EAAE;QAC7B,qBAAqB,EAAE,KAAK;QAC5B,WAAW,EAAE,EAAE;QACf,qBAAqB,EAAE,IAAI,GAAG,EAAE;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import type { ActionDispatchedEvent } from 'expo-router';\n\nexport interface ScreenTimes {\n dispatchTime: number;\n lastInteractiveCall?: number;\n}\n\nexport interface PendingAction {\n actionType: ActionDispatchedEvent['actionType'];\n dispatchTime: number;\n}\n\nexport interface RouterIntegrationStorage {\n /**\n * Actions dispatched, but not yet processed by the integration\n */\n pendingActions: PendingAction[];\n renderedScreensIds: Set;\n /**\n * Wether the app had already recorded the first render of the screen\n */\n hasRecordedInitialTtr: boolean;\n /**\n * Times used to calculate spans from dispatch to certain event\n */\n screenTimes: Record;\n interactiveScreensIds: Set;\n}\n\nexport function createRouterIntegrationStorage(): RouterIntegrationStorage {\n return {\n pendingActions: [],\n renderedScreensIds: new Set(),\n hasRecordedInitialTtr: false,\n screenTimes: {},\n interactiveScreensIds: new Set(),\n };\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.d.ts.map b/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.d.ts.map
index d1263be6820ee2..4778980905632d 100644
--- a/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.d.ts.map
+++ b/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"useObserveForRouter.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/useObserveForRouter.ts"],"names":[],"mappings":"AAAA,OAAO,UAAqC,MAAM,kBAAkB,CAAC;AAMrE,KAAK,eAAe,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAE9D,wBAAgB,mBAAmB,IAAI,eAAe,GAAG,IAAI,CAwD5D"}
\ No newline at end of file
+{"version":3,"file":"useObserveForRouter.d.ts","sourceRoot":"","sources":["../../../src/integrations/expo-router/useObserveForRouter.ts"],"names":[],"mappings":"AAAA,OAAO,UAAqC,MAAM,kBAAkB,CAAC;AAOrE,KAAK,eAAe,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAE9D,wBAAgB,mBAAmB,IAAI,eAAe,GAAG,IAAI,CA8G5D"}
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js b/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js
index c5a6d2a99614f5..dae34909569b0e 100644
--- a/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js
+++ b/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js
@@ -1,12 +1,15 @@
import AppMetrics, {} from 'expo-app-metrics';
-import { useCallback, useEffect, useRef } from 'react';
+import { use, useCallback, useEffect, useRef } from 'react';
+import { ObserveRouterIntegrationContext } from './ObserveRouterIntegrationProvider';
import { isInitialized } from './init';
import { optionalRouter } from './router';
export function useObserveForRouter() {
+ const storage = use(ObserveRouterIntegrationContext);
const isMounted = useRef(true);
const route = optionalRouter?.useRoute();
const navigation = optionalRouter?.useNavigation();
- const pathname = optionalRouter?.useCurrentRouteInfo()?.pathname;
+ const routeInfo = optionalRouter?.useCurrentRouteInfo();
+ const { pathname, params: routeParams } = routeInfo ?? {};
const initializedAtMount = useRef(isInitialized());
if (initializedAtMount.current !== isInitialized()) {
throw new Error("[expo-observe] Router integration was toggled during a screen's lifecycle. " +
@@ -28,6 +31,8 @@ export function useObserveForRouter() {
};
}, []);
const markInteractive = useCallback(async (attributes) => {
+ const now = performance.now();
+ const timestamp = new Date().toISOString();
if (!isMounted.current) {
console.warn('[expo-observe] Calling markInteractive on unmounted screen');
return;
@@ -42,7 +47,48 @@ export function useObserveForRouter() {
routeName: pathname,
});
}
- }, [screenId, navigation, pathname]);
+ if (process.env.EXPO_OS !== 'android') {
+ return;
+ }
+ if (!storage) {
+ throw new Error('[expo-observe] markInteractive was called without an active ObserveProvider. Wrap your app in ObserveRoot from expo-observe.');
+ }
+ // Snapshot times BEFORE writing the new interactive timestamp so the
+ // duplicate-detection logic below sees the *previous* call, not this one.
+ const currentScreenData = storage.screenTimes[screenId];
+ storage.interactiveScreensIds.add(screenId);
+ if (storage.screenTimes[screenId]) {
+ storage.screenTimes[screenId] = {
+ ...storage.screenTimes[screenId],
+ lastInteractiveCall: now,
+ };
+ }
+ if (!currentScreenData?.dispatchTime)
+ return;
+ const previousInteractiveCall = currentScreenData.lastInteractiveCall;
+ const previousWasAfterDispatch = previousInteractiveCall != null && currentScreenData.dispatchTime < previousInteractiveCall;
+ if (previousWasAfterDispatch) {
+ // We only want to record interactive once per navigation
+ return;
+ }
+ // Stored in seconds to match the OTel `unit = "s"` convention
+ const interactiveTimeSeconds = (now - currentScreenData.dispatchTime) / 1000;
+ const mainSessionId = (await AppMetrics.getMainSession())?.id;
+ // TODO(@ubax): we should count the time against the action which caused the first navigation
+ // and add a param stating if during that time there was any navigation
+ if (mainSessionId) {
+ await AppMetrics.addCustomMetricToSession({
+ sessionId: mainSessionId,
+ timestamp,
+ category: 'navigation',
+ // TODO(@ubax): Use segments.join here to get full routeName and pass pathname and params via params
+ routeName: pathname,
+ name: 'tti',
+ value: interactiveTimeSeconds,
+ params: { routeParams },
+ });
+ }
+ }, [screenId, navigation, pathname, storage, routeParams]);
return initializedAtMount.current ? markInteractive : null;
}
//# sourceMappingURL=useObserveForRouter.js.map
\ No newline at end of file
diff --git a/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js.map b/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js.map
index 986fb74c74bd02..a7dc09884110f4 100644
--- a/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js.map
+++ b/packages/expo-observe/build/integrations/expo-router/useObserveForRouter.js.map
@@ -1 +1 @@
-{"version":3,"file":"useObserveForRouter.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/useObserveForRouter.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,EAAE,EAAyB,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI1C,MAAM,UAAU,mBAAmB;IACjC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,cAAc,EAAE,QAAQ,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,cAAc,EAAE,aAAa,EAAE,CAAC;IACnD,MAAM,QAAQ,GAAG,cAAc,EAAE,mBAAmB,EAAE,EAAE,QAAQ,CAAC;IAEjE,MAAM,kBAAkB,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IACnD,IAAI,kBAAkB,CAAC,OAAO,KAAK,aAAa,EAAE,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,6EAA6E;YAC3E,sGAAsG,CACzG,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,EAAE,GAAG,CAAC;IAC5B,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,YAAY,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CACV,2FAA2F,CAC5F,CAAC;QACF,YAAY,CAAC,OAAO,GAAG,QAAQ,CAAC;IAClC,CAAC;IAED,SAAS,CAAC,GAAG,EAAE;QACb,4EAA4E;QAC5E,8EAA8E;QAC9E,gEAAgE;QAChE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC;QACzB,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,OAAO,GAAG,KAAK,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,eAAe,GAAG,WAAW,CACjC,KAAK,EAAE,UAA6B,EAAE,EAAE;QACtC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;YAC3E,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CACV,sHAAsH,CACvH,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC;YAC5B,UAAU,CAAC,eAAe,CAAC;gBACzB,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;gBACrB,SAAS,EAAE,QAAQ;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CACjC,CAAC;IAEF,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC","sourcesContent":["import AppMetrics, { type MetricAttributes } from 'expo-app-metrics';\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { isInitialized } from './init';\nimport { optionalRouter } from './router';\n\ntype MarkInteractive = (typeof AppMetrics)['markInteractive'];\n\nexport function useObserveForRouter(): MarkInteractive | null {\n const isMounted = useRef(true);\n const route = optionalRouter?.useRoute();\n const navigation = optionalRouter?.useNavigation();\n const pathname = optionalRouter?.useCurrentRouteInfo()?.pathname;\n\n const initializedAtMount = useRef(isInitialized());\n if (initializedAtMount.current !== isInitialized()) {\n throw new Error(\n \"[expo-observe] Router integration was toggled during a screen's lifecycle. \" +\n 'Call `ExpoObserve.configure({ disableRouterIntegration })` once at startup before any screen mounts.'\n );\n }\n\n const screenId = route?.key;\n const prevScreenId = useRef(screenId);\n if (prevScreenId.current !== screenId) {\n console.warn(\n '[expo-observe] Screen ID changed between renders. This is most likely an expo-router bug.'\n );\n prevScreenId.current = screenId;\n }\n\n useEffect(() => {\n // Strict-mode mounts the effect twice (mount â cleanup â re-mount). Without\n // restoring isMounted here, the second mount would leave it permanently false\n // and every markInteractive call would warn \"unmounted screen\".\n isMounted.current = true;\n return () => {\n isMounted.current = false;\n };\n }, []);\n\n const markInteractive = useCallback(\n async (attributes?: MetricAttributes) => {\n if (!isMounted.current) {\n console.warn('[expo-observe] Calling markInteractive on unmounted screen');\n return;\n }\n if (!screenId) {\n console.warn(\n '[expo-observe] No metadata available for the current screen. Make sure to call useObserve inside a screen component.'\n );\n return;\n }\n if (navigation?.isFocused()) {\n AppMetrics.markInteractive({\n ...(attributes ?? {}),\n routeName: pathname,\n });\n }\n },\n [screenId, navigation, pathname]\n );\n\n return initializedAtMount.current ? markInteractive : null;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"useObserveForRouter.js","sourceRoot":"","sources":["../../../src/integrations/expo-router/useObserveForRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import AppMetrics, { type MetricAttributes } from 'expo-app-metrics';\nimport { use, useCallback, useEffect, useRef } from 'react';\n\nimport { ObserveRouterIntegrationContext } from './ObserveRouterIntegrationProvider';\nimport { isInitialized } from './init';\nimport { optionalRouter } from './router';\n\ntype MarkInteractive = (typeof AppMetrics)['markInteractive'];\n\nexport function useObserveForRouter(): MarkInteractive | null {\n const storage = use(ObserveRouterIntegrationContext);\n const isMounted = useRef(true);\n const route = optionalRouter?.useRoute();\n const navigation = optionalRouter?.useNavigation();\n const routeInfo = optionalRouter?.useCurrentRouteInfo();\n const { pathname, params: routeParams } = routeInfo ?? {};\n\n const initializedAtMount = useRef(isInitialized());\n if (initializedAtMount.current !== isInitialized()) {\n throw new Error(\n \"[expo-observe] Router integration was toggled during a screen's lifecycle. \" +\n 'Call `ExpoObserve.configure({ disableRouterIntegration })` once at startup before any screen mounts.'\n );\n }\n\n const screenId = route?.key;\n const prevScreenId = useRef(screenId);\n if (prevScreenId.current !== screenId) {\n console.warn(\n '[expo-observe] Screen ID changed between renders. This is most likely an expo-router bug.'\n );\n prevScreenId.current = screenId;\n }\n\n useEffect(() => {\n // Strict-mode mounts the effect twice (mount â cleanup â re-mount). Without\n // restoring isMounted here, the second mount would leave it permanently false\n // and every markInteractive call would warn \"unmounted screen\".\n isMounted.current = true;\n return () => {\n isMounted.current = false;\n };\n }, []);\n\n const markInteractive = useCallback(\n async (attributes?: MetricAttributes) => {\n const now = performance.now();\n const timestamp = new Date().toISOString();\n if (!isMounted.current) {\n console.warn('[expo-observe] Calling markInteractive on unmounted screen');\n return;\n }\n if (!screenId) {\n console.warn(\n '[expo-observe] No metadata available for the current screen. Make sure to call useObserve inside a screen component.'\n );\n return;\n }\n if (navigation?.isFocused()) {\n AppMetrics.markInteractive({\n ...(attributes ?? {}),\n routeName: pathname,\n });\n }\n\n if (process.env.EXPO_OS !== 'android') {\n return;\n }\n if (!storage) {\n throw new Error(\n '[expo-observe] markInteractive was called without an active ObserveProvider. Wrap your app in ObserveRoot from expo-observe.'\n );\n }\n\n // Snapshot times BEFORE writing the new interactive timestamp so the\n // duplicate-detection logic below sees the *previous* call, not this one.\n const currentScreenData = storage.screenTimes[screenId];\n\n storage.interactiveScreensIds.add(screenId);\n if (storage.screenTimes[screenId]) {\n storage.screenTimes[screenId] = {\n ...storage.screenTimes[screenId],\n lastInteractiveCall: now,\n };\n }\n\n if (!currentScreenData?.dispatchTime) return;\n\n const previousInteractiveCall = currentScreenData.lastInteractiveCall;\n const previousWasAfterDispatch =\n previousInteractiveCall != null && currentScreenData.dispatchTime < previousInteractiveCall;\n\n if (previousWasAfterDispatch) {\n // We only want to record interactive once per navigation\n return;\n }\n\n // Stored in seconds to match the OTel `unit = \"s\"` convention\n const interactiveTimeSeconds = (now - currentScreenData.dispatchTime) / 1000;\n const mainSessionId = (await AppMetrics.getMainSession())?.id;\n // TODO(@ubax): we should count the time against the action which caused the first navigation\n // and add a param stating if during that time there was any navigation\n if (mainSessionId) {\n await AppMetrics.addCustomMetricToSession({\n sessionId: mainSessionId,\n timestamp,\n category: 'navigation',\n // TODO(@ubax): Use segments.join here to get full routeName and pass pathname and params via params\n routeName: pathname,\n name: 'tti',\n value: interactiveTimeSeconds,\n params: { routeParams },\n });\n }\n },\n [screenId, navigation, pathname, storage, routeParams]\n );\n\n return initializedAtMount.current ? markInteractive : null;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-observe/src/ObserveProvider.tsx b/packages/expo-observe/src/ObserveProvider.tsx
index 3a267894c72350..94e8871c3906a4 100644
--- a/packages/expo-observe/src/ObserveProvider.tsx
+++ b/packages/expo-observe/src/ObserveProvider.tsx
@@ -1,5 +1,7 @@
import { type PropsWithChildren } from 'react';
+import { ObserveRouterIntegrationProvider } from './integrations/expo-router/ObserveRouterIntegrationProvider';
+
export function ObserveProvider({ children }: PropsWithChildren) {
- return <>{children}>;
+ return {children};
}
diff --git a/packages/expo-observe/src/integrations/expo-router/ObserveRouterIntegrationProvider.tsx b/packages/expo-observe/src/integrations/expo-router/ObserveRouterIntegrationProvider.tsx
new file mode 100644
index 00000000000000..85ae8ea87fc68d
--- /dev/null
+++ b/packages/expo-observe/src/integrations/expo-router/ObserveRouterIntegrationProvider.tsx
@@ -0,0 +1,31 @@
+import { createContext, type PropsWithChildren, useEffect, useRef, useState } from 'react';
+
+import { initListeners, isInitialized } from './init';
+import { optionalRouter } from './router';
+import { createRouterIntegrationStorage, type RouterIntegrationStorage } from './storage';
+
+export const ObserveRouterIntegrationContext = createContext(null);
+
+export function ObserveRouterIntegrationProvider({ children }: PropsWithChildren) {
+ const [storage] = useState(() =>
+ isInitialized() ? createRouterIntegrationStorage() : null
+ );
+
+ const prevInitialized = useRef(isInitialized());
+ if (prevInitialized.current !== isInitialized()) {
+ throw new Error(
+ `[expo-observe] Router integration was ${isInitialized() ? 'enabled' : 'disabled'} after application mounted. Call ExpoObserve.configure() before mounting AppMetricsRoot.`
+ );
+ }
+
+ useEffect(() => {
+ if (!storage || !optionalRouter) return;
+ return initListeners(storage, optionalRouter.unstable_navigationEvents);
+ }, [storage]);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/packages/expo-observe/src/integrations/expo-router/__tests__/ObserveRouterIntegrationProvider.test.native.tsx b/packages/expo-observe/src/integrations/expo-router/__tests__/ObserveRouterIntegrationProvider.test.native.tsx
new file mode 100644
index 00000000000000..5bba15b8388630
--- /dev/null
+++ b/packages/expo-observe/src/integrations/expo-router/__tests__/ObserveRouterIntegrationProvider.test.native.tsx
@@ -0,0 +1,130 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+import { render } from '@testing-library/react-native';
+import { use } from 'react';
+import { Text } from 'react-native';
+
+import {
+ ObserveRouterIntegrationContext,
+ ObserveRouterIntegrationProvider,
+} from '../ObserveRouterIntegrationProvider';
+import * as initModule from '../init';
+
+jest.mock('expo-app-metrics', () => ({
+ __esModule: true,
+ default: {
+ markInteractive: jest.fn(),
+ getMainSessionId: jest.fn(() => 'session-1'),
+ addCustomMetricToSession: jest.fn(),
+ },
+}));
+
+jest.mock('../init', () => {
+ const initListenersCleanup = jest.fn();
+ return {
+ __esModule: true,
+ isInitialized: jest.fn(() => true),
+ initListeners: jest.fn(() => initListenersCleanup),
+ initRouterIntegration: jest.fn(),
+ __initListenersCleanup: initListenersCleanup,
+ };
+});
+
+jest.mock('../router', () => ({
+ optionalRouter: { unstable_navigationEvents: { addListener: jest.fn(), emit: jest.fn() } },
+ isRouterInstalled: true,
+}));
+
+const mockIsInitialized = initModule.isInitialized as jest.Mock;
+const mockInitListeners = initModule.initListeners as jest.Mock;
+const mockInitListenersCleanup = (initModule as unknown as { __initListenersCleanup: jest.Mock })
+ .__initListenersCleanup;
+
+function StorageProbe({ onRead }: { onRead: (storage: unknown) => void }) {
+ const storage = use(ObserveRouterIntegrationContext);
+ onRead(storage);
+ return probe;
+}
+
+beforeEach(() => {
+ mockIsInitialized.mockReturnValue(true);
+ mockIsInitialized.mockClear();
+ mockInitListeners.mockClear();
+ mockInitListenersCleanup.mockClear();
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
+});
+
+describe('ObserveRouterIntegrationProvider', () => {
+ it('exposes a non-null storage on first render when isInitialized() is true at mount', () => {
+ const reads: unknown[] = [];
+ render(
+
+ reads.push(s)} />
+
+ );
+ expect(reads[0]).toBeTruthy();
+ expect(
+ (reads[0] as { interactiveScreensIds: Set }).interactiveScreensIds
+ ).toBeInstanceOf(Set);
+ });
+
+ it('keeps storage null and does not attach listeners when isInitialized() is false at mount', () => {
+ mockIsInitialized.mockReturnValue(false);
+ const reads: unknown[] = [];
+ render(
+
+ reads.push(s)} />
+
+ );
+ expect(reads[0]).toBeNull();
+ expect(mockInitListeners).not.toHaveBeenCalled();
+ });
+
+ it('calls initListeners with storage in useEffect and runs cleanup on unmount', () => {
+ const { unmount } = render(
+
+ child
+
+ );
+ expect(mockInitListeners).toHaveBeenCalledTimes(1);
+ const [storageArg] = mockInitListeners.mock.calls[0];
+ expect(
+ (storageArg as { interactiveScreensIds: Set }).interactiveScreensIds
+ ).toBeInstanceOf(Set);
+
+ expect(mockInitListenersCleanup).not.toHaveBeenCalled();
+ unmount();
+ expect(mockInitListenersCleanup).toHaveBeenCalledTimes(1);
+ });
+
+ it('throws when isInitialized() flips during the provider lifetime', () => {
+ mockIsInitialized.mockReturnValue(false);
+ jest.spyOn(console, 'error').mockImplementation(() => {});
+
+ const { rerender } = render(
+
+ child
+
+ );
+
+ mockIsInitialized.mockReturnValue(true);
+ expect(() =>
+ rerender(
+
+ child
+
+ )
+ ).toThrow(
+ '[expo-observe] Router integration was enabled after application mounted. Call ExpoObserve.configure() before mounting AppMetricsRoot.'
+ );
+ });
+
+ it('renders children when storage is null (router not installed scenario)', () => {
+ mockIsInitialized.mockReturnValue(false);
+ const { getByText } = render(
+
+ visible
+
+ );
+ expect(getByText('visible')).toBeTruthy();
+ });
+});
diff --git a/packages/expo-observe/src/integrations/expo-router/__tests__/init.test.android.ts b/packages/expo-observe/src/integrations/expo-router/__tests__/init.test.android.ts
new file mode 100644
index 00000000000000..4d500a1704ba3f
--- /dev/null
+++ b/packages/expo-observe/src/integrations/expo-router/__tests__/init.test.android.ts
@@ -0,0 +1,203 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+import AppMetrics from 'expo-app-metrics';
+import type { ActionDispatchedEvent, PageFocusedEvent } from 'expo-router';
+
+import { initListeners } from '../init';
+import { createRouterIntegrationStorage, type RouterIntegrationStorage } from '../storage';
+
+jest.mock('expo-app-metrics', () => {
+ const addCustomMetricToSession = jest.fn();
+ const getMainSession = jest.fn(async () => ({ id: 'session-1' }));
+ return {
+ __esModule: true,
+ default: {
+ markInteractive: jest.fn(),
+ getMainSession,
+ addCustomMetricToSession,
+ },
+ };
+});
+
+jest.mock('../router', () => ({ optionalRouter: undefined, isRouterInstalled: false }));
+
+const mockGetMainSession = AppMetrics.getMainSession as jest.Mock;
+const mockAddCustomMetric = AppMetrics.addCustomMetricToSession as jest.Mock;
+const mockSessionId = 'session-1';
+
+type Listener = (event: T) => void;
+
+interface FakeNavigationEvents {
+ addListener(type: string, cb: Listener): () => void;
+ emit(type: string, event: T): void;
+}
+
+function createFakeNavigationEvents(): FakeNavigationEvents {
+ const listeners: Record>> = {};
+ return {
+ addListener(type, cb) {
+ listeners[type] = listeners[type] ?? new Set();
+ listeners[type].add(cb);
+ return () => listeners[type].delete(cb);
+ },
+ emit(type, event) {
+ listeners[type]?.forEach((cb) => cb(event));
+ },
+ };
+}
+
+function dispatch(events: FakeNavigationEvents, actionType: string) {
+ events.emit>('actionDispatched', {
+ type: 'actionDispatched',
+ actionType: actionType as ActionDispatchedEvent['actionType'],
+ });
+}
+
+function focus(events: FakeNavigationEvents, screenId: string) {
+ events.emit>('pageFocused', {
+ type: 'pageFocused',
+ screenId,
+ pathname: `/${screenId}`,
+ params: {},
+ });
+}
+
+function flushAsync() {
+ return new Promise((resolve) => setImmediate(resolve));
+}
+
+let storage: RouterIntegrationStorage;
+let events: FakeNavigationEvents;
+let cleanup: () => void;
+let logSpy: jest.SpyInstance;
+let warnSpy: jest.SpyInstance;
+
+beforeEach(() => {
+ jest.clearAllMocks();
+ logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+ warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
+ storage = createRouterIntegrationStorage();
+ events = createFakeNavigationEvents();
+ cleanup = initListeners(storage, events as any);
+});
+
+afterEach(() => {
+ cleanup?.();
+ expect(logSpy).not.toHaveBeenCalled();
+ expect(warnSpy).not.toHaveBeenCalled();
+ jest.clearAllMocks();
+});
+
+describe('initListeners (android)', () => {
+ it('records TTR with isAppLaunch=true on the first focus after a non-PRELOAD action', async () => {
+ const now = performance.now();
+ jest.spyOn(performance, 'now').mockReturnValue(now + 100);
+ focus(events, 'a');
+ await flushAsync();
+
+ expect(mockAddCustomMetric).toHaveBeenCalledTimes(1);
+ expect(mockAddCustomMetric).toHaveBeenCalledWith({
+ sessionId: mockSessionId,
+ timestamp: expect.any(String),
+ category: 'navigation',
+ name: 'ttr',
+ routeName: '/a',
+ value: expect.closeTo(0.1, 2),
+ params: { isInitial: true, isAppLaunch: true, routeParams: {} },
+ });
+ });
+
+ it('records TTR with isAppLaunch=false on subsequent focuses', async () => {
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'a');
+ await flushAsync();
+ mockAddCustomMetric.mockClear();
+
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'b');
+ await flushAsync();
+
+ expect(mockAddCustomMetric).toHaveBeenCalledTimes(1);
+ expect(mockAddCustomMetric.mock.calls[0][0].params).toEqual({
+ isInitial: true,
+ isAppLaunch: false,
+ routeParams: {},
+ });
+ });
+
+ it('records TTR with isInitial=false when revisiting a previously rendered screen', async () => {
+ focus(events, 'a');
+ await flushAsync();
+
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'b');
+ await flushAsync();
+
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'a');
+ await flushAsync();
+
+ expect(mockAddCustomMetric).toHaveBeenCalledTimes(3);
+ expect(mockAddCustomMetric.mock.calls[0][0].params).toEqual({
+ isInitial: true,
+ isAppLaunch: true,
+ routeParams: {},
+ });
+ expect(mockAddCustomMetric.mock.calls[1][0].params).toEqual({
+ isInitial: true,
+ isAppLaunch: false,
+ routeParams: {},
+ });
+ expect(mockAddCustomMetric.mock.calls[2][0].params).toEqual({
+ isInitial: false,
+ isAppLaunch: false,
+ routeParams: {},
+ });
+ });
+
+ it('does not record a TTR for a PRELOAD action', async () => {
+ storage.hasRecordedInitialTtr = true;
+
+ dispatch(events, 'PRELOAD');
+ focus(events, 'a');
+ await flushAsync();
+ expect(mockAddCustomMetric).not.toHaveBeenCalled();
+ });
+
+ it('cleanup unsubscribes both listeners', async () => {
+ cleanup();
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'a');
+ await flushAsync();
+ expect(mockAddCustomMetric).not.toHaveBeenCalled();
+ expect(storage.pendingActions).toHaveLength(0);
+ cleanup = () => {};
+ });
+
+ it('uses getMainSession from AppMetrics for the metric session id', async () => {
+ mockGetMainSession.mockResolvedValueOnce({ id: 'custom-session' });
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'a');
+ await new Promise((resolve) => setImmediate(resolve));
+ expect(mockAddCustomMetric).toHaveBeenCalledWith(
+ expect.objectContaining({ sessionId: 'custom-session' })
+ );
+ });
+});
+
+describe('isInitialized + initRouterIntegration', () => {
+ it('flips initialized when initRouterIntegration is called and stays decoupled from initListeners', () => {
+ jest.isolateModules(() => {
+ const init = require('../init');
+ expect(init.isInitialized()).toBe(false);
+
+ const fresh = createRouterIntegrationStorage();
+ const fakeEvents = createFakeNavigationEvents();
+ const dispose = init.initListeners(fresh, fakeEvents);
+ expect(init.isInitialized()).toBe(false);
+ dispose();
+
+ init.initRouterIntegration();
+ expect(init.isInitialized()).toBe(true);
+ });
+ });
+});
diff --git a/packages/expo-observe/src/integrations/expo-router/__tests__/init.test.ios.ts b/packages/expo-observe/src/integrations/expo-router/__tests__/init.test.ios.ts
new file mode 100644
index 00000000000000..6ddc69057e99fc
--- /dev/null
+++ b/packages/expo-observe/src/integrations/expo-router/__tests__/init.test.ios.ts
@@ -0,0 +1,85 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+import AppMetrics from 'expo-app-metrics';
+import type { ActionDispatchedEvent, PageFocusedEvent } from 'expo-router';
+
+import { initListeners } from '../init';
+import { createRouterIntegrationStorage, type RouterIntegrationStorage } from '../storage';
+
+jest.mock('expo-app-metrics', () => ({
+ __esModule: true,
+ default: {
+ markInteractive: jest.fn(),
+ getMainSessionId: jest.fn(() => 'session-1'),
+ addCustomMetricToSession: jest.fn(),
+ },
+}));
+
+jest.mock('../router', () => ({ optionalRouter: undefined, isRouterInstalled: false }));
+
+const mockAddCustomMetric = AppMetrics.addCustomMetricToSession as jest.Mock;
+
+type Listener = (event: T) => void;
+
+interface FakeNavigationEvents {
+ addListener(type: string, cb: Listener): () => void;
+ emit(type: string, event: T): void;
+}
+
+function createFakeNavigationEvents(): FakeNavigationEvents {
+ const listeners: Record>> = {};
+ return {
+ addListener(type, cb) {
+ listeners[type] = listeners[type] ?? new Set();
+ listeners[type].add(cb);
+ return () => listeners[type].delete(cb);
+ },
+ emit(type, event) {
+ listeners[type]?.forEach((cb) => cb(event));
+ },
+ };
+}
+
+function dispatch(events: FakeNavigationEvents, actionType: string) {
+ events.emit>('actionDispatched', {
+ type: 'actionDispatched',
+ actionType: actionType as ActionDispatchedEvent['actionType'],
+ });
+}
+
+function focus(events: FakeNavigationEvents, screenId: string) {
+ events.emit>('pageFocused', {
+ type: 'pageFocused',
+ screenId,
+ pathname: `/${screenId}`,
+ params: {},
+ });
+}
+
+let storage: RouterIntegrationStorage;
+let events: FakeNavigationEvents;
+let cleanup: () => void;
+let warnSpy: jest.SpyInstance;
+let logSpy: jest.SpyInstance;
+
+beforeEach(() => {
+ jest.clearAllMocks();
+ warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
+ logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+ storage = createRouterIntegrationStorage();
+ events = createFakeNavigationEvents();
+ cleanup = initListeners(storage, events as any);
+});
+
+afterEach(() => {
+ cleanup?.();
+});
+
+describe('initListeners (ios)', () => {
+ it('does not record the TTR metric on iOS', () => {
+ dispatch(events, 'NAVIGATE');
+ focus(events, 'a');
+ expect(mockAddCustomMetric).not.toHaveBeenCalled();
+ expect(logSpy).not.toHaveBeenCalled();
+ expect(warnSpy).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/expo-observe/src/integrations/expo-router/__tests__/storage.test.ts b/packages/expo-observe/src/integrations/expo-router/__tests__/storage.test.ts
new file mode 100644
index 00000000000000..dd1b2250e985f3
--- /dev/null
+++ b/packages/expo-observe/src/integrations/expo-router/__tests__/storage.test.ts
@@ -0,0 +1,22 @@
+import { createRouterIntegrationStorage } from '../storage';
+
+describe('createRouterIntegrationStorage', () => {
+ it('returns isolated instances', () => {
+ const a = createRouterIntegrationStorage();
+ const b = createRouterIntegrationStorage();
+ expect(a).not.toBe(b);
+ expect(a.screenTimes).not.toBe(b.screenTimes);
+ expect(a.interactiveScreensIds).not.toBe(b.interactiveScreensIds);
+ expect(a.renderedScreensIds).not.toBe(b.renderedScreensIds);
+ expect(a.pendingActions).not.toBe(b.pendingActions);
+ });
+
+ it('initializes empty collections and default flags', () => {
+ const storage = createRouterIntegrationStorage();
+ expect(storage.pendingActions).toEqual([]);
+ expect(storage.renderedScreensIds.size).toBe(0);
+ expect(storage.hasRecordedInitialTtr).toBe(false);
+ expect(storage.screenTimes).toEqual({});
+ expect(storage.interactiveScreensIds.size).toBe(0);
+ });
+});
diff --git a/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.android.tsx b/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.android.tsx
index 56352558327ecc..2a5d7881cf34e0 100644
--- a/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.android.tsx
+++ b/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.android.tsx
@@ -1,8 +1,11 @@
/* eslint-disable @typescript-eslint/no-require-imports */
import { renderHook, act } from '@testing-library/react-native';
import AppMetrics from 'expo-app-metrics';
+import { type ReactNode } from 'react';
+import { ObserveRouterIntegrationContext } from '../ObserveRouterIntegrationProvider';
import * as routerModule from '../router';
+import { createRouterIntegrationStorage, type RouterIntegrationStorage } from '../storage';
import { useObserveForRouter } from '../useObserveForRouter';
jest.mock('expo-app-metrics', () => ({
@@ -17,6 +20,7 @@ jest.mock('expo-app-metrics', () => ({
jest.mock('../init', () => ({
__esModule: true,
isInitialized: jest.fn(() => true),
+ initListeners: jest.fn(() => () => {}),
initRouterIntegration: jest.fn(),
}));
@@ -33,6 +37,7 @@ jest.mock('../router', () => {
useRoute,
useNavigation,
useCurrentRouteInfo,
+ unstable_navigationEvents: { addListener: jest.fn(), emit: jest.fn() },
},
isRouterInstalled: true,
__useRoute: useRoute,
@@ -40,19 +45,53 @@ jest.mock('../router', () => {
};
});
+const mockAddCustomMetric = AppMetrics.addCustomMetricToSession as jest.Mock;
const mockUseRoute = (routerModule as unknown as { __useRoute: jest.Mock }).__useRoute;
const mockUseNavigation = (routerModule as unknown as { __useNavigation: jest.Mock })
.__useNavigation;
+function wrapper(storage: RouterIntegrationStorage | null) {
+ return ({ children }: { children: ReactNode }) => (
+
+ {children}
+
+ );
+}
+
+let storage: RouterIntegrationStorage;
+
beforeEach(() => {
jest.clearAllMocks();
mockUseRoute.mockReturnValue({ key: 'screen-a' });
mockUseNavigation.mockReturnValue({ isFocused: () => true });
+ storage = createRouterIntegrationStorage();
});
describe('useObserveForRouter (android)', () => {
+ it('records TTI from dispatchTime on the first call when focused', async () => {
+ storage.screenTimes['screen-a'] = { dispatchTime: 1000 };
+ jest.spyOn(performance, 'now').mockReturnValue(1300);
+
+ const { result } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
+ await act(async () => {
+ await result.current!();
+ });
+
+ expect(mockAddCustomMetric).toHaveBeenCalledTimes(1);
+ expect(mockAddCustomMetric).toHaveBeenCalledWith({
+ sessionId: 'session-1',
+ timestamp: expect.any(String),
+ category: 'navigation',
+ routeName: '/test',
+ name: 'tti',
+ value: 0.3,
+ params: { routeParams: { x: '1' } },
+ });
+ });
+
it('calls AppMetrics.markInteractive when the screen is focused', async () => {
- const { result } = renderHook(() => useObserveForRouter());
+ storage.screenTimes['screen-a'] = { dispatchTime: 1000 };
+ const { result } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
const arg = { params: { x: 'payload' } };
await act(async () => {
await result.current!({ ...arg });
@@ -60,15 +99,35 @@ describe('useObserveForRouter (android)', () => {
expect(AppMetrics.markInteractive).toHaveBeenCalledWith({ ...arg, routeName: '/test' });
});
- it('does not call AppMetrics.markInteractive when the screen is not focused', async () => {
+ it('does not call AppMetrics.markInteractive when the screen is not focused, but still computes TTI', async () => {
mockUseNavigation.mockReturnValue({ isFocused: () => false });
- jest.spyOn(Date, 'now').mockReturnValue(1300);
+ storage.screenTimes['screen-a'] = { dispatchTime: 1000 };
+ jest.spyOn(performance, 'now').mockReturnValue(1300);
- const { result } = renderHook(() => useObserveForRouter());
+ const { result } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
await act(async () => {
await result.current!();
});
expect(AppMetrics.markInteractive).not.toHaveBeenCalled();
+ expect(mockAddCustomMetric).toHaveBeenCalledWith({
+ sessionId: 'session-1',
+ timestamp: expect.any(String),
+ category: 'navigation',
+ routeName: '/test',
+ name: 'tti',
+ value: 0.3,
+ params: { routeParams: { x: '1' } },
+ });
+ });
+
+ it('skips TTI calculation silently when no dispatchTime is recorded for the screen', async () => {
+ const warnSpy = jest.spyOn(console, 'warn');
+ const { result } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
+ await act(async () => {
+ await result.current!();
+ });
+ expect(mockAddCustomMetric).not.toHaveBeenCalled();
+ expect(warnSpy).not.toHaveBeenCalled();
});
});
diff --git a/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.ios.tsx b/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.ios.tsx
index 961735dce6dd0a..ca346d160b5c7d 100644
--- a/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.ios.tsx
+++ b/packages/expo-observe/src/integrations/expo-router/__tests__/useObserveForRouter.test.ios.tsx
@@ -1,8 +1,11 @@
/* eslint-disable @typescript-eslint/no-require-imports */
import { renderHook, act } from '@testing-library/react-native';
import AppMetrics from 'expo-app-metrics';
+import { type ReactNode } from 'react';
+import { ObserveRouterIntegrationContext } from '../ObserveRouterIntegrationProvider';
import * as routerModule from '../router';
+import { createRouterIntegrationStorage, type RouterIntegrationStorage } from '../storage';
import { useObserveForRouter } from '../useObserveForRouter';
jest.mock('expo-app-metrics', () => ({
@@ -17,6 +20,7 @@ jest.mock('expo-app-metrics', () => ({
jest.mock('../init', () => ({
__esModule: true,
isInitialized: jest.fn(() => true),
+ initListeners: jest.fn(() => () => {}),
initRouterIntegration: jest.fn(),
}));
@@ -33,6 +37,7 @@ jest.mock('../router', () => {
useRoute,
useNavigation,
useCurrentRouteInfo,
+ unstable_navigationEvents: { addListener: jest.fn(), emit: jest.fn() },
},
isRouterInstalled: true,
__useRoute: useRoute,
@@ -40,14 +45,28 @@ jest.mock('../router', () => {
};
});
+const mockAddCustomMetric = AppMetrics.addCustomMetricToSession as jest.Mock;
const mockUseRoute = (routerModule as unknown as { __useRoute: jest.Mock }).__useRoute;
const mockUseNavigation = (routerModule as unknown as { __useNavigation: jest.Mock })
.__useNavigation;
+function wrapper(storage: RouterIntegrationStorage | null) {
+ return ({ children }: { children: ReactNode }) => (
+
+ {children}
+
+ );
+}
+
+let storage: RouterIntegrationStorage;
+
beforeEach(() => {
jest.clearAllMocks();
+ jest.spyOn(console, 'log').mockImplementation(() => {});
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
mockUseRoute.mockReturnValue({ key: 'screen-a' });
mockUseNavigation.mockReturnValue({ isFocused: () => true });
+ storage = createRouterIntegrationStorage();
});
describe('useObserveForRouter (ios)', () => {
@@ -71,4 +90,71 @@ describe('useObserveForRouter (ios)', () => {
expect(AppMetrics.markInteractive).not.toHaveBeenCalled();
});
+
+ it('does not record the per-screen TTI metric on iOS', async () => {
+ storage.screenTimes['screen-a'] = { dispatchTime: 1000 };
+ const { result } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
+ await act(async () => {
+ await result.current!();
+ });
+ expect(mockAddCustomMetric).not.toHaveBeenCalled();
+ });
+
+ it('warns and skips when called on an unmounted screen', async () => {
+ const warnSpy = jest.spyOn(console, 'warn');
+ const { result, unmount } = renderHook(() => useObserveForRouter(), {
+ wrapper: wrapper(storage),
+ });
+ const fn = result.current!;
+ unmount();
+ await act(async () => {
+ await fn();
+ });
+ expect(warnSpy).toHaveBeenCalledWith(
+ '[expo-observe] Calling markInteractive on unmounted screen'
+ );
+ expect(AppMetrics.markInteractive).not.toHaveBeenCalled();
+ });
+
+ it('warns when there is no screenId on the route', async () => {
+ mockUseRoute.mockReturnValue({ key: undefined });
+ const warnSpy = jest.spyOn(console, 'warn');
+ const { result } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
+ await act(async () => {
+ await result.current!();
+ });
+ expect(warnSpy).toHaveBeenCalledWith(
+ '[expo-observe] No metadata available for the current screen. Make sure to call useObserve inside a screen component.'
+ );
+ expect(AppMetrics.markInteractive).not.toHaveBeenCalled();
+ });
+
+ it('warns when the screen ID changes between renders', () => {
+ const warnSpy = jest.spyOn(console, 'warn');
+ mockUseRoute.mockReturnValue({ key: 'screen-a' });
+ const { rerender } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
+
+ mockUseRoute.mockReturnValue({ key: 'screen-b' });
+ rerender(undefined);
+
+ expect(warnSpy).toHaveBeenCalledWith(
+ '[expo-observe] Screen ID changed between renders. This is most likely an expo-router bug.'
+ );
+ });
+
+ it('throws when isInitialized() flips mid-lifetime', () => {
+ const init = require('../init') as typeof import('../init');
+ const isInitMock = init.isInitialized as jest.Mock;
+ isInitMock.mockReturnValue(false);
+ // Suppress React's own console.error for the unhandled render error so the
+ // test output stays focused on the assertion.
+ jest.spyOn(console, 'error').mockImplementation(() => {});
+
+ const { rerender } = renderHook(() => useObserveForRouter(), { wrapper: wrapper(storage) });
+
+ isInitMock.mockReturnValue(true);
+ expect(() => rerender(undefined)).toThrow(
+ "[expo-observe] Router integration was toggled during a screen's lifecycle. Call `ExpoObserve.configure({ disableRouterIntegration })` once at startup before any screen mounts."
+ );
+ });
});
diff --git a/packages/expo-observe/src/integrations/expo-router/index.ts b/packages/expo-observe/src/integrations/expo-router/index.ts
index 92a8602e977ecf..928cc54cac23dd 100644
--- a/packages/expo-observe/src/integrations/expo-router/index.ts
+++ b/packages/expo-observe/src/integrations/expo-router/index.ts
@@ -1,3 +1,4 @@
export { isRouterInstalled } from './router';
export { useObserveForRouter } from './useObserveForRouter';
export { initRouterIntegration } from './init';
+export { ObserveRouterIntegrationProvider } from './ObserveRouterIntegrationProvider';
diff --git a/packages/expo-observe/src/integrations/expo-router/init.ts b/packages/expo-observe/src/integrations/expo-router/init.ts
index 37d510c7d8ed59..5ba6aa95e375d0 100644
--- a/packages/expo-observe/src/integrations/expo-router/init.ts
+++ b/packages/expo-observe/src/integrations/expo-router/init.ts
@@ -1,7 +1,96 @@
+import AppMetrics from 'expo-app-metrics';
+
+import { optionalRouter } from './router';
+import { type RouterIntegrationStorage } from './storage';
+
let initialized = false;
export const isInitialized = () => initialized;
export function initRouterIntegration() {
initialized = true;
+ optionalRouter?.unstable_navigationEvents.enable();
+}
+
+type NavigationEvents = NonNullable['unstable_navigationEvents'];
+
+export function initListeners(
+ storage: RouterIntegrationStorage,
+ navigationEvents: NavigationEvents
+): () => void {
+ const appLaunchTime = performance.now();
+ const cleanup = new Set<() => void>();
+
+ const unsubscribeAction = navigationEvents.addListener('actionDispatched', (event) => {
+ // TODO(@ubax): Handle screen preloading
+ // PRELOAD comes from router.prefetch() â a route warm-up, not a user
+ // navigation â so it must not seed dispatchTime.
+ if (event.actionType === 'PRELOAD') return;
+ storage.pendingActions.push({
+ actionType: event.actionType,
+ dispatchTime: performance.now(),
+ });
+ });
+ cleanup.add(unsubscribeAction);
+
+ const unsubscribeFocus = navigationEvents.addListener('pageFocused', async (e) => {
+ // Snapshot both clocks once so every metric written below is stamped with
+ // the moment the focus event fired, not the moment `addCustomMetricToSession`
+ // happens to run after the awaited `getMainSession()` round-trip.
+ const now = performance.now();
+ const timestamp = new Date().toISOString();
+ const isInitial = !storage.renderedScreensIds.has(e.screenId);
+ storage.renderedScreensIds.add(e.screenId);
+ if (process.env.EXPO_OS !== 'android') {
+ return;
+ }
+ const mainSessionId = (await AppMetrics.getMainSession())?.id;
+ if (!mainSessionId) {
+ return;
+ }
+
+ if (!storage.hasRecordedInitialTtr) {
+ // Stored in seconds to match the OTel `unit = "s"` convention
+ const appLaunchTtrSeconds = (now - appLaunchTime) / 1000;
+ storage.hasRecordedInitialTtr = true;
+ AppMetrics.addCustomMetricToSession({
+ sessionId: mainSessionId,
+ timestamp,
+ category: 'navigation',
+ name: 'ttr',
+ routeName: e.pathname,
+ value: appLaunchTtrSeconds,
+ params: { isInitial, isAppLaunch: true, routeParams: e.params },
+ });
+ return;
+ }
+
+ if (storage.pendingActions.length === 0) return;
+
+ const last = storage.pendingActions[storage.pendingActions.length - 1];
+ if (last) {
+ const dispatchTime = last.dispatchTime;
+ storage.screenTimes[e.screenId] = {
+ ...storage.screenTimes[e.screenId],
+ dispatchTime,
+ };
+
+ AppMetrics.addCustomMetricToSession({
+ sessionId: mainSessionId,
+ timestamp,
+ category: 'navigation',
+ name: 'ttr',
+ routeName: e.pathname,
+ value: (now - dispatchTime) / 1000,
+ params: { isInitial, isAppLaunch: false, routeParams: e.params },
+ });
+ }
+ storage.pendingActions.length = 0;
+ });
+ cleanup.add(unsubscribeFocus);
+
+ return () => {
+ cleanup.forEach((c) => c());
+ cleanup.clear();
+ };
}
diff --git a/packages/expo-observe/src/integrations/expo-router/storage.ts b/packages/expo-observe/src/integrations/expo-router/storage.ts
new file mode 100644
index 00000000000000..0a4656efd09e95
--- /dev/null
+++ b/packages/expo-observe/src/integrations/expo-router/storage.ts
@@ -0,0 +1,38 @@
+import type { ActionDispatchedEvent } from 'expo-router';
+
+export interface ScreenTimes {
+ dispatchTime: number;
+ lastInteractiveCall?: number;
+}
+
+export interface PendingAction {
+ actionType: ActionDispatchedEvent['actionType'];
+ dispatchTime: number;
+}
+
+export interface RouterIntegrationStorage {
+ /**
+ * Actions dispatched, but not yet processed by the integration
+ */
+ pendingActions: PendingAction[];
+ renderedScreensIds: Set;
+ /**
+ * Wether the app had already recorded the first render of the screen
+ */
+ hasRecordedInitialTtr: boolean;
+ /**
+ * Times used to calculate spans from dispatch to certain event
+ */
+ screenTimes: Record;
+ interactiveScreensIds: Set;
+}
+
+export function createRouterIntegrationStorage(): RouterIntegrationStorage {
+ return {
+ pendingActions: [],
+ renderedScreensIds: new Set(),
+ hasRecordedInitialTtr: false,
+ screenTimes: {},
+ interactiveScreensIds: new Set(),
+ };
+}
diff --git a/packages/expo-observe/src/integrations/expo-router/useObserveForRouter.ts b/packages/expo-observe/src/integrations/expo-router/useObserveForRouter.ts
index 7f18f9c3ce9ee4..929238faca600d 100644
--- a/packages/expo-observe/src/integrations/expo-router/useObserveForRouter.ts
+++ b/packages/expo-observe/src/integrations/expo-router/useObserveForRouter.ts
@@ -1,16 +1,19 @@
import AppMetrics, { type MetricAttributes } from 'expo-app-metrics';
-import { useCallback, useEffect, useRef } from 'react';
+import { use, useCallback, useEffect, useRef } from 'react';
+import { ObserveRouterIntegrationContext } from './ObserveRouterIntegrationProvider';
import { isInitialized } from './init';
import { optionalRouter } from './router';
type MarkInteractive = (typeof AppMetrics)['markInteractive'];
export function useObserveForRouter(): MarkInteractive | null {
+ const storage = use(ObserveRouterIntegrationContext);
const isMounted = useRef(true);
const route = optionalRouter?.useRoute();
const navigation = optionalRouter?.useNavigation();
- const pathname = optionalRouter?.useCurrentRouteInfo()?.pathname;
+ const routeInfo = optionalRouter?.useCurrentRouteInfo();
+ const { pathname, params: routeParams } = routeInfo ?? {};
const initializedAtMount = useRef(isInitialized());
if (initializedAtMount.current !== isInitialized()) {
@@ -41,6 +44,8 @@ export function useObserveForRouter(): MarkInteractive | null {
const markInteractive = useCallback(
async (attributes?: MetricAttributes) => {
+ const now = performance.now();
+ const timestamp = new Date().toISOString();
if (!isMounted.current) {
console.warn('[expo-observe] Calling markInteractive on unmounted screen');
return;
@@ -57,8 +62,58 @@ export function useObserveForRouter(): MarkInteractive | null {
routeName: pathname,
});
}
+
+ if (process.env.EXPO_OS !== 'android') {
+ return;
+ }
+ if (!storage) {
+ throw new Error(
+ '[expo-observe] markInteractive was called without an active ObserveProvider. Wrap your app in ObserveRoot from expo-observe.'
+ );
+ }
+
+ // Snapshot times BEFORE writing the new interactive timestamp so the
+ // duplicate-detection logic below sees the *previous* call, not this one.
+ const currentScreenData = storage.screenTimes[screenId];
+
+ storage.interactiveScreensIds.add(screenId);
+ if (storage.screenTimes[screenId]) {
+ storage.screenTimes[screenId] = {
+ ...storage.screenTimes[screenId],
+ lastInteractiveCall: now,
+ };
+ }
+
+ if (!currentScreenData?.dispatchTime) return;
+
+ const previousInteractiveCall = currentScreenData.lastInteractiveCall;
+ const previousWasAfterDispatch =
+ previousInteractiveCall != null && currentScreenData.dispatchTime < previousInteractiveCall;
+
+ if (previousWasAfterDispatch) {
+ // We only want to record interactive once per navigation
+ return;
+ }
+
+ // Stored in seconds to match the OTel `unit = "s"` convention
+ const interactiveTimeSeconds = (now - currentScreenData.dispatchTime) / 1000;
+ const mainSessionId = (await AppMetrics.getMainSession())?.id;
+ // TODO(@ubax): we should count the time against the action which caused the first navigation
+ // and add a param stating if during that time there was any navigation
+ if (mainSessionId) {
+ await AppMetrics.addCustomMetricToSession({
+ sessionId: mainSessionId,
+ timestamp,
+ category: 'navigation',
+ // TODO(@ubax): Use segments.join here to get full routeName and pass pathname and params via params
+ routeName: pathname,
+ name: 'tti',
+ value: interactiveTimeSeconds,
+ params: { routeParams },
+ });
+ }
},
- [screenId, navigation, pathname]
+ [screenId, navigation, pathname, storage, routeParams]
);
return initializedAtMount.current ? markInteractive : null;
diff --git a/packages/expo-router/build/exports.d.ts b/packages/expo-router/build/exports.d.ts
index 7b8f9ae993be89..4b45ef4e8c3f8a 100644
--- a/packages/expo-router/build/exports.d.ts
+++ b/packages/expo-router/build/exports.d.ts
@@ -31,6 +31,7 @@ export type { SingularOptions } from './useScreens';
export type * from './types';
export { Badge, type BadgeProps, Icon, type IconProps, Label, type LabelProps, VectorIcon, type VectorIconProps, } from './primitives';
export { unstable_navigationEvents } from './navigationEvents';
+export type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved, ActionDispatchedEvent, AnalyticsEvent, } from './navigationEvents';
/**
* @deprecated Use `import { Tabs } from 'expo-router/js-tabs'` instead.
*/
diff --git a/packages/expo-router/build/exports.d.ts.map b/packages/expo-router/build/exports.d.ts.map
index 969fcafd57299f..882d8e73089488 100644
--- a/packages/expo-router/build/exports.d.ts.map
+++ b/packages/expo-router/build/exports.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,WAAW,EACX,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,MAAM,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAG3B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;GAEG;AACH,OAAO,KAAK,YAAY,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,6CAA6C,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAE5D,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,mBAAmB,SAAS,CAAC;AAE7B,OAAO,EACL,KAAK,EACL,KAAK,UAAU,EACf,IAAI,EACJ,KAAK,SAAS,EACd,KAAK,EACL,KAAK,UAAU,EACf,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;GAEG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EACV,kCAAkC,EAClC,mCAAmC,EACnC,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC"}
\ No newline at end of file
+{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,WAAW,EACX,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,MAAM,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAG3B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;GAEG;AACH,OAAO,KAAK,YAAY,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,6CAA6C,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAE5D,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,mBAAmB,SAAS,CAAC;AAE7B,OAAO,EACL,KAAK,EACL,KAAK,UAAU,EACf,IAAI,EACJ,KAAK,SAAS,EACd,KAAK,EACL,KAAK,UAAU,EACf,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EACV,kCAAkC,EAClC,mCAAmC,EACnC,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-router/build/exports.js.map b/packages/expo-router/build/exports.js.map
index 0ccc24115efe4f..f2b6aaa92e6a9a 100644
--- a/packages/expo-router/build/exports.js.map
+++ b/packages/expo-router/build/exports.js.map
@@ -1 +1 @@
-{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kBAAkB;AAClB,iDAAoD;AAmB3C,0FAnBA,qBAAS,OAmBA;AAAE,qFAnBA,gBAAI,OAmBA;AAjBxB,iCAYiB;AAXf,kGAAA,SAAS,OAAA;AACT,8GAAA,qBAAqB,OAAA;AACrB,oGAAA,WAAW,OAAA;AACX,kHAAA,yBAAyB,OAAA;AACzB,8GAAA,qBAAqB,OAAA;AACrB,6GAAA,oBAAoB,OAAA;AACpB,oGAAA,WAAW,OAAA;AACX,0GAAA,iBAAiB,OAAA;AACjB,+GAAA,sBAAsB,OAAA;AACtB,sGAAA,aAAa,OAAA;AACb,4GAAA,mBAAmB,OAAA;AAGrB,mDAAiE;AAAxD,wGAAA,MAAM,OAAA;AAEf,iEAAgE;AAAvD,sHAAA,iBAAiB,OAAA;AAG1B,oBAAoB;AACpB,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,+CAA8C;AAArC,sGAAA,SAAS,OAAA;AAClB,2CAA0C;AAAjC,kGAAA,OAAO,OAAA;AAChB,iDAAkE;AAAzD,wGAAA,UAAU,OAAA;AAEnB,uDAAsD;AAA7C,8GAAA,aAAa,OAAA;AACtB,6DAAwF;AAA/E,oHAAA,gBAAgB,OAAA;AAGzB,WAAW;AACX;;GAEG;AACH,+DAA+C;AAE/C,mBAAmB;AACnB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,mDAAuE;AAA9D,gHAAA,cAAc,OAAA;AACvB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAGrB,yEAAwE;AAA/D,sGAAA,SAAS,OAAA;AAClB,+EAA8E;AAArE,4GAAA,YAAY,OAAA;AACrB,+EAA8E;AAArE,8GAAA,aAAa,OAAA;AACtB,qEAAoE;AAA3D,oGAAA,QAAQ,OAAA;AACjB,uEAAsE;AAA7D,4GAAA,YAAY,OAAA;AACrB,2EAA0E;AAAjE,gHAAA,cAAc,OAAA;AACvB,6DAA4D;AAAnD,oGAAA,QAAQ,OAAA;AAOjB,2CASsB;AARpB,mGAAA,KAAK,OAAA;AAEL,kGAAA,IAAI,OAAA;AAEJ,mGAAA,KAAK,OAAA;AAEL,wGAAA,UAAU,OAAA;AAIZ,uDAA+D;AAAtD,6HAAA,yBAAyB,OAAA;AAElC;;GAEG;AACH,uCAAsC;AAA7B,4FAAA,IAAI,OAAA;AAEb,mEAAiE;AAAxD,uHAAA,iBAAiB,OAAA","sourcesContent":["// Expo Router API\nimport { Navigator, Slot } from './views/Navigator';\n\nexport {\n useRouter,\n useUnstableGlobalHref,\n usePathname,\n useNavigationContainerRef,\n useGlobalSearchParams,\n useLocalSearchParams,\n useSegments,\n useRootNavigation,\n useRootNavigationState,\n useLoaderData,\n useCurrentRouteInfo,\n} from './hooks';\n\nexport { router, type ImperativeRouter } from './imperative-api';\n\nexport { withLayoutContext } from './layouts/withLayoutContext';\nexport { Navigator, Slot };\n\n// Expo Router Views\nexport { ExpoRoot } from './ExpoRoot';\nexport { Unmatched } from './views/Unmatched';\nexport { Sitemap } from './views/Sitemap';\nexport { useSitemap, type SitemapType } from './views/useSitemap';\nexport type { ErrorBoundaryProps } from './views/Try';\nexport { ErrorBoundary } from './views/ErrorBoundary';\nexport { SuspenseFallback, type SuspenseFallbackProps } from './views/SuspenseFallback';\nexport type { ScreenProps } from './useScreens';\n\n// Platform\n/**\n * @hidden\n */\nexport * as SplashScreen from './views/Splash';\n\n// React Navigation\nexport { useNavigation } from './useNavigation';\nexport { useFocusEffect, type EffectCallback } from './useFocusEffect';\nexport { useIsFocused } from './useIsFocused';\nexport type { ResultState } from './fork/getStateFromPath';\n\nexport { DarkTheme } from './react-navigation/native/theming/DarkTheme';\nexport { DefaultTheme } from './react-navigation/native/theming/DefaultTheme';\nexport { ThemeProvider } from './react-navigation/core/theming/ThemeProvider';\nexport { useTheme } from './react-navigation/core/theming/useTheme';\nexport { useRoutePath } from './react-navigation/native/useRoutePath';\nexport { useScrollToTop } from './react-navigation/native/useScrollToTop';\nexport { useRoute } from './react-navigation/core/useRoute';\n\nexport type { RedirectConfig } from './getRoutesCore';\nexport type { SingularOptions } from './useScreens';\n\nexport type * from './types';\n\nexport {\n Badge,\n type BadgeProps,\n Icon,\n type IconProps,\n Label,\n type LabelProps,\n VectorIcon,\n type VectorIconProps,\n} from './primitives';\n\nexport { unstable_navigationEvents } from './navigationEvents';\n\n/**\n * @deprecated Use `import { Tabs } from 'expo-router/js-tabs'` instead.\n */\nexport { Tabs } from './layouts/Tabs';\n\nexport { ExperimentalStack } from './layouts/experimental-stack';\nexport type {\n ExperimentalStackNavigationOptions,\n ExperimentalStackNavigationEventMap,\n ExperimentalStackNavigationProp,\n ExperimentalStackScreenProps,\n} from './layouts/experimental-stack';\n"]}
\ No newline at end of file
+{"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kBAAkB;AAClB,iDAAoD;AAmB3C,0FAnBA,qBAAS,OAmBA;AAAE,qFAnBA,gBAAI,OAmBA;AAjBxB,iCAYiB;AAXf,kGAAA,SAAS,OAAA;AACT,8GAAA,qBAAqB,OAAA;AACrB,oGAAA,WAAW,OAAA;AACX,kHAAA,yBAAyB,OAAA;AACzB,8GAAA,qBAAqB,OAAA;AACrB,6GAAA,oBAAoB,OAAA;AACpB,oGAAA,WAAW,OAAA;AACX,0GAAA,iBAAiB,OAAA;AACjB,+GAAA,sBAAsB,OAAA;AACtB,sGAAA,aAAa,OAAA;AACb,4GAAA,mBAAmB,OAAA;AAGrB,mDAAiE;AAAxD,wGAAA,MAAM,OAAA;AAEf,iEAAgE;AAAvD,sHAAA,iBAAiB,OAAA;AAG1B,oBAAoB;AACpB,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,+CAA8C;AAArC,sGAAA,SAAS,OAAA;AAClB,2CAA0C;AAAjC,kGAAA,OAAO,OAAA;AAChB,iDAAkE;AAAzD,wGAAA,UAAU,OAAA;AAEnB,uDAAsD;AAA7C,8GAAA,aAAa,OAAA;AACtB,6DAAwF;AAA/E,oHAAA,gBAAgB,OAAA;AAGzB,WAAW;AACX;;GAEG;AACH,+DAA+C;AAE/C,mBAAmB;AACnB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,mDAAuE;AAA9D,gHAAA,cAAc,OAAA;AACvB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAGrB,yEAAwE;AAA/D,sGAAA,SAAS,OAAA;AAClB,+EAA8E;AAArE,4GAAA,YAAY,OAAA;AACrB,+EAA8E;AAArE,8GAAA,aAAa,OAAA;AACtB,qEAAoE;AAA3D,oGAAA,QAAQ,OAAA;AACjB,uEAAsE;AAA7D,4GAAA,YAAY,OAAA;AACrB,2EAA0E;AAAjE,gHAAA,cAAc,OAAA;AACvB,6DAA4D;AAAnD,oGAAA,QAAQ,OAAA;AAOjB,2CASsB;AARpB,mGAAA,KAAK,OAAA;AAEL,kGAAA,IAAI,OAAA;AAEJ,mGAAA,KAAK,OAAA;AAEL,wGAAA,UAAU,OAAA;AAIZ,uDAA+D;AAAtD,6HAAA,yBAAyB,OAAA;AAUlC;;GAEG;AACH,uCAAsC;AAA7B,4FAAA,IAAI,OAAA;AAEb,mEAAiE;AAAxD,uHAAA,iBAAiB,OAAA","sourcesContent":["// Expo Router API\nimport { Navigator, Slot } from './views/Navigator';\n\nexport {\n useRouter,\n useUnstableGlobalHref,\n usePathname,\n useNavigationContainerRef,\n useGlobalSearchParams,\n useLocalSearchParams,\n useSegments,\n useRootNavigation,\n useRootNavigationState,\n useLoaderData,\n useCurrentRouteInfo,\n} from './hooks';\n\nexport { router, type ImperativeRouter } from './imperative-api';\n\nexport { withLayoutContext } from './layouts/withLayoutContext';\nexport { Navigator, Slot };\n\n// Expo Router Views\nexport { ExpoRoot } from './ExpoRoot';\nexport { Unmatched } from './views/Unmatched';\nexport { Sitemap } from './views/Sitemap';\nexport { useSitemap, type SitemapType } from './views/useSitemap';\nexport type { ErrorBoundaryProps } from './views/Try';\nexport { ErrorBoundary } from './views/ErrorBoundary';\nexport { SuspenseFallback, type SuspenseFallbackProps } from './views/SuspenseFallback';\nexport type { ScreenProps } from './useScreens';\n\n// Platform\n/**\n * @hidden\n */\nexport * as SplashScreen from './views/Splash';\n\n// React Navigation\nexport { useNavigation } from './useNavigation';\nexport { useFocusEffect, type EffectCallback } from './useFocusEffect';\nexport { useIsFocused } from './useIsFocused';\nexport type { ResultState } from './fork/getStateFromPath';\n\nexport { DarkTheme } from './react-navigation/native/theming/DarkTheme';\nexport { DefaultTheme } from './react-navigation/native/theming/DefaultTheme';\nexport { ThemeProvider } from './react-navigation/core/theming/ThemeProvider';\nexport { useTheme } from './react-navigation/core/theming/useTheme';\nexport { useRoutePath } from './react-navigation/native/useRoutePath';\nexport { useScrollToTop } from './react-navigation/native/useScrollToTop';\nexport { useRoute } from './react-navigation/core/useRoute';\n\nexport type { RedirectConfig } from './getRoutesCore';\nexport type { SingularOptions } from './useScreens';\n\nexport type * from './types';\n\nexport {\n Badge,\n type BadgeProps,\n Icon,\n type IconProps,\n Label,\n type LabelProps,\n VectorIcon,\n type VectorIconProps,\n} from './primitives';\n\nexport { unstable_navigationEvents } from './navigationEvents';\nexport type {\n PageWillRender,\n PageFocusedEvent,\n PageBlurredEvent,\n PageRemoved,\n ActionDispatchedEvent,\n AnalyticsEvent,\n} from './navigationEvents';\n\n/**\n * @deprecated Use `import { Tabs } from 'expo-router/js-tabs'` instead.\n */\nexport { Tabs } from './layouts/Tabs';\n\nexport { ExperimentalStack } from './layouts/experimental-stack';\nexport type {\n ExperimentalStackNavigationOptions,\n ExperimentalStackNavigationEventMap,\n ExperimentalStackNavigationProp,\n ExperimentalStackScreenProps,\n} from './layouts/experimental-stack';\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/global-state/store.d.ts.map b/packages/expo-router/build/global-state/store.d.ts.map
index 9cd7fddc1ff4e6..fd983727b3e28f 100644
--- a/packages/expo-router/build/global-state/store.d.ts.map
+++ b/packages/expo-router/build/global-state/store.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/global-state/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGrD,MAAM,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC;AAEvC,KAAK,QAAQ,GAAG;IACd,aAAa,EAAE,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IAChF,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,QAAQ;aACJ,QAAQ;CACxB,CAAC;AAKF,wBAAgB,6BAA6B,uBAE5C;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,QAEtE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,QAEzD;AAED,eAAO,MAAM,KAAK;;;;;oBAaA,SAAS;;;0BASH,IAAI,YAAY,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;2BAS5B,iBAAiB;;4BAehB,oBAAoB,GAAG,SAAS;;CAwCzD,CAAC"}
\ No newline at end of file
+{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/global-state/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGrD,MAAM,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC;AAEvC,KAAK,QAAQ,GAAG;IACd,aAAa,EAAE,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IAChF,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,QAAQ;aACJ,QAAQ;CACxB,CAAC;AAKF,wBAAgB,6BAA6B,uBAE5C;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,QAEtE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,QAEzD;AAED,eAAO,MAAM,KAAK;;;;;oBAaA,SAAS;;;0BASH,IAAI,YAAY,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;2BAS5B,iBAAiB;;4BAgBhB,oBAAoB,GAAG,SAAS;;CAwCzD,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-router/build/global-state/store.js b/packages/expo-router/build/global-state/store.js
index d4f77233209304..4a8c1286d52a20 100644
--- a/packages/expo-router/build/global-state/store.js
+++ b/packages/expo-router/build/global-state/store.js
@@ -40,6 +40,7 @@ exports.setHasAttemptedToHideSplash = setHasAttemptedToHideSplash;
const getRouteInfoFromState_1 = require("./getRouteInfoFromState");
const routeInfoCache_1 = require("./routeInfoCache");
const href_1 = require("../link/href");
+const navigation_1 = require("../navigationEvents/navigation");
const SplashScreen = __importStar(require("../views/Splash"));
exports.storeRef = {
current: {},
@@ -90,6 +91,7 @@ exports.store = {
exports.storeRef.current.routeInfo = routeInfo;
},
onReady() {
+ (0, navigation_1.handleNavigationOnReady)();
if (!hasAttemptedToHideSplash) {
setHasAttemptedToHideSplash(true);
// NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.
diff --git a/packages/expo-router/build/global-state/store.js.map b/packages/expo-router/build/global-state/store.js.map
index d0eb21dd56db72..0f978f3ade18c3 100644
--- a/packages/expo-router/build/global-state/store.js.map
+++ b/packages/expo-router/build/global-state/store.js.map
@@ -1 +1 @@
-{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/global-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react';\n\nimport { defaultRouteInfo, type UrlObject } from './getRouteInfoFromState';\nimport { getCachedRouteInfo, routeInfoSubscribers } from './routeInfoCache';\nimport type {\n FocusedRouteState,\n LinkToOptions,\n ReactNavigationState,\n StoreRedirects,\n} from './types';\nimport type { RouteNode } from '../Route';\nimport type { ExpoLinkingOptions } from '../getLinkingConfig';\nimport { resolveHref, resolveHrefStringWithSegments } from '../link/href';\nimport type { NavigationContainerRefWithCurrent } from '../react-navigation/native';\nimport type { RequireContext, Href } from '../types';\nimport * as SplashScreen from '../views/Splash';\n\nexport type RouterStore = typeof store;\n\ntype StoreRef = {\n navigationRef: NavigationContainerRefWithCurrent;\n routeNode: RouteNode | null;\n rootComponent: ComponentType;\n state?: ReactNavigationState;\n linking?: ExpoLinkingOptions;\n config: any;\n redirects: StoreRedirects[];\n routeInfo?: UrlObject;\n context?: RequireContext;\n};\n\nexport const storeRef = {\n current: {} as StoreRef,\n};\n\nlet splashScreenAnimationFrame: number | undefined;\nlet hasAttemptedToHideSplash = false;\n\nexport function getSplashScreenAnimationFrame() {\n return splashScreenAnimationFrame;\n}\n\nexport function setSplashScreenAnimationFrame(value: number | undefined) {\n splashScreenAnimationFrame = value;\n}\n\nexport function setHasAttemptedToHideSplash(value: boolean) {\n hasAttemptedToHideSplash = value;\n}\n\nexport const store = {\n shouldShowTutorial() {\n return !storeRef.current.routeNode && process.env.NODE_ENV === 'development';\n },\n get state() {\n return storeRef.current.state;\n },\n get navigationRef() {\n return storeRef.current.navigationRef;\n },\n get routeNode() {\n return storeRef.current.routeNode;\n },\n getRouteInfo(): UrlObject {\n return storeRef.current.routeInfo || defaultRouteInfo;\n },\n get redirects() {\n return storeRef.current.redirects || [];\n },\n get rootComponent() {\n return storeRef.current.rootComponent;\n },\n getStateForHref(href: Href, options?: LinkToOptions) {\n href = resolveHref(href);\n\n href = resolveHrefStringWithSegments(href, store.getRouteInfo(), options);\n return this.linking?.getStateFromPath!(href, this.linking.config);\n },\n get linking() {\n return storeRef.current.linking;\n },\n setFocusedState(state: FocusedRouteState) {\n const routeInfo = getCachedRouteInfo(state);\n storeRef.current.routeInfo = routeInfo;\n },\n onReady() {\n if (!hasAttemptedToHideSplash) {\n setHasAttemptedToHideSplash(true);\n // NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.\n setSplashScreenAnimationFrame(\n requestAnimationFrame(() => {\n SplashScreen._internal_maybeHideAsync?.();\n })\n );\n }\n },\n onStateChange(newState: ReactNavigationState | undefined) {\n if (!newState) {\n return;\n }\n if (process.env.NODE_ENV === 'development') {\n let isStale: boolean | undefined = false;\n let state: ReactNavigationState | undefined = newState;\n\n while (!isStale && state) {\n isStale = state.stale;\n state =\n state.routes?.[\n 'index' in state && typeof state.index === 'number'\n ? state.index\n : state.routes.length - 1\n ]?.state;\n }\n if (isStale) {\n // This should never happen, as onStateChange should provide a full state. However, adding this check to catch any undocumented behavior.\n console.error(\n 'Detected stale state in onStateChange. This is likely a bug in Expo Router.'\n );\n }\n }\n\n storeRef.current.state = newState;\n\n storeRef.current.routeInfo = getCachedRouteInfo(newState);\n\n for (const callback of routeInfoSubscribers) {\n callback();\n }\n },\n assertIsReady() {\n if (!storeRef.current.navigationRef.isReady()) {\n throw new Error(\n 'Attempted to navigate before mounting the Root Layout component. Ensure the Root Layout component is rendering a Slot, or other navigator on the first render.'\n );\n }\n },\n};\n"]}
\ No newline at end of file
+{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/global-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from 'react';\n\nimport { defaultRouteInfo, type UrlObject } from './getRouteInfoFromState';\nimport { getCachedRouteInfo, routeInfoSubscribers } from './routeInfoCache';\nimport type {\n FocusedRouteState,\n LinkToOptions,\n ReactNavigationState,\n StoreRedirects,\n} from './types';\nimport type { RouteNode } from '../Route';\nimport type { ExpoLinkingOptions } from '../getLinkingConfig';\nimport { resolveHref, resolveHrefStringWithSegments } from '../link/href';\nimport { handleNavigationOnReady } from '../navigationEvents/navigation';\nimport type { NavigationContainerRefWithCurrent } from '../react-navigation/native';\nimport type { RequireContext, Href } from '../types';\nimport * as SplashScreen from '../views/Splash';\n\nexport type RouterStore = typeof store;\n\ntype StoreRef = {\n navigationRef: NavigationContainerRefWithCurrent;\n routeNode: RouteNode | null;\n rootComponent: ComponentType;\n state?: ReactNavigationState;\n linking?: ExpoLinkingOptions;\n config: any;\n redirects: StoreRedirects[];\n routeInfo?: UrlObject;\n context?: RequireContext;\n};\n\nexport const storeRef = {\n current: {} as StoreRef,\n};\n\nlet splashScreenAnimationFrame: number | undefined;\nlet hasAttemptedToHideSplash = false;\n\nexport function getSplashScreenAnimationFrame() {\n return splashScreenAnimationFrame;\n}\n\nexport function setSplashScreenAnimationFrame(value: number | undefined) {\n splashScreenAnimationFrame = value;\n}\n\nexport function setHasAttemptedToHideSplash(value: boolean) {\n hasAttemptedToHideSplash = value;\n}\n\nexport const store = {\n shouldShowTutorial() {\n return !storeRef.current.routeNode && process.env.NODE_ENV === 'development';\n },\n get state() {\n return storeRef.current.state;\n },\n get navigationRef() {\n return storeRef.current.navigationRef;\n },\n get routeNode() {\n return storeRef.current.routeNode;\n },\n getRouteInfo(): UrlObject {\n return storeRef.current.routeInfo || defaultRouteInfo;\n },\n get redirects() {\n return storeRef.current.redirects || [];\n },\n get rootComponent() {\n return storeRef.current.rootComponent;\n },\n getStateForHref(href: Href, options?: LinkToOptions) {\n href = resolveHref(href);\n\n href = resolveHrefStringWithSegments(href, store.getRouteInfo(), options);\n return this.linking?.getStateFromPath!(href, this.linking.config);\n },\n get linking() {\n return storeRef.current.linking;\n },\n setFocusedState(state: FocusedRouteState) {\n const routeInfo = getCachedRouteInfo(state);\n storeRef.current.routeInfo = routeInfo;\n },\n onReady() {\n handleNavigationOnReady();\n if (!hasAttemptedToHideSplash) {\n setHasAttemptedToHideSplash(true);\n // NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.\n setSplashScreenAnimationFrame(\n requestAnimationFrame(() => {\n SplashScreen._internal_maybeHideAsync?.();\n })\n );\n }\n },\n onStateChange(newState: ReactNavigationState | undefined) {\n if (!newState) {\n return;\n }\n if (process.env.NODE_ENV === 'development') {\n let isStale: boolean | undefined = false;\n let state: ReactNavigationState | undefined = newState;\n\n while (!isStale && state) {\n isStale = state.stale;\n state =\n state.routes?.[\n 'index' in state && typeof state.index === 'number'\n ? state.index\n : state.routes.length - 1\n ]?.state;\n }\n if (isStale) {\n // This should never happen, as onStateChange should provide a full state. However, adding this check to catch any undocumented behavior.\n console.error(\n 'Detected stale state in onStateChange. This is likely a bug in Expo Router.'\n );\n }\n }\n\n storeRef.current.state = newState;\n\n storeRef.current.routeInfo = getCachedRouteInfo(newState);\n\n for (const callback of routeInfoSubscribers) {\n callback();\n }\n },\n assertIsReady() {\n if (!storeRef.current.navigationRef.isReady()) {\n throw new Error(\n 'Attempted to navigate before mounting the Root Layout component. Ensure the Root Layout component is rendering a Slot, or other navigator on the first render.'\n );\n }\n },\n};\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts b/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts
index aa53493c6ba56a..47edd7562ebf68 100644
--- a/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts
+++ b/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts
@@ -32,6 +32,8 @@ export declare function useSharedScreenProps(props: InternalTabScreenProps): {
onWillAppear?: import("react-native-screens").TabsScreenEventHandler | undefined;
onWillDisappear?: import("react-native-screens").TabsScreenEventHandler | undefined;
orientation?: import("react-native-screens").TabsScreenOrientation | undefined;
+ onDidAppear?: import("react-native-screens").TabsScreenEventHandler | undefined;
+ onDidDisappear?: import("react-native-screens").TabsScreenEventHandler | undefined;
preventNativeSelection?: boolean | undefined | undefined;
badgeValue?: string | undefined | undefined;
specialEffects?: {
@@ -42,8 +44,6 @@ export declare function useSharedScreenProps(props: InternalTabScreenProps): {
} | undefined | undefined;
tabBarItemTestID?: string | undefined | undefined;
tabBarItemAccessibilityLabel?: string | undefined | undefined;
- onDidAppear?: import("react-native-screens").TabsScreenEventHandler | undefined;
- onDidDisappear?: import("react-native-screens").TabsScreenEventHandler | undefined;
};
screenKey: string;
icon: {
diff --git a/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts.map b/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts.map
index 804c881e6d7766..f10352e7c2efde 100644
--- a/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts.map
+++ b/packages/expo-router/build/native-tabs/NativeTabsView.shared.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"NativeTabsView.shared.d.ts","sourceRoot":"","sources":["../../src/native-tabs/NativeTabsView.shared.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAIrE,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,UAAU,EACV,IAAI,GACL,EAAE,IAAI,CAAC,mBAAmB,EAAE,cAAc,GAAG,YAAY,GAAG,MAAM,CAAC,GAAG;IACrE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,CAgBA;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,mBAAmB,CAAC,aAAa,CAAC,GAC9C,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAU7C;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,eAAe,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;CACxC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,sBAAsB;;mBAgBV,UAAU,GAAG,MAAM;;;;;;;;;;;;;;;;gCAZhE,CAAC;yBAGN,CAAA;2BAGE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;EAeR;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,eAAe,GAChB,EAAE;IACD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,eAAe,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;CACxC,2CAcA"}
\ No newline at end of file
+{"version":3,"file":"NativeTabsView.shared.d.ts","sourceRoot":"","sources":["../../src/native-tabs/NativeTabsView.shared.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAIrE,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,UAAU,EACV,IAAI,GACL,EAAE,IAAI,CAAC,mBAAmB,EAAE,cAAc,GAAG,YAAY,GAAG,MAAM,CAAC,GAAG;IACrE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,CAgBA;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,mBAAmB,CAAC,aAAa,CAAC,GAC9C,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAU7C;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,eAAe,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;CACxC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,sBAAsB;;mBAgBV,UAAU,GAAG,MAAM;;;;;;;;;;;;;;;;;;gCAZhE,CAAC;yBAGN,CAAA;2BAGE,CAAC;;;;;;;;;;;;;;;;;;;;;;;EAeR;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,eAAe,GAChB,EAAE;IACD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,eAAe,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;CACxC,2CAcA"}
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/index.d.ts b/packages/expo-router/build/navigationEvents/index.d.ts
index 1c665d5a03f512..ae78f28574647c 100644
--- a/packages/expo-router/build/navigationEvents/index.d.ts
+++ b/packages/expo-router/build/navigationEvents/index.d.ts
@@ -1,6 +1,6 @@
-import type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved } from './types';
-export type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved } from './types';
-export type AnalyticsEvent = PageWillRender | PageFocusedEvent | PageBlurredEvent | PageRemoved;
+import type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved, ActionDispatchedEvent } from './types';
+export type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved, ActionDispatchedEvent, } from './types';
+export type AnalyticsEvent = PageWillRender | PageFocusedEvent | PageBlurredEvent | PageRemoved | ActionDispatchedEvent;
type EventTypeName = AnalyticsEvent['type'];
type Payload = Omit void;
isEnabled: () => boolean;
- saveCurrentPathname: () => void;
- readonly currentPathname: string | undefined;
- readonly currentParams: Record | undefined;
};
//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/index.d.ts.map b/packages/expo-router/build/navigationEvents/index.d.ts.map
index 1632af3dd0fc5b..820aad49ecb1f2 100644
--- a/packages/expo-router/build/navigationEvents/index.d.ts.map
+++ b/packages/expo-router/build/navigationEvents/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigationEvents/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/F,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/F,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,WAAW,CAAC;AAShG,KAAK,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;AAC5C,KAAK,OAAO,CAAC,CAAC,SAAS,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AAM3F,iBAAS,WAAW,CAAC,SAAS,SAAS,aAAa,EAClD,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,cAe9C;AAED,wBAAgB,IAAI,CAAC,SAAS,SAAS,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,QAO/F;AAQD,eAAO,MAAM,yBAAyB;;;;;;;;CAsBrC,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigationEvents/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,GACtB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,cAAc,GACtB,cAAc,GACd,gBAAgB,GAChB,gBAAgB,GAChB,WAAW,GACX,qBAAqB,CAAC;AAU1B,KAAK,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;AAC5C,KAAK,OAAO,CAAC,CAAC,SAAS,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AAM3F,iBAAS,WAAW,CAAC,SAAS,SAAS,aAAa,EAClD,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,cAe9C;AAED,wBAAgB,IAAI,CAAC,SAAS,SAAS,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,QAO/F;AAID,eAAO,MAAM,yBAAyB;;;;;CASrC,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/index.js b/packages/expo-router/build/navigationEvents/index.js
index db2e124823dcdf..4773606a221bad 100644
--- a/packages/expo-router/build/navigationEvents/index.js
+++ b/packages/expo-router/build/navigationEvents/index.js
@@ -7,6 +7,7 @@ const availableEvents = [
'pageFocused',
'pageBlurred',
'pageRemoved',
+ 'actionDispatched',
];
const subscribers = {};
function addListener(eventType, callback) {
@@ -33,9 +34,6 @@ function emit(type, event) {
}
}
let enabled = false;
-let currentPathname = undefined;
-let currentParams = undefined;
-let currentPathnameListener = undefined;
exports.unstable_navigationEvents = {
addListener,
emit,
@@ -45,44 +43,5 @@ exports.unstable_navigationEvents = {
isEnabled: () => {
return enabled;
},
- saveCurrentPathname: () => {
- if (!enabled || currentPathnameListener)
- return;
- currentPathnameListener = addListener('pageFocused', (event) => {
- currentPathname = event.pathname;
- currentParams = event.params;
- });
- },
- get currentPathname() {
- return currentPathname;
- },
- get currentParams() {
- return currentParams;
- },
};
-if (globalThis.expo) {
- globalThis.expo.router = globalThis.expo.router || {};
- if (!('navigationEvents' in globalThis.expo.router)) {
- Object.defineProperties(globalThis.expo.router, {
- navigationEvents: {
- get() {
- return exports.unstable_navigationEvents;
- },
- enumerable: true,
- },
- currentPathname: {
- get() {
- return currentPathname;
- },
- enumerable: true,
- },
- currentParams: {
- get() {
- return currentParams;
- },
- enumerable: true,
- },
- });
- }
-}
//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/index.js.map b/packages/expo-router/build/navigationEvents/index.js.map
index 6edf5a2d815dda..93dca6cb5dae0d 100644
--- a/packages/expo-router/build/navigationEvents/index.js.map
+++ b/packages/expo-router/build/navigationEvents/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/navigationEvents/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved } from './types';\n\nexport type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved } from './types';\n\nexport type AnalyticsEvent = PageWillRender | PageFocusedEvent | PageBlurredEvent | PageRemoved;\n\nconst availableEvents: AnalyticsEvent['type'][] = [\n 'pageWillRender',\n 'pageFocused',\n 'pageBlurred',\n 'pageRemoved',\n];\n\ntype EventTypeName = AnalyticsEvent['type'];\ntype Payload = Omit, 'type'>;\n\nconst subscribers: {\n [K in EventTypeName]?: Set<(event: Payload) => void>;\n} = {};\n\nfunction addListener(\n eventType: EventType,\n callback: (event: Payload) => void\n) {\n if (!availableEvents.includes(eventType)) {\n throw new Error(`Unsupported event type: ${eventType}`);\n }\n if (!subscribers[eventType]) {\n subscribers[eventType] = new Set() as (typeof subscribers)[EventType];\n }\n subscribers[eventType]!.add(callback);\n return () => {\n subscribers[eventType]!.delete(callback);\n if (subscribers[eventType]!.size === 0) {\n delete subscribers[eventType];\n }\n };\n}\n\nexport function emit(type: EventType, event: Payload) {\n const subscribersForEvent = subscribers[type];\n if (subscribersForEvent) {\n for (const callback of subscribersForEvent) {\n callback(event);\n }\n }\n}\n\nlet enabled = false;\n\nlet currentPathname: string | undefined = undefined;\nlet currentParams: Record | undefined = undefined;\nlet currentPathnameListener: ReturnType | undefined = undefined;\n\nexport const unstable_navigationEvents = {\n addListener,\n emit,\n enable: () => {\n enabled = true;\n },\n isEnabled: () => {\n return enabled;\n },\n saveCurrentPathname: () => {\n if (!enabled || currentPathnameListener) return;\n currentPathnameListener = addListener('pageFocused', (event) => {\n currentPathname = event.pathname;\n currentParams = event.params;\n });\n },\n get currentPathname() {\n return currentPathname;\n },\n get currentParams() {\n return currentParams;\n },\n};\n\nif (globalThis.expo) {\n globalThis.expo.router = globalThis.expo.router || {};\n\n if (!('navigationEvents' in globalThis.expo.router)) {\n Object.defineProperties(globalThis.expo.router, {\n navigationEvents: {\n get() {\n return unstable_navigationEvents;\n },\n enumerable: true,\n },\n currentPathname: {\n get() {\n return currentPathname;\n },\n enumerable: true,\n },\n currentParams: {\n get() {\n return currentParams;\n },\n enumerable: true,\n },\n });\n }\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/navigationEvents/index.ts"],"names":[],"mappings":";;;AAyDA,oBAOC;AAzCD,MAAM,eAAe,GAA6B;IAChD,gBAAgB;IAChB,aAAa;IACb,aAAa;IACb,aAAa;IACb,kBAAkB;CACnB,CAAC;AAKF,MAAM,WAAW,GAEb,EAAE,CAAC;AAEP,SAAS,WAAW,CAClB,SAAoB,EACpB,QAA6C;IAE7C,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,2BAA2B,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,EAAqC,CAAC;IACxE,CAAC;IACD,WAAW,CAAC,SAAS,CAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,OAAO,GAAG,EAAE;QACV,WAAW,CAAC,SAAS,CAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,WAAW,CAAC,SAAS,CAAE,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,IAAI,CAAkC,IAAe,EAAE,KAAyB;IAC9F,MAAM,mBAAmB,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,mBAAmB,EAAE,CAAC;QACxB,KAAK,MAAM,QAAQ,IAAI,mBAAmB,EAAE,CAAC;YAC3C,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC;AAED,IAAI,OAAO,GAAG,KAAK,CAAC;AAEP,QAAA,yBAAyB,GAAG;IACvC,WAAW;IACX,IAAI;IACJ,MAAM,EAAE,GAAG,EAAE;QACX,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IACD,SAAS,EAAE,GAAG,EAAE;QACd,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAC","sourcesContent":["import type {\n PageWillRender,\n PageFocusedEvent,\n PageBlurredEvent,\n PageRemoved,\n ActionDispatchedEvent,\n} from './types';\n\nexport type {\n PageWillRender,\n PageFocusedEvent,\n PageBlurredEvent,\n PageRemoved,\n ActionDispatchedEvent,\n} from './types';\n\nexport type AnalyticsEvent =\n | PageWillRender\n | PageFocusedEvent\n | PageBlurredEvent\n | PageRemoved\n | ActionDispatchedEvent;\n\nconst availableEvents: AnalyticsEvent['type'][] = [\n 'pageWillRender',\n 'pageFocused',\n 'pageBlurred',\n 'pageRemoved',\n 'actionDispatched',\n];\n\ntype EventTypeName = AnalyticsEvent['type'];\ntype Payload = Omit, 'type'>;\n\nconst subscribers: {\n [K in EventTypeName]?: Set<(event: Payload) => void>;\n} = {};\n\nfunction addListener(\n eventType: EventType,\n callback: (event: Payload) => void\n) {\n if (!availableEvents.includes(eventType)) {\n throw new Error(`Unsupported event type: ${eventType}`);\n }\n if (!subscribers[eventType]) {\n subscribers[eventType] = new Set() as (typeof subscribers)[EventType];\n }\n subscribers[eventType]!.add(callback);\n return () => {\n subscribers[eventType]!.delete(callback);\n if (subscribers[eventType]!.size === 0) {\n delete subscribers[eventType];\n }\n };\n}\n\nexport function emit(type: EventType, event: Payload) {\n const subscribersForEvent = subscribers[type];\n if (subscribersForEvent) {\n for (const callback of subscribersForEvent) {\n callback(event);\n }\n }\n}\n\nlet enabled = false;\n\nexport const unstable_navigationEvents = {\n addListener,\n emit,\n enable: () => {\n enabled = true;\n },\n isEnabled: () => {\n return enabled;\n },\n};\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/navigation.d.ts b/packages/expo-router/build/navigationEvents/navigation.d.ts
new file mode 100644
index 00000000000000..22fe6cf60183b0
--- /dev/null
+++ b/packages/expo-router/build/navigationEvents/navigation.d.ts
@@ -0,0 +1,2 @@
+export declare function handleNavigationOnReady(): void;
+//# sourceMappingURL=navigation.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/navigation.d.ts.map b/packages/expo-router/build/navigationEvents/navigation.d.ts.map
new file mode 100644
index 00000000000000..3aa06381af4307
--- /dev/null
+++ b/packages/expo-router/build/navigationEvents/navigation.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/navigationEvents/navigation.ts"],"names":[],"mappings":"AAKA,wBAAgB,uBAAuB,SAYtC"}
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/navigation.js b/packages/expo-router/build/navigationEvents/navigation.js
new file mode 100644
index 00000000000000..e7eb7c5565aada
--- /dev/null
+++ b/packages/expo-router/build/navigationEvents/navigation.js
@@ -0,0 +1,21 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.handleNavigationOnReady = handleNavigationOnReady;
+const _1 = require(".");
+const store_1 = require("../global-state/store");
+let unsubscribe;
+function handleNavigationOnReady() {
+ if (unsubscribe)
+ unsubscribe();
+ unsubscribe = store_1.storeRef.current.navigationRef.addListener('__unsafe_action__', (e) => {
+ if (!e.data.noop && store_1.storeRef.current.state) {
+ const action = e.data.action;
+ (0, _1.emit)('actionDispatched', {
+ actionType: action.type,
+ payload: action.payload,
+ state: store_1.storeRef.current.state,
+ });
+ }
+ });
+}
+//# sourceMappingURL=navigation.js.map
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/navigation.js.map b/packages/expo-router/build/navigationEvents/navigation.js.map
new file mode 100644
index 00000000000000..90a0f5e4bb3b2c
--- /dev/null
+++ b/packages/expo-router/build/navigationEvents/navigation.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/navigationEvents/navigation.ts"],"names":[],"mappings":";;AAKA,0DAYC;AAjBD,wBAAyB;AACzB,iDAAiD;AAEjD,IAAI,WAAqC,CAAC;AAE1C,SAAgB,uBAAuB;IACrC,IAAI,WAAW;QAAE,WAAW,EAAE,CAAC;IAC/B,WAAW,GAAG,gBAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE;QAClF,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,gBAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;YAC7B,IAAA,OAAI,EAAC,kBAAkB,EAAE;gBACvB,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,gBAAQ,CAAC,OAAO,CAAC,KAAK;aAC9B,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { emit } from '.';\nimport { storeRef } from '../global-state/store';\n\nlet unsubscribe: (() => void) | undefined;\n\nexport function handleNavigationOnReady() {\n if (unsubscribe) unsubscribe();\n unsubscribe = storeRef.current.navigationRef.addListener('__unsafe_action__', (e) => {\n if (!e.data.noop && storeRef.current.state) {\n const action = e.data.action;\n emit('actionDispatched', {\n actionType: action.type,\n payload: action.payload,\n state: storeRef.current.state,\n });\n }\n });\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/types.d.ts b/packages/expo-router/build/navigationEvents/types.d.ts
index 1a6d6c1f74b8e8..eadfd8b99c7666 100644
--- a/packages/expo-router/build/navigationEvents/types.d.ts
+++ b/packages/expo-router/build/navigationEvents/types.d.ts
@@ -1,6 +1,8 @@
+import type { ReactNavigationState } from '../global-state/types';
+import type { NavigationAction } from '../react-navigation';
export interface BasePageEvent {
pathname: string;
- params: Record;
+ params: Record;
screenId: string;
}
/**
@@ -20,4 +22,11 @@ export interface PageBlurredEvent extends BasePageEvent {
export interface PageRemoved extends BasePageEvent {
type: 'pageRemoved';
}
+export interface ActionDispatchedEvent {
+ type: 'actionDispatched';
+ /** The action type from the dispatched NavigationAction (e.g. `NAVIGATE`). */
+ actionType: NavigationAction['type'];
+ payload: NavigationAction['payload'];
+ state: ReactNavigationState;
+}
//# sourceMappingURL=types.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/types.d.ts.map b/packages/expo-router/build/navigationEvents/types.d.ts.map
index f5e74ba9b06d44..c1946a4977564b 100644
--- a/packages/expo-router/build/navigationEvents/types.d.ts.map
+++ b/packages/expo-router/build/navigationEvents/types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/navigationEvents/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,IAAI,EAAE,aAAa,CAAC;CACrB"}
\ No newline at end of file
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/navigationEvents/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,kBAAkB,CAAC;IACzB,8EAA8E;IAC9E,UAAU,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACrC,KAAK,EAAE,oBAAoB,CAAC;CAC7B"}
\ No newline at end of file
diff --git a/packages/expo-router/build/navigationEvents/types.js.map b/packages/expo-router/build/navigationEvents/types.js.map
index a6cc22377167f2..79d2680924738d 100644
--- a/packages/expo-router/build/navigationEvents/types.js.map
+++ b/packages/expo-router/build/navigationEvents/types.js.map
@@ -1 +1 @@
-{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/navigationEvents/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface BasePageEvent {\n pathname: string;\n params: Record;\n screenId: string;\n}\n\n/**\n * The rendering of the page started\n *\n * This can happen if screen is to be focused for the first time or when the screen is preloaded\n */\nexport interface PageWillRender extends BasePageEvent {\n type: 'pageWillRender';\n}\n\nexport interface PageFocusedEvent extends BasePageEvent {\n type: 'pageFocused';\n}\n\nexport interface PageBlurredEvent extends BasePageEvent {\n type: 'pageBlurred';\n}\n\nexport interface PageRemoved extends BasePageEvent {\n type: 'pageRemoved';\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/navigationEvents/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ReactNavigationState } from '../global-state/types';\nimport type { NavigationAction } from '../react-navigation';\n\nexport interface BasePageEvent {\n pathname: string;\n params: Record;\n screenId: string;\n}\n\n/**\n * The rendering of the page started\n *\n * This can happen if screen is to be focused for the first time or when the screen is preloaded\n */\nexport interface PageWillRender extends BasePageEvent {\n type: 'pageWillRender';\n}\n\nexport interface PageFocusedEvent extends BasePageEvent {\n type: 'pageFocused';\n}\n\nexport interface PageBlurredEvent extends BasePageEvent {\n type: 'pageBlurred';\n}\n\nexport interface PageRemoved extends BasePageEvent {\n type: 'pageRemoved';\n}\n\nexport interface ActionDispatchedEvent {\n type: 'actionDispatched';\n /** The action type from the dispatched NavigationAction (e.g. `NAVIGATE`). */\n actionType: NavigationAction['type'];\n payload: NavigationAction['payload'];\n state: ReactNavigationState;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/useScreens.d.ts.map b/packages/expo-router/build/useScreens.d.ts.map
index 7660d1e4f1a5c0..319fbf72db98af 100644
--- a/packages/expo-router/build/useScreens.d.ts.map
+++ b/packages/expo-router/build/useScreens.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"useScreens.d.ts","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"useScreens.d.ts","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,SAAS,CAAC;AAetD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQnD,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1D,MAAM,SAAS,eAAe,GAAG,eAAe,EAChD,SAAS,SAAS,YAAY,GAAG,YAAY,IAC3C;IACF,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,OAAO,CAAC,EACJ,QAAQ,GACR,CAAC,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,KAAK,QAAQ,CAAC,CAAC;IAEvF,SAAS,CAAC,EACN,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,GAClC,CAAC,CAAC,IAAI,EAAE;QACN,KAAK,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACxC,UAAU,EAAE,GAAG,CAAC;KACjB,KAAK,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAE9C,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IAE7E,mBAAmB,CAAC,EAAE,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;AA6FxE;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAAE,EACpB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,EAC7B,yBAAyB,GAAE,OAAe,GACzC,KAAK,CAAC,SAAS,EAAE,CA6BnB;AAsDD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS;sCAyCtD;QACD,KAAK,CAAC,EAAE,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACzC,UAAU,EAAE,IAAI,CACd,cAAc,CACZ,aAAa,EACb,MAAM,EACN,SAAS,EACT,eAAe,EACf,MAAM,EACN,6BAA6B,GAAG,2BAA2B,CAC5D,EACD,UAAU,CACX,GAAG;YACF,QAAQ,IAAI,eAAe,GAAG,SAAS,CAAC;SACzC,CAAC;KACH;;EAuFF;AAmFD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,EAChB,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,GAC/B,WAAW,CAAC,SAAS,CAAC,CAqBxB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAE,OAAO,CAAC,WAAW,CAAM,2CAYxD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,UAa5E"}
\ No newline at end of file
diff --git a/packages/expo-router/build/useScreens.js b/packages/expo-router/build/useScreens.js
index 14801e7e8e47d5..660b3925e6519d 100644
--- a/packages/expo-router/build/useScreens.js
+++ b/packages/expo-router/build/useScreens.js
@@ -48,11 +48,11 @@ const react_2 = __importStar(require("react"));
const Route_1 = require("./Route");
const storeContext_1 = require("./global-state/storeContext");
const utils_1 = require("./global-state/utils");
+const hooks_1 = require("./hooks");
const import_mode_1 = __importDefault(require("./import-mode"));
const ZoomTransitionEnabler_1 = require("./link/zoom/ZoomTransitionEnabler");
const zoom_transition_context_providers_1 = require("./link/zoom/zoom-transition-context-providers");
const navigationEvents_1 = require("./navigationEvents");
-const utils_2 = require("./navigationEvents/utils");
const navigationParams_1 = require("./navigationParams");
const primitives_1 = require("./primitives");
const native_1 = require("./react-navigation/native");
@@ -260,46 +260,49 @@ function getQualifiedRouteComponent(value) {
return BaseRoute;
}
function AnalyticsListeners({ navigation, screenId, }) {
- const stateForPath = (0, native_1.useStateForPath)();
const isFirstRenderRef = react_2.default.useRef(true);
const hasBlurredRef = react_2.default.useRef(true);
- const stringUrl = (0, react_2.useMemo)(() => (0, utils_2.generateStringUrlForState)(stateForPath), [stateForPath]);
+ const routeInfo = (0, hooks_1.useCurrentRouteInfo)();
if (isFirstRenderRef.current) {
isFirstRenderRef.current = false;
- if (stringUrl) {
+ if (routeInfo) {
navigationEvents_1.unstable_navigationEvents.emit('pageWillRender', {
- ...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
}
}
(0, react_2.useEffect)(() => {
- if (stringUrl) {
+ if (routeInfo) {
return () => {
navigationEvents_1.unstable_navigationEvents.emit('pageRemoved', {
- ...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
};
}
return () => { };
- }, [stringUrl, screenId]);
+ }, [routeInfo?.params, routeInfo?.pathname, screenId]);
const isFocused = navigation.isFocused();
- if (isFocused && stringUrl) {
+ if (isFocused && routeInfo) {
navigationEvents_1.unstable_navigationEvents.emit('pageFocused', {
- ...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
hasBlurredRef.current = false;
}
(0, react_2.useEffect)(() => {
- if (stringUrl) {
+ if (routeInfo) {
const cleanFocus = navigation.addListener('focus', () => {
// If the screen was not blurred, don't emit focused again
// hasBlurredRef will be false when the screen was initially focused
if (hasBlurredRef.current) {
navigationEvents_1.unstable_navigationEvents.emit('pageFocused', {
- ...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
hasBlurredRef.current = false;
@@ -307,7 +310,8 @@ function AnalyticsListeners({ navigation, screenId, }) {
});
const cleanBlur = navigation.addListener('blur', () => {
navigationEvents_1.unstable_navigationEvents.emit('pageBlurred', {
- ...(0, utils_2.getPathAndParamsFromStringUrl)(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
hasBlurredRef.current = true;
@@ -318,7 +322,7 @@ function AnalyticsListeners({ navigation, screenId, }) {
};
}
return () => { };
- }, [navigation, stringUrl, screenId]);
+ }, [navigation, routeInfo?.pathname, routeInfo?.params, screenId]);
return null;
}
function screenOptionsFactory(route, options) {
diff --git a/packages/expo-router/build/useScreens.js.map b/packages/expo-router/build/useScreens.js.map
index 4f38cf8b380892..df6a406c3ba2e1 100644
--- a/packages/expo-router/build/useScreens.js.map
+++ b/packages/expo-router/build/useScreens.js.map
@@ -1 +1 @@
-{"version":3,"file":"useScreens.js","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport React, { use, useEffect, useMemo } from 'react';\n\nimport type { LoadedRoute, RouteNode } from './Route';\nimport { SuspenseFallbackContext, Route, sortRoutesWithInitial, useRouteNode } from './Route';\nimport { useExpoRouterStore } from './global-state/storeContext';\nimport { useColorSchemeChangesIfNeeded } from './global-state/utils';\nimport EXPO_ROUTER_IMPORT_MODE from './import-mode';\nimport { ZoomTransitionEnabler } from './link/zoom/ZoomTransitionEnabler';\nimport { ZoomTransitionTargetContextProvider } from './link/zoom/zoom-transition-context-providers';\nimport { unstable_navigationEvents } from './navigationEvents';\nimport { generateStringUrlForState, getPathAndParamsFromStringUrl } from './navigationEvents/utils';\nimport {\n hasParam,\n INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME,\n removeParams,\n} from './navigationParams';\nimport { Screen } from './primitives';\nimport type { BottomTabNavigationEventMap } from './react-navigation/bottom-tabs';\nimport {\n useStateForPath,\n type EventConsumer,\n type EventMapBase,\n type NavigationProp,\n type NavigationState,\n type ParamListBase,\n type RouteProp,\n type ScreenListeners,\n} from './react-navigation/native';\nimport type { NativeStackNavigationEventMap } from './react-navigation/native-stack';\nimport type { UnknownOutputParams } from './types';\nimport { EmptyRoute } from './views/EmptyRoute';\nimport {\n SuspenseFallback as DefaultSuspenseFallback,\n type SuspenseFallbackProps,\n} from './views/SuspenseFallback';\nimport { Try } from './views/Try';\n\nexport type ScreenProps<\n TOptions extends Record = Record,\n TState extends NavigationState = NavigationState,\n TEventMap extends EventMapBase = EventMapBase,\n> = {\n /** Name is required when used inside a Layout component. */\n name?: string;\n /**\n * Redirect to the nearest sibling route.\n * If all children are `redirect={true}`, the layout will render `null` as there are no children to render.\n */\n redirect?: boolean;\n initialParams?: Record;\n options?:\n | TOptions\n | ((prop: { route: RouteProp; navigation: any }) => TOptions);\n\n listeners?:\n | ScreenListeners\n | ((prop: {\n route: RouteProp;\n navigation: any;\n }) => ScreenListeners);\n\n getId?: ({ params }: { params?: Record }) => string | undefined;\n\n dangerouslySingular?: SingularOptions;\n};\n\nexport type SingularOptions =\n | boolean\n | ((name: string, params: UnknownOutputParams) => string | undefined);\n\nfunction getSortedChildren(\n children: RouteNode[],\n order: ScreenProps[] = [],\n initialRouteName?: string\n): { route: RouteNode; props: Partial }[] {\n if (!order?.length) {\n return children\n .sort(sortRoutesWithInitial(initialRouteName))\n .map((route) => ({ route, props: {} }));\n }\n const entries = [...children];\n\n const ordered = order\n .map(\n ({\n name,\n redirect,\n initialParams,\n listeners,\n options,\n getId,\n dangerouslySingular: singular,\n }) => {\n if (!entries.length) {\n console.warn(\n `[Layout children]: Too many screens defined. Route \"${name}\" is extraneous.`\n );\n return null;\n }\n const matchIndex = entries.findIndex(\n (child) => child.route === name || child.route === `${name}/index`\n );\n if (matchIndex === -1) {\n console.warn(\n `[Layout children]: No route named \"${name}\" exists in nested children:`,\n children.map(({ route }) => route)\n );\n return null;\n } else {\n // Get match and remove from entries\n const match = entries[matchIndex];\n entries.splice(matchIndex, 1);\n\n // Ensure to return null after removing from entries.\n if (redirect) {\n if (typeof redirect === 'string') {\n throw new Error(`Redirecting to a specific route is not supported yet.`);\n }\n return null;\n }\n\n if (getId) {\n console.warn(\n `Deprecated: prop 'getId' on screen ${name} is deprecated. Please rename the prop to 'dangerouslySingular'`\n );\n if (singular) {\n console.warn(\n `Screen ${name} cannot use both getId and dangerouslySingular together.`\n );\n }\n } else if (singular) {\n // If singular is set, use it as the getId function.\n if (typeof singular === 'string') {\n getId = () => singular;\n } else if (typeof singular === 'function' && name) {\n getId = (options) => singular(name, options.params || {});\n } else if (singular === true && name) {\n getId = (options) => getSingularId(name, options);\n }\n }\n\n return {\n route: match,\n props: { initialParams, listeners, options, getId },\n };\n }\n }\n )\n .filter(Boolean) as {\n route: RouteNode;\n props: Partial;\n }[];\n\n // Add any remaining children\n ordered.push(\n ...entries.sort(sortRoutesWithInitial(initialRouteName)).map((route) => ({ route, props: {} }))\n );\n\n return ordered;\n}\n\n/**\n * @returns React Navigation screens sorted by the `route` property.\n */\nexport function useSortedScreens(\n order: ScreenProps[],\n protectedScreens: Set,\n useOnlyUserDefinedScreens: boolean = false\n): React.ReactNode[] {\n const node = useRouteNode();\n\n const nodeChildren = node?.children ?? [];\n const children = useOnlyUserDefinedScreens\n ? nodeChildren.filter((child) =>\n order.some(\n (userDefinedScreen) =>\n userDefinedScreen.name === child.route ||\n `${userDefinedScreen.name}/index` === child.route\n )\n )\n : nodeChildren;\n\n const sorted = children.length ? getSortedChildren(children, order, node?.initialRouteName) : [];\n return React.useMemo(\n () =>\n sorted\n .filter((item) => {\n const route = item.route.route;\n return (\n !protectedScreens.has(route) && !protectedScreens.has(route.replace(/\\/index$/, ''))\n );\n })\n .map((value) => {\n return routeToScreen(value.route, value.props);\n }),\n [sorted, protectedScreens]\n );\n}\n\nfunction fromImport(\n value: RouteNode,\n { ErrorBoundary, SuspenseFallback, ...component }: LoadedRoute\n) {\n // If possible, add a more helpful display name for the component stack to improve debugging of React errors such as `Text strings must be rendered within a component.`.\n if (component?.default && __DEV__) {\n component.default.displayName ??= `${component.default.name ?? 'Route'}(${value.contextKey})`;\n }\n\n if (ErrorBoundary) {\n const Wrapped = React.forwardRef((props: any, ref: any) => {\n const children = React.createElement(component.default || EmptyRoute, {\n ...props,\n ref,\n });\n return {children};\n });\n\n if (__DEV__) {\n Wrapped.displayName = `ErrorBoundary(${value.contextKey})`;\n }\n\n return {\n default: Wrapped,\n SuspenseFallback,\n };\n }\n if (process.env.NODE_ENV !== 'production') {\n if (\n typeof component.default === 'object' &&\n component.default &&\n Object.keys(component.default).length === 0\n ) {\n return { default: EmptyRoute, SuspenseFallback };\n }\n }\n\n return { default: component.default, SuspenseFallback };\n}\n\nfunction fromLoadedRoute(value: RouteNode, res: LoadedRoute) {\n if (!(res instanceof Promise)) {\n return fromImport(value, res);\n }\n\n return res.then(fromImport.bind(null, value));\n}\n\n// TODO: Maybe there's a more React-y way to do this?\n// Without this store, the process enters a recursive loop.\nconst qualifiedStore = new WeakMap>();\n\n/** Wrap the component with various enhancements and add access to child routes. */\nexport function getQualifiedRouteComponent(value: RouteNode) {\n if (qualifiedStore.has(value)) {\n return qualifiedStore.get(value)!;\n }\n\n let ScreenComponent:\n | React.ForwardRefExoticComponent>\n | React.ComponentType<{ segment?: string }>;\n\n let LayoutSuspenseFallback: React.ComponentType | undefined;\n\n // TODO: This ensures sync doesn't use React.lazy, but it's not ideal.\n if (EXPO_ROUTER_IMPORT_MODE === 'lazy') {\n ScreenComponent = React.lazy(async () => {\n const res = value.loadRoute();\n return fromLoadedRoute(value, res) as Promise<{\n default: React.ComponentType;\n }>;\n });\n\n if (__DEV__) {\n ScreenComponent.displayName = `AsyncRoute(${value.route})`;\n }\n } else {\n const res = value.loadRoute();\n const result = fromImport(value, res);\n ScreenComponent = result.default!;\n LayoutSuspenseFallback = value.type === 'layout' ? result.SuspenseFallback : undefined;\n }\n const WrappedScreenComponent: typeof ScreenComponent = (props: object) => {\n useColorSchemeChangesIfNeeded();\n return ;\n };\n function BaseRoute({\n // Remove these React Navigation props to\n // enforce usage of expo-router hooks (where the query params are correct).\n route,\n navigation,\n\n // Pass all other props to the component\n ...props\n }: {\n route?: RouteProp;\n navigation: Omit<\n NavigationProp<\n ParamListBase,\n string,\n undefined,\n NavigationState,\n object,\n NativeStackNavigationEventMap | BottomTabNavigationEventMap\n >,\n 'getState'\n > & {\n getState(): NavigationState | undefined;\n };\n }) {\n const stateForPath = useStateForPath();\n const isFocused = navigation.isFocused();\n const store = useExpoRouterStore();\n const InheritedSuspenseFallback = use(SuspenseFallbackContext);\n\n const ResolvedSuspenseFallback =\n EXPO_ROUTER_IMPORT_MODE === 'lazy'\n ? DefaultSuspenseFallback\n : (LayoutSuspenseFallback ?? InheritedSuspenseFallback ?? DefaultSuspenseFallback);\n const providedSuspenseFallback =\n value.type === 'layout'\n ? (LayoutSuspenseFallback ?? InheritedSuspenseFallback)\n : InheritedSuspenseFallback;\n\n if (isFocused) {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }\n\n useEffect(\n () =>\n navigation.addListener('focus', () => {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n // Because setFocusedState caches the route info, this call will only trigger rerenders\n // if the component itself didnât rerender and the route info changed.\n // Otherwise, the update from the `if` above will handle it,\n // and this wonât cause a redundant second update.\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }),\n [navigation]\n );\n\n useEffect(() => {\n return navigation.addListener('transitionEnd', (e) => {\n if (!e?.data?.closing) {\n // When navigating to a screen, remove the no animation param to re-enable animations\n // Otherwise the navigation back would also have no animation\n if (hasParam(route?.params, INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME)) {\n navigation.replaceParams(\n removeParams(route?.params, [INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME])\n );\n }\n }\n });\n }, [navigation]);\n\n const isRouteType = value.type === 'route';\n const hasRouteKey = !!route?.key;\n\n return (\n \n \n {unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && (\n \n )}\n \n \n \n }>\n \n \n \n \n \n );\n }\n\n if (__DEV__) {\n BaseRoute.displayName = `Route(${value.route})`;\n }\n\n qualifiedStore.set(value, BaseRoute);\n return BaseRoute;\n}\n\nfunction AnalyticsListeners({\n navigation,\n screenId,\n}: {\n navigation: EventConsumer & {\n isFocused(): boolean;\n };\n screenId: string;\n}) {\n const stateForPath = useStateForPath();\n const isFirstRenderRef = React.useRef(true);\n const hasBlurredRef = React.useRef(true);\n const stringUrl = useMemo(() => generateStringUrlForState(stateForPath), [stateForPath]);\n\n if (isFirstRenderRef.current) {\n isFirstRenderRef.current = false;\n if (stringUrl) {\n unstable_navigationEvents.emit('pageWillRender', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n }\n }\n\n useEffect(() => {\n if (stringUrl) {\n return () => {\n unstable_navigationEvents.emit('pageRemoved', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n };\n }\n return () => {};\n }, [stringUrl, screenId]);\n\n const isFocused = navigation.isFocused();\n\n if (isFocused && stringUrl) {\n unstable_navigationEvents.emit('pageFocused', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = false;\n }\n\n useEffect(() => {\n if (stringUrl) {\n const cleanFocus = navigation.addListener('focus', () => {\n // If the screen was not blurred, don't emit focused again\n // hasBlurredRef will be false when the screen was initially focused\n if (hasBlurredRef.current) {\n unstable_navigationEvents.emit('pageFocused', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = false;\n }\n });\n const cleanBlur = navigation.addListener('blur', () => {\n unstable_navigationEvents.emit('pageBlurred', {\n ...getPathAndParamsFromStringUrl(stringUrl),\n screenId,\n });\n hasBlurredRef.current = true;\n });\n return () => {\n cleanFocus();\n cleanBlur();\n };\n }\n return () => {};\n }, [navigation, stringUrl, screenId]);\n\n return null;\n}\n\nexport function screenOptionsFactory(\n route: RouteNode,\n options?: ScreenProps['options']\n): ScreenProps['options'] {\n return (args) => {\n // Only eager load generated components\n const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null;\n const staticResult = typeof staticOptions === 'function' ? staticOptions(args) : staticOptions;\n const dynamicResult = typeof options === 'function' ? options?.(args) : options;\n const output = {\n ...staticResult,\n ...dynamicResult,\n };\n\n // Prevent generated screens from showing up in the tab bar.\n if (route.internal) {\n output.tabBarItemStyle = { display: 'none' };\n output.tabBarButton = () => null;\n // TODO: React Navigation doesn't provide a way to prevent rendering the drawer item.\n output.drawerItemStyle = { height: 0, display: 'none' };\n }\n\n return output;\n };\n}\n\nexport function routeToScreen(\n route: RouteNode,\n { options, getId, ...props }: Partial = {}\n) {\n return (\n getQualifiedRouteComponent(route)}\n />\n );\n}\n\nexport function getSingularId(name: string, options: Record = {}) {\n return name\n .split('/')\n .map((segment) => {\n if (segment.startsWith('[...')) {\n return options.params?.[segment.slice(4, -1)]?.join('/') || segment;\n } else if (segment.startsWith('[') && segment.endsWith(']')) {\n return options.params?.[segment.slice(1, -1)] || segment;\n } else {\n return segment;\n }\n })\n .join('/');\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"useScreens.js","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsKb,4CAiCC;AAuDD,gEA+IC;AAmFD,oDAwBC;AAED,sCAcC;AAED,sCAaC;;;AArhBD,+CAA8C;AAG9C,mCAA8F;AAC9F,8DAAiE;AACjE,gDAAqE;AACrE,mCAA8C;AAC9C,gEAAoD;AACpD,6EAA0E;AAC1E,qGAAoG;AACpG,yDAA+D;AAC/D,yDAI4B;AAC5B,6CAAsC;AAEtC,sDASmC;AAGnC,mDAAgD;AAChD,+DAGkC;AAClC,qCAAkC;AAmClC,SAAS,iBAAiB,CACxB,QAAqB,EACrB,QAAuB,EAAE,EACzB,gBAAyB;IAEzB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,QAAQ;aACZ,IAAI,CAAC,IAAA,6BAAqB,EAAC,gBAAgB,CAAC,CAAC;aAC7C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;IAE9B,MAAM,OAAO,GAAG,KAAK;SAClB,GAAG,CACF,CAAC,EACC,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,SAAS,EACT,OAAO,EACP,KAAK,EACL,mBAAmB,EAAE,QAAQ,GAC9B,EAAE,EAAE;QACH,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CACV,uDAAuD,IAAI,kBAAkB,CAC9E,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAClC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,GAAG,IAAI,QAAQ,CACnE,CAAC;QACF,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CACV,sCAAsC,IAAI,8BAA8B,EACxE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CACnC,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;aAAM,CAAC;YACN,oCAAoC;YACpC,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YAE9B,qDAAqD;YACrD,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;oBACjC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;gBAC3E,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CACV,sCAAsC,IAAI,iEAAiE,CAC5G,CAAC;gBACF,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,CAAC,IAAI,CACV,UAAU,IAAI,0DAA0D,CACzE,CAAC;gBACJ,CAAC;YACH,CAAC;iBAAM,IAAI,QAAQ,EAAE,CAAC;gBACpB,oDAAoD;gBACpD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;oBACjC,KAAK,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC;gBACzB,CAAC;qBAAM,IAAI,OAAO,QAAQ,KAAK,UAAU,IAAI,IAAI,EAAE,CAAC;oBAClD,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;gBAC5D,CAAC;qBAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,IAAI,EAAE,CAAC;oBACrC,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;YAED,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;aACpD,CAAC;QACJ,CAAC;IACH,CAAC,CACF;SACA,MAAM,CAAC,OAAO,CAGd,CAAC;IAEJ,6BAA6B;IAC7B,OAAO,CAAC,IAAI,CACV,GAAG,OAAO,CAAC,IAAI,CAAC,IAAA,6BAAqB,EAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAChG,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAC9B,KAAoB,EACpB,gBAA6B,EAC7B,4BAAqC,KAAK;IAE1C,MAAM,IAAI,GAAG,IAAA,oBAAY,GAAE,CAAC;IAE5B,MAAM,YAAY,GAAG,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,yBAAyB;QACxC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC5B,KAAK,CAAC,IAAI,CACR,CAAC,iBAAiB,EAAE,EAAE,CACpB,iBAAiB,CAAC,IAAI,KAAK,KAAK,CAAC,KAAK;YACtC,GAAG,iBAAiB,CAAC,IAAI,QAAQ,KAAK,KAAK,CAAC,KAAK,CACpD,CACF;QACH,CAAC,CAAC,YAAY,CAAC;IAEjB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjG,OAAO,eAAK,CAAC,OAAO,CAClB,GAAG,EAAE,CACH,MAAM;SACH,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAC/B,OAAO,CACL,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CACrF,CAAC;IACJ,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,OAAO,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC,EACN,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,KAAgB,EAChB,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG,SAAS,EAAe;IAE9D,gLAAgL;IAChL,IAAI,SAAS,EAAE,OAAO,IAAI,OAAO,EAAE,CAAC;QAClC,SAAS,CAAC,OAAO,CAAC,WAAW,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC;IAChG,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,eAAK,CAAC,UAAU,CAAC,CAAC,KAAU,EAAE,GAAQ,EAAE,EAAE;YACxD,MAAM,QAAQ,GAAG,eAAK,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,IAAI,uBAAU,EAAE;gBACpE,GAAG,KAAK;gBACR,GAAG;aACJ,CAAC,CAAC;YACH,OAAO,uBAAC,SAAG,IAAC,KAAK,EAAE,aAAa,YAAG,QAAQ,GAAO,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,WAAW,GAAG,iBAAiB,KAAK,CAAC,UAAU,GAAG,CAAC;QAC7D,CAAC;QAED,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,gBAAgB;SACjB,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,IACE,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ;YACrC,SAAS,CAAC,OAAO;YACjB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAC3C,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,uBAAU,EAAE,gBAAgB,EAAE,CAAC;QACnD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,eAAe,CAAC,KAAgB,EAAE,GAAgB;IACzD,IAAI,CAAC,CAAC,GAAG,YAAY,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,qDAAqD;AACrD,2DAA2D;AAC3D,MAAM,cAAc,GAAG,IAAI,OAAO,EAAuC,CAAC;AAE1E,mFAAmF;AACnF,SAAgB,0BAA0B,CAAC,KAAgB;IACzD,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,cAAc,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC;IACpC,CAAC;IAED,IAAI,eAEyC,CAAC;IAE9C,IAAI,sBAA8E,CAAC;IAEnF,sEAAsE;IACtE,IAAI,qBAAuB,KAAK,MAAM,EAAE,CAAC;QACvC,eAAe,GAAG,eAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YACtC,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAC9B,OAAO,eAAe,CAAC,KAAK,EAAE,GAAG,CAE/B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,OAAO,EAAE,CAAC;YACZ,eAAe,CAAC,WAAW,GAAG,cAAc,KAAK,CAAC,KAAK,GAAG,CAAC;QAC7D,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,eAAe,GAAG,MAAM,CAAC,OAAQ,CAAC;QAClC,sBAAsB,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,CAAC;IACD,MAAM,sBAAsB,GAA2B,CAAC,KAAa,EAAE,EAAE;QACvE,IAAA,qCAA6B,GAAE,CAAC;QAChC,OAAO,uBAAC,eAAe,OAAK,KAAK,GAAI,CAAC;IACxC,CAAC,CAAC;IACF,SAAS,SAAS,CAAC;IACjB,yCAAyC;IACzC,2EAA2E;IAC3E,KAAK,EACL,UAAU;IAEV,wCAAwC;IACxC,GAAG,KAAK,EAgBT;QACC,MAAM,YAAY,GAAG,IAAA,wBAAe,GAAE,CAAC;QACvC,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,IAAA,iCAAkB,GAAE,CAAC;QACnC,MAAM,yBAAyB,GAAG,IAAA,WAAG,EAAC,+BAAuB,CAAC,CAAC;QAE/D,MAAM,wBAAwB,GAC5B,qBAAuB,KAAK,MAAM;YAChC,CAAC,CAAC,mCAAuB;YACzB,CAAC,CAAC,CAAC,sBAAsB,IAAI,yBAAyB,IAAI,mCAAuB,CAAC,CAAC;QACvF,MAAM,wBAAwB,GAC5B,KAAK,CAAC,IAAI,KAAK,QAAQ;YACrB,CAAC,CAAC,CAAC,sBAAsB,IAAI,yBAAyB,CAAC;YACvD,CAAC,CAAC,yBAAyB,CAAC;QAEhC,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAE,CAAC,CAAC;YACjE,IAAI,MAAM,IAAI,YAAY;gBAAE,KAAK,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAClE,CAAC;QAED,IAAA,iBAAS,EACP,GAAG,EAAE,CACH,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE;YACnC,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAE,CAAC,CAAC;YACjE,uFAAuF;YACvF,sEAAsE;YACtE,4DAA4D;YAC5D,kDAAkD;YAClD,IAAI,MAAM,IAAI,YAAY;gBAAE,KAAK,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAClE,CAAC,CAAC,EACJ,CAAC,UAAU,CAAC,CACb,CAAC;QAEF,IAAA,iBAAS,EAAC,GAAG,EAAE;YACb,OAAO,UAAU,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE;gBACnD,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBACtB,qFAAqF;oBACrF,6DAA6D;oBAC7D,IAAI,IAAA,2BAAQ,EAAC,KAAK,EAAE,MAAM,EAAE,+DAA4C,CAAC,EAAE,CAAC;wBAC1E,UAAU,CAAC,aAAa,CACtB,IAAA,+BAAY,EAAC,KAAK,EAAE,MAAM,EAAE,CAAC,+DAA4C,CAAC,CAAC,CAC5E,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;QAEjB,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;QAC3C,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC;QAEjC,OAAO,CACL,uBAAC,aAAK,IAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YACvC,wBAAC,+BAAuB,IAAC,KAAK,EAAE,wBAAwB,aACrD,4CAAyB,CAAC,SAAS,EAAE,IAAI,WAAW,IAAI,WAAW,IAAI,CACtE,uBAAC,kBAAkB,IAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,GAAI,CACpE,EACD,wBAAC,uEAAmC,IAAC,KAAK,EAAE,KAAK,aAC/C,uBAAC,6CAAqB,IAAC,KAAK,EAAE,KAAK,GAAI,EACvC,uBAAC,eAAK,CAAC,QAAQ,IACb,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,EAChD,QAAQ,EACN,uBAAC,wBAAwB,IACvB,KAAK,EAAE,KAAK,CAAC,UAAU,EACvB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAoC,GAChE,YAEJ,uBAAC,sBAAsB,OACjB,KAAK;oCACT,oEAAoE;oCACpE,gEAAgE;oCAChE,OAAO,EAAE,KAAK,CAAC,KAAK,GACpB,GACa,IACmB,IACd,GACpB,CACT,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,SAAS,CAAC,WAAW,GAAG,SAAS,KAAK,CAAC,KAAK,GAAG,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACrC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,UAAU,EACV,QAAQ,GAMT;IACC,MAAM,gBAAgB,GAAG,eAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,eAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,IAAA,2BAAmB,GAAE,CAAC;IAExC,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC7B,gBAAgB,CAAC,OAAO,GAAG,KAAK,CAAC;QACjC,IAAI,SAAS,EAAE,CAAC;YACd,4CAAyB,CAAC,IAAI,CAAC,gBAAgB,EAAE;gBAC/C,QAAQ,EAAE,SAAS,CAAC,QAAQ;gBAC5B,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,GAAG,EAAE;gBACV,4CAAyB,CAAC,IAAI,CAAC,aAAa,EAAE;oBAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ;oBAC5B,MAAM,EAAE,SAAS,CAAC,MAAM;oBACxB,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAClB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEvD,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;IAEzC,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;QAC3B,4CAAyB,CAAC,IAAI,CAAC,aAAa,EAAE;YAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,QAAQ;SACT,CAAC,CAAC;QACH,aAAa,CAAC,OAAO,GAAG,KAAK,CAAC;IAChC,CAAC;IAED,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE;gBACtD,0DAA0D;gBAC1D,oEAAoE;gBACpE,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;oBAC1B,4CAAyB,CAAC,IAAI,CAAC,aAAa,EAAE;wBAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ;wBAC5B,MAAM,EAAE,SAAS,CAAC,MAAM;wBACxB,QAAQ;qBACT,CAAC,CAAC;oBACH,aAAa,CAAC,OAAO,GAAG,KAAK,CAAC;gBAChC,CAAC;YACH,CAAC,CAAC,CAAC;YACH,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE;gBACpD,4CAAyB,CAAC,IAAI,CAAC,aAAa,EAAE;oBAC5C,QAAQ,EAAE,SAAS,CAAC,QAAQ;oBAC5B,MAAM,EAAE,SAAS,CAAC,MAAM;oBACxB,QAAQ;iBACT,CAAC,CAAC;gBACH,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC/B,CAAC,CAAC,CAAC;YACH,OAAO,GAAG,EAAE;gBACV,UAAU,EAAE,CAAC;gBACb,SAAS,EAAE,CAAC;YACd,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAClB,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,oBAAoB,CAClC,KAAgB,EAChB,OAAgC;IAEhC,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,uCAAuC;QACvC,MAAM,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QAChF,MAAM,YAAY,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;QAC/F,MAAM,aAAa,GAAG,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAChF,MAAM,MAAM,GAAG;YACb,GAAG,YAAY;YACf,GAAG,aAAa;SACjB,CAAC;QAEF,4DAA4D;QAC5D,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,CAAC,eAAe,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,MAAM,CAAC,YAAY,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC;YACjC,qFAAqF;YACrF,MAAM,CAAC,eAAe,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC1D,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,aAAa,CAC3B,KAAgB,EAChB,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,KAA2B,EAAE;IAEvD,OAAO,CACL,2BAAC,mBAAM,OACD,KAAK,EACT,IAAI,EAAE,KAAK,CAAC,KAAK,EACjB,GAAG,EAAE,KAAK,CAAC,KAAK,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,EAC7C,YAAY,EAAE,GAAG,EAAE,CAAC,0BAA0B,CAAC,KAAK,CAAC,GACrD,CACH,CAAC;AACJ,CAAC;AAED,SAAgB,aAAa,CAAC,IAAY,EAAE,UAA+B,EAAE;IAC3E,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACf,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC;QACtE,CAAC;aAAM,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5D,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;QAC3D,CAAC;aAAM,CAAC;YACN,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC","sourcesContent":["'use client';\n\nimport React, { use, useEffect } from 'react';\n\nimport type { LoadedRoute, RouteNode } from './Route';\nimport { SuspenseFallbackContext, Route, sortRoutesWithInitial, useRouteNode } from './Route';\nimport { useExpoRouterStore } from './global-state/storeContext';\nimport { useColorSchemeChangesIfNeeded } from './global-state/utils';\nimport { useCurrentRouteInfo } from './hooks';\nimport EXPO_ROUTER_IMPORT_MODE from './import-mode';\nimport { ZoomTransitionEnabler } from './link/zoom/ZoomTransitionEnabler';\nimport { ZoomTransitionTargetContextProvider } from './link/zoom/zoom-transition-context-providers';\nimport { unstable_navigationEvents } from './navigationEvents';\nimport {\n hasParam,\n INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME,\n removeParams,\n} from './navigationParams';\nimport { Screen } from './primitives';\nimport type { BottomTabNavigationEventMap } from './react-navigation/bottom-tabs';\nimport {\n useStateForPath,\n type EventConsumer,\n type EventMapBase,\n type NavigationProp,\n type NavigationState,\n type ParamListBase,\n type RouteProp,\n type ScreenListeners,\n} from './react-navigation/native';\nimport type { NativeStackNavigationEventMap } from './react-navigation/native-stack';\nimport type { UnknownOutputParams } from './types';\nimport { EmptyRoute } from './views/EmptyRoute';\nimport {\n SuspenseFallback as DefaultSuspenseFallback,\n type SuspenseFallbackProps,\n} from './views/SuspenseFallback';\nimport { Try } from './views/Try';\n\nexport type ScreenProps<\n TOptions extends Record = Record,\n TState extends NavigationState = NavigationState,\n TEventMap extends EventMapBase = EventMapBase,\n> = {\n /** Name is required when used inside a Layout component. */\n name?: string;\n /**\n * Redirect to the nearest sibling route.\n * If all children are `redirect={true}`, the layout will render `null` as there are no children to render.\n */\n redirect?: boolean;\n initialParams?: Record;\n options?:\n | TOptions\n | ((prop: { route: RouteProp; navigation: any }) => TOptions);\n\n listeners?:\n | ScreenListeners\n | ((prop: {\n route: RouteProp;\n navigation: any;\n }) => ScreenListeners);\n\n getId?: ({ params }: { params?: Record }) => string | undefined;\n\n dangerouslySingular?: SingularOptions;\n};\n\nexport type SingularOptions =\n | boolean\n | ((name: string, params: UnknownOutputParams) => string | undefined);\n\nfunction getSortedChildren(\n children: RouteNode[],\n order: ScreenProps[] = [],\n initialRouteName?: string\n): { route: RouteNode; props: Partial }[] {\n if (!order?.length) {\n return children\n .sort(sortRoutesWithInitial(initialRouteName))\n .map((route) => ({ route, props: {} }));\n }\n const entries = [...children];\n\n const ordered = order\n .map(\n ({\n name,\n redirect,\n initialParams,\n listeners,\n options,\n getId,\n dangerouslySingular: singular,\n }) => {\n if (!entries.length) {\n console.warn(\n `[Layout children]: Too many screens defined. Route \"${name}\" is extraneous.`\n );\n return null;\n }\n const matchIndex = entries.findIndex(\n (child) => child.route === name || child.route === `${name}/index`\n );\n if (matchIndex === -1) {\n console.warn(\n `[Layout children]: No route named \"${name}\" exists in nested children:`,\n children.map(({ route }) => route)\n );\n return null;\n } else {\n // Get match and remove from entries\n const match = entries[matchIndex];\n entries.splice(matchIndex, 1);\n\n // Ensure to return null after removing from entries.\n if (redirect) {\n if (typeof redirect === 'string') {\n throw new Error(`Redirecting to a specific route is not supported yet.`);\n }\n return null;\n }\n\n if (getId) {\n console.warn(\n `Deprecated: prop 'getId' on screen ${name} is deprecated. Please rename the prop to 'dangerouslySingular'`\n );\n if (singular) {\n console.warn(\n `Screen ${name} cannot use both getId and dangerouslySingular together.`\n );\n }\n } else if (singular) {\n // If singular is set, use it as the getId function.\n if (typeof singular === 'string') {\n getId = () => singular;\n } else if (typeof singular === 'function' && name) {\n getId = (options) => singular(name, options.params || {});\n } else if (singular === true && name) {\n getId = (options) => getSingularId(name, options);\n }\n }\n\n return {\n route: match,\n props: { initialParams, listeners, options, getId },\n };\n }\n }\n )\n .filter(Boolean) as {\n route: RouteNode;\n props: Partial;\n }[];\n\n // Add any remaining children\n ordered.push(\n ...entries.sort(sortRoutesWithInitial(initialRouteName)).map((route) => ({ route, props: {} }))\n );\n\n return ordered;\n}\n\n/**\n * @returns React Navigation screens sorted by the `route` property.\n */\nexport function useSortedScreens(\n order: ScreenProps[],\n protectedScreens: Set,\n useOnlyUserDefinedScreens: boolean = false\n): React.ReactNode[] {\n const node = useRouteNode();\n\n const nodeChildren = node?.children ?? [];\n const children = useOnlyUserDefinedScreens\n ? nodeChildren.filter((child) =>\n order.some(\n (userDefinedScreen) =>\n userDefinedScreen.name === child.route ||\n `${userDefinedScreen.name}/index` === child.route\n )\n )\n : nodeChildren;\n\n const sorted = children.length ? getSortedChildren(children, order, node?.initialRouteName) : [];\n return React.useMemo(\n () =>\n sorted\n .filter((item) => {\n const route = item.route.route;\n return (\n !protectedScreens.has(route) && !protectedScreens.has(route.replace(/\\/index$/, ''))\n );\n })\n .map((value) => {\n return routeToScreen(value.route, value.props);\n }),\n [sorted, protectedScreens]\n );\n}\n\nfunction fromImport(\n value: RouteNode,\n { ErrorBoundary, SuspenseFallback, ...component }: LoadedRoute\n) {\n // If possible, add a more helpful display name for the component stack to improve debugging of React errors such as `Text strings must be rendered within a component.`.\n if (component?.default && __DEV__) {\n component.default.displayName ??= `${component.default.name ?? 'Route'}(${value.contextKey})`;\n }\n\n if (ErrorBoundary) {\n const Wrapped = React.forwardRef((props: any, ref: any) => {\n const children = React.createElement(component.default || EmptyRoute, {\n ...props,\n ref,\n });\n return {children};\n });\n\n if (__DEV__) {\n Wrapped.displayName = `ErrorBoundary(${value.contextKey})`;\n }\n\n return {\n default: Wrapped,\n SuspenseFallback,\n };\n }\n if (process.env.NODE_ENV !== 'production') {\n if (\n typeof component.default === 'object' &&\n component.default &&\n Object.keys(component.default).length === 0\n ) {\n return { default: EmptyRoute, SuspenseFallback };\n }\n }\n\n return { default: component.default, SuspenseFallback };\n}\n\nfunction fromLoadedRoute(value: RouteNode, res: LoadedRoute) {\n if (!(res instanceof Promise)) {\n return fromImport(value, res);\n }\n\n return res.then(fromImport.bind(null, value));\n}\n\n// TODO: Maybe there's a more React-y way to do this?\n// Without this store, the process enters a recursive loop.\nconst qualifiedStore = new WeakMap>();\n\n/** Wrap the component with various enhancements and add access to child routes. */\nexport function getQualifiedRouteComponent(value: RouteNode) {\n if (qualifiedStore.has(value)) {\n return qualifiedStore.get(value)!;\n }\n\n let ScreenComponent:\n | React.ForwardRefExoticComponent>\n | React.ComponentType<{ segment?: string }>;\n\n let LayoutSuspenseFallback: React.ComponentType | undefined;\n\n // TODO: This ensures sync doesn't use React.lazy, but it's not ideal.\n if (EXPO_ROUTER_IMPORT_MODE === 'lazy') {\n ScreenComponent = React.lazy(async () => {\n const res = value.loadRoute();\n return fromLoadedRoute(value, res) as Promise<{\n default: React.ComponentType;\n }>;\n });\n\n if (__DEV__) {\n ScreenComponent.displayName = `AsyncRoute(${value.route})`;\n }\n } else {\n const res = value.loadRoute();\n const result = fromImport(value, res);\n ScreenComponent = result.default!;\n LayoutSuspenseFallback = value.type === 'layout' ? result.SuspenseFallback : undefined;\n }\n const WrappedScreenComponent: typeof ScreenComponent = (props: object) => {\n useColorSchemeChangesIfNeeded();\n return ;\n };\n function BaseRoute({\n // Remove these React Navigation props to\n // enforce usage of expo-router hooks (where the query params are correct).\n route,\n navigation,\n\n // Pass all other props to the component\n ...props\n }: {\n route?: RouteProp;\n navigation: Omit<\n NavigationProp<\n ParamListBase,\n string,\n undefined,\n NavigationState,\n object,\n NativeStackNavigationEventMap | BottomTabNavigationEventMap\n >,\n 'getState'\n > & {\n getState(): NavigationState | undefined;\n };\n }) {\n const stateForPath = useStateForPath();\n const isFocused = navigation.isFocused();\n const store = useExpoRouterStore();\n const InheritedSuspenseFallback = use(SuspenseFallbackContext);\n\n const ResolvedSuspenseFallback =\n EXPO_ROUTER_IMPORT_MODE === 'lazy'\n ? DefaultSuspenseFallback\n : (LayoutSuspenseFallback ?? InheritedSuspenseFallback ?? DefaultSuspenseFallback);\n const providedSuspenseFallback =\n value.type === 'layout'\n ? (LayoutSuspenseFallback ?? InheritedSuspenseFallback)\n : InheritedSuspenseFallback;\n\n if (isFocused) {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }\n\n useEffect(\n () =>\n navigation.addListener('focus', () => {\n const state = navigation.getState();\n const isLeaf = !(state && 'state' in state.routes[state.index]!);\n // Because setFocusedState caches the route info, this call will only trigger rerenders\n // if the component itself didnât rerender and the route info changed.\n // Otherwise, the update from the `if` above will handle it,\n // and this wonât cause a redundant second update.\n if (isLeaf && stateForPath) store.setFocusedState(stateForPath);\n }),\n [navigation]\n );\n\n useEffect(() => {\n return navigation.addListener('transitionEnd', (e) => {\n if (!e?.data?.closing) {\n // When navigating to a screen, remove the no animation param to re-enable animations\n // Otherwise the navigation back would also have no animation\n if (hasParam(route?.params, INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME)) {\n navigation.replaceParams(\n removeParams(route?.params, [INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME])\n );\n }\n }\n });\n }, [navigation]);\n\n const isRouteType = value.type === 'route';\n const hasRouteKey = !!route?.key;\n\n return (\n \n \n {unstable_navigationEvents.isEnabled() && isRouteType && hasRouteKey && (\n \n )}\n \n \n \n }>\n \n \n \n \n \n );\n }\n\n if (__DEV__) {\n BaseRoute.displayName = `Route(${value.route})`;\n }\n\n qualifiedStore.set(value, BaseRoute);\n return BaseRoute;\n}\n\nfunction AnalyticsListeners({\n navigation,\n screenId,\n}: {\n navigation: EventConsumer & {\n isFocused(): boolean;\n };\n screenId: string;\n}) {\n const isFirstRenderRef = React.useRef(true);\n const hasBlurredRef = React.useRef(true);\n const routeInfo = useCurrentRouteInfo();\n\n if (isFirstRenderRef.current) {\n isFirstRenderRef.current = false;\n if (routeInfo) {\n unstable_navigationEvents.emit('pageWillRender', {\n pathname: routeInfo.pathname,\n params: routeInfo.params,\n screenId,\n });\n }\n }\n\n useEffect(() => {\n if (routeInfo) {\n return () => {\n unstable_navigationEvents.emit('pageRemoved', {\n pathname: routeInfo.pathname,\n params: routeInfo.params,\n screenId,\n });\n };\n }\n return () => {};\n }, [routeInfo?.params, routeInfo?.pathname, screenId]);\n\n const isFocused = navigation.isFocused();\n\n if (isFocused && routeInfo) {\n unstable_navigationEvents.emit('pageFocused', {\n pathname: routeInfo.pathname,\n params: routeInfo.params,\n screenId,\n });\n hasBlurredRef.current = false;\n }\n\n useEffect(() => {\n if (routeInfo) {\n const cleanFocus = navigation.addListener('focus', () => {\n // If the screen was not blurred, don't emit focused again\n // hasBlurredRef will be false when the screen was initially focused\n if (hasBlurredRef.current) {\n unstable_navigationEvents.emit('pageFocused', {\n pathname: routeInfo.pathname,\n params: routeInfo.params,\n screenId,\n });\n hasBlurredRef.current = false;\n }\n });\n const cleanBlur = navigation.addListener('blur', () => {\n unstable_navigationEvents.emit('pageBlurred', {\n pathname: routeInfo.pathname,\n params: routeInfo.params,\n screenId,\n });\n hasBlurredRef.current = true;\n });\n return () => {\n cleanFocus();\n cleanBlur();\n };\n }\n return () => {};\n }, [navigation, routeInfo?.pathname, routeInfo?.params, screenId]);\n\n return null;\n}\n\nexport function screenOptionsFactory(\n route: RouteNode,\n options?: ScreenProps['options']\n): ScreenProps['options'] {\n return (args) => {\n // Only eager load generated components\n const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null;\n const staticResult = typeof staticOptions === 'function' ? staticOptions(args) : staticOptions;\n const dynamicResult = typeof options === 'function' ? options?.(args) : options;\n const output = {\n ...staticResult,\n ...dynamicResult,\n };\n\n // Prevent generated screens from showing up in the tab bar.\n if (route.internal) {\n output.tabBarItemStyle = { display: 'none' };\n output.tabBarButton = () => null;\n // TODO: React Navigation doesn't provide a way to prevent rendering the drawer item.\n output.drawerItemStyle = { height: 0, display: 'none' };\n }\n\n return output;\n };\n}\n\nexport function routeToScreen(\n route: RouteNode,\n { options, getId, ...props }: Partial = {}\n) {\n return (\n getQualifiedRouteComponent(route)}\n />\n );\n}\n\nexport function getSingularId(name: string, options: Record = {}) {\n return name\n .split('/')\n .map((segment) => {\n if (segment.startsWith('[...')) {\n return options.params?.[segment.slice(4, -1)]?.join('/') || segment;\n } else if (segment.startsWith('[') && segment.endsWith(']')) {\n return options.params?.[segment.slice(1, -1)] || segment;\n } else {\n return segment;\n }\n })\n .join('/');\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/src/exports.ts b/packages/expo-router/src/exports.ts
index a2be7815493b8c..f2f399f0ad71d6 100644
--- a/packages/expo-router/src/exports.ts
+++ b/packages/expo-router/src/exports.ts
@@ -67,6 +67,14 @@ export {
} from './primitives';
export { unstable_navigationEvents } from './navigationEvents';
+export type {
+ PageWillRender,
+ PageFocusedEvent,
+ PageBlurredEvent,
+ PageRemoved,
+ ActionDispatchedEvent,
+ AnalyticsEvent,
+} from './navigationEvents';
/**
* @deprecated Use `import { Tabs } from 'expo-router/js-tabs'` instead.
diff --git a/packages/expo-router/src/global-state/store.ts b/packages/expo-router/src/global-state/store.ts
index 4d9483c52cbd45..1c270a9dfc36cc 100644
--- a/packages/expo-router/src/global-state/store.ts
+++ b/packages/expo-router/src/global-state/store.ts
@@ -11,6 +11,7 @@ import type {
import type { RouteNode } from '../Route';
import type { ExpoLinkingOptions } from '../getLinkingConfig';
import { resolveHref, resolveHrefStringWithSegments } from '../link/href';
+import { handleNavigationOnReady } from '../navigationEvents/navigation';
import type { NavigationContainerRefWithCurrent } from '../react-navigation/native';
import type { RequireContext, Href } from '../types';
import * as SplashScreen from '../views/Splash';
@@ -84,6 +85,7 @@ export const store = {
storeRef.current.routeInfo = routeInfo;
},
onReady() {
+ handleNavigationOnReady();
if (!hasAttemptedToHideSplash) {
setHasAttemptedToHideSplash(true);
// NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.
diff --git a/packages/expo-router/src/navigationEvents/index.ts b/packages/expo-router/src/navigationEvents/index.ts
index 673626732e2e6b..528ed4ce9e1df7 100644
--- a/packages/expo-router/src/navigationEvents/index.ts
+++ b/packages/expo-router/src/navigationEvents/index.ts
@@ -1,14 +1,32 @@
-import type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved } from './types';
+import type {
+ PageWillRender,
+ PageFocusedEvent,
+ PageBlurredEvent,
+ PageRemoved,
+ ActionDispatchedEvent,
+} from './types';
-export type { PageWillRender, PageFocusedEvent, PageBlurredEvent, PageRemoved } from './types';
+export type {
+ PageWillRender,
+ PageFocusedEvent,
+ PageBlurredEvent,
+ PageRemoved,
+ ActionDispatchedEvent,
+} from './types';
-export type AnalyticsEvent = PageWillRender | PageFocusedEvent | PageBlurredEvent | PageRemoved;
+export type AnalyticsEvent =
+ | PageWillRender
+ | PageFocusedEvent
+ | PageBlurredEvent
+ | PageRemoved
+ | ActionDispatchedEvent;
const availableEvents: AnalyticsEvent['type'][] = [
'pageWillRender',
'pageFocused',
'pageBlurred',
'pageRemoved',
+ 'actionDispatched',
];
type EventTypeName = AnalyticsEvent['type'];
@@ -48,10 +66,6 @@ export function emit(type: EventType, event: Pa
let enabled = false;
-let currentPathname: string | undefined = undefined;
-let currentParams: Record | undefined = undefined;
-let currentPathnameListener: ReturnType | undefined = undefined;
-
export const unstable_navigationEvents = {
addListener,
emit,
@@ -61,44 +75,4 @@ export const unstable_navigationEvents = {
isEnabled: () => {
return enabled;
},
- saveCurrentPathname: () => {
- if (!enabled || currentPathnameListener) return;
- currentPathnameListener = addListener('pageFocused', (event) => {
- currentPathname = event.pathname;
- currentParams = event.params;
- });
- },
- get currentPathname() {
- return currentPathname;
- },
- get currentParams() {
- return currentParams;
- },
};
-
-if (globalThis.expo) {
- globalThis.expo.router = globalThis.expo.router || {};
-
- if (!('navigationEvents' in globalThis.expo.router)) {
- Object.defineProperties(globalThis.expo.router, {
- navigationEvents: {
- get() {
- return unstable_navigationEvents;
- },
- enumerable: true,
- },
- currentPathname: {
- get() {
- return currentPathname;
- },
- enumerable: true,
- },
- currentParams: {
- get() {
- return currentParams;
- },
- enumerable: true,
- },
- });
- }
-}
diff --git a/packages/expo-router/src/navigationEvents/navigation.ts b/packages/expo-router/src/navigationEvents/navigation.ts
new file mode 100644
index 00000000000000..cefd9e681cfedf
--- /dev/null
+++ b/packages/expo-router/src/navigationEvents/navigation.ts
@@ -0,0 +1,18 @@
+import { emit } from '.';
+import { storeRef } from '../global-state/store';
+
+let unsubscribe: (() => void) | undefined;
+
+export function handleNavigationOnReady() {
+ if (unsubscribe) unsubscribe();
+ unsubscribe = storeRef.current.navigationRef.addListener('__unsafe_action__', (e) => {
+ if (!e.data.noop && storeRef.current.state) {
+ const action = e.data.action;
+ emit('actionDispatched', {
+ actionType: action.type,
+ payload: action.payload,
+ state: storeRef.current.state,
+ });
+ }
+ });
+}
diff --git a/packages/expo-router/src/navigationEvents/types.ts b/packages/expo-router/src/navigationEvents/types.ts
index 227f7517b9ec14..838016d74507a2 100644
--- a/packages/expo-router/src/navigationEvents/types.ts
+++ b/packages/expo-router/src/navigationEvents/types.ts
@@ -1,6 +1,9 @@
+import type { ReactNavigationState } from '../global-state/types';
+import type { NavigationAction } from '../react-navigation';
+
export interface BasePageEvent {
pathname: string;
- params: Record;
+ params: Record;
screenId: string;
}
@@ -24,3 +27,11 @@ export interface PageBlurredEvent extends BasePageEvent {
export interface PageRemoved extends BasePageEvent {
type: 'pageRemoved';
}
+
+export interface ActionDispatchedEvent {
+ type: 'actionDispatched';
+ /** The action type from the dispatched NavigationAction (e.g. `NAVIGATE`). */
+ actionType: NavigationAction['type'];
+ payload: NavigationAction['payload'];
+ state: ReactNavigationState;
+}
diff --git a/packages/expo-router/src/useScreens.tsx b/packages/expo-router/src/useScreens.tsx
index 54b17432749448..a57c2574b14a55 100644
--- a/packages/expo-router/src/useScreens.tsx
+++ b/packages/expo-router/src/useScreens.tsx
@@ -1,16 +1,16 @@
'use client';
-import React, { use, useEffect, useMemo } from 'react';
+import React, { use, useEffect } from 'react';
import type { LoadedRoute, RouteNode } from './Route';
import { SuspenseFallbackContext, Route, sortRoutesWithInitial, useRouteNode } from './Route';
import { useExpoRouterStore } from './global-state/storeContext';
import { useColorSchemeChangesIfNeeded } from './global-state/utils';
+import { useCurrentRouteInfo } from './hooks';
import EXPO_ROUTER_IMPORT_MODE from './import-mode';
import { ZoomTransitionEnabler } from './link/zoom/ZoomTransitionEnabler';
import { ZoomTransitionTargetContextProvider } from './link/zoom/zoom-transition-context-providers';
import { unstable_navigationEvents } from './navigationEvents';
-import { generateStringUrlForState, getPathAndParamsFromStringUrl } from './navigationEvents/utils';
import {
hasParam,
INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME,
@@ -406,51 +406,54 @@ function AnalyticsListeners({
};
screenId: string;
}) {
- const stateForPath = useStateForPath();
const isFirstRenderRef = React.useRef(true);
const hasBlurredRef = React.useRef(true);
- const stringUrl = useMemo(() => generateStringUrlForState(stateForPath), [stateForPath]);
+ const routeInfo = useCurrentRouteInfo();
if (isFirstRenderRef.current) {
isFirstRenderRef.current = false;
- if (stringUrl) {
+ if (routeInfo) {
unstable_navigationEvents.emit('pageWillRender', {
- ...getPathAndParamsFromStringUrl(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
}
}
useEffect(() => {
- if (stringUrl) {
+ if (routeInfo) {
return () => {
unstable_navigationEvents.emit('pageRemoved', {
- ...getPathAndParamsFromStringUrl(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
};
}
return () => {};
- }, [stringUrl, screenId]);
+ }, [routeInfo?.params, routeInfo?.pathname, screenId]);
const isFocused = navigation.isFocused();
- if (isFocused && stringUrl) {
+ if (isFocused && routeInfo) {
unstable_navigationEvents.emit('pageFocused', {
- ...getPathAndParamsFromStringUrl(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
hasBlurredRef.current = false;
}
useEffect(() => {
- if (stringUrl) {
+ if (routeInfo) {
const cleanFocus = navigation.addListener('focus', () => {
// If the screen was not blurred, don't emit focused again
// hasBlurredRef will be false when the screen was initially focused
if (hasBlurredRef.current) {
unstable_navigationEvents.emit('pageFocused', {
- ...getPathAndParamsFromStringUrl(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
hasBlurredRef.current = false;
@@ -458,7 +461,8 @@ function AnalyticsListeners({
});
const cleanBlur = navigation.addListener('blur', () => {
unstable_navigationEvents.emit('pageBlurred', {
- ...getPathAndParamsFromStringUrl(stringUrl),
+ pathname: routeInfo.pathname,
+ params: routeInfo.params,
screenId,
});
hasBlurredRef.current = true;
@@ -469,7 +473,7 @@ function AnalyticsListeners({
};
}
return () => {};
- }, [navigation, stringUrl, screenId]);
+ }, [navigation, routeInfo?.pathname, routeInfo?.params, screenId]);
return null;
}
From 4416d9066d534063a2110fb9c631972b6590fc26 Mon Sep 17 00:00:00 2001
From: Christian Falch <875252+chrfalch@users.noreply.github.com>
Date: Tue, 12 May 2026 14:30:49 +0200
Subject: [PATCH 05/11] fix(precompile) add fallback to source for missing
framework slice (#45664)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Why
We were seeing tvOS builds try to link an iOS-only precompiled Expo
XCFramework.
With `EXPO_USE_PRECOMPILED_MODULES=1`, CocoaPods treated the prebuilt
XApplication.tar.gz as available just because the tarball existed. It
did not check whether EXApplication.xcframework/Info.plist contained a
tvos slice.
So for a tvOS target, CocoaPods vendored the iOS-only
EXApplication.xcframework, and the Xcode build later failed at link time
with errors like:
```
ld: framework 'EXApplication' not found
```
The fix makes prebuilt resolution platform-aware, so tvOS falls back to
source when the prebuilt XCFramework only contains iOS slices.
# How
Instead of using a prebuilt XCFramework just because its tarball exists,
precompiled_modules.rb now reads the XCFramework Info.plist and checks
AvailableLibraries[*].SupportedPlatform against the current CocoaPods
target platform. osx is normalized to macos.
AutolinkingManager passes the target platform into PrecompiledModules,
so tvOS/macOS/etc. fall back to source when a prebuilt only contains iOS
slices. We also cached the slice checks to avoid repeatedly unpacking
tarballs.
# Test Plan
â
Ran temp tvOS project with `EXPO_USE_PRECOMPILED_MODULES=1` and
verified the error. Applied the fix and reran.
â
Verified BareExpo
â
Verified Minimal Tester
# 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).
---
.../expo-modules-autolinking/CHANGELOG.md | 2 +
.../scripts/ios/autolinking_manager.rb | 12 ++-
.../scripts/ios/precompiled_modules.rb | 94 ++++++++++++++++++-
3 files changed, 100 insertions(+), 8 deletions(-)
diff --git a/packages/expo-modules-autolinking/CHANGELOG.md b/packages/expo-modules-autolinking/CHANGELOG.md
index 56500cde83dbe6..d78cdbbd0301c9 100644
--- a/packages/expo-modules-autolinking/CHANGELOG.md
+++ b/packages/expo-modules-autolinking/CHANGELOG.md
@@ -8,6 +8,8 @@
### đ Bug fixes
+[iOS] Added fallback to source for missing framework slice. ([#45664](https://github.com/expo/expo/pull/45664) by [@chrfalch](https://github.com/chrfalch))
+
### đĄ Others
## 56.0.4 â 2026-05-11
diff --git a/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb b/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb
index 20a79ddbee0814..716e45f0c7bb9b 100644
--- a/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb
+++ b/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb
@@ -22,16 +22,18 @@ class AutolinkingManager
validate_target_definition()
- # Clear stale CocoaPods download cache for precompiled pods.
- Expo::PrecompiledModules.clear_cocoapods_cache
-
resolve_result = resolve()
Expo::PackagesConfig.instance.coreFeatures = resolve_result['coreFeatures']
- # Pass buildFromSource configuration to PrecompiledModules
configuration = resolve_result['configuration'] || {}
- Expo::PrecompiledModules.build_from_source = configuration['buildFromSource'] || []
+ Expo::PrecompiledModules.configure(
+ target_platform: @target_definition.platform,
+ build_from_source: configuration['buildFromSource'] || []
+ )
+
+ # Clear stale CocoaPods download cache for precompiled pods.
+ Expo::PrecompiledModules.clear_cocoapods_cache
@packages = resolve_result['modules'].map { |json_package| Package.new(json_package) }
@extraPods = resolve_result['extraDependencies']
diff --git a/packages/expo-modules-autolinking/scripts/ios/precompiled_modules.rb b/packages/expo-modules-autolinking/scripts/ios/precompiled_modules.rb
index 16ebc15f5a8ec3..2dc8e68f67bccd 100644
--- a/packages/expo-modules-autolinking/scripts/ios/precompiled_modules.rb
+++ b/packages/expo-modules-autolinking/scripts/ios/precompiled_modules.rb
@@ -29,7 +29,9 @@
require 'fileutils'
require 'json'
require 'net/http'
+require 'open3'
require 'set'
+require 'tempfile'
require 'uri'
module Expo
@@ -76,6 +78,8 @@ module PrecompiledModules
@framework_owner_map = nil # Hash: framework_name -> owning_pod_name
@failed_remote_downloads = Set.new
@warned_no_prebuilt_react = false
+ @target_platform = nil
+ @xcframework_slice_cache = nil
class << self
# Returns the build flavor (debug/release) for precompiled modules.
@@ -113,13 +117,26 @@ def enabled?
false
end
- # Sets the list of package name patterns that should be built from source
- # instead of using precompiled xcframeworks. Patterns are treated as regexes
- # (e.g., ".*" for all, "expo-audio" for exact match, "expo-.*" for prefix).
+ def configure(target_platform: nil, build_from_source: nil)
+ self.target_platform = target_platform unless target_platform.nil?
+ self.build_from_source = build_from_source unless build_from_source.nil?
+ end
+
def build_from_source=(patterns)
@build_from_source_patterns = (patterns || []).map { |p| Regexp.new("^#{p}$") }
end
+ def target_platform=(platform)
+ normalized = normalize_xcframework_platform(platform)
+ return if @target_platform == normalized
+
+ @target_platform = normalized
+ @all_bundled_frameworks = nil
+ @claimed_vendored_frameworks = nil
+ @framework_owner_map = nil
+ @xcframework_slice_cache = nil
+ end
+
# Checks if a pod is configured to be built from source via buildFromSource.
# Matches against both the pod name and the npm package name.
def build_from_source?(pod_name)
@@ -1785,6 +1802,7 @@ def resolve_own_prebuilt_info(pod_name)
product_name = pod_info[:product_name] || pod_name
tarball = resolve_prebuilt_tarball(pod_info, product_name, build_flavor, pod_name)
return { available: false, reason: :missing_tarball, path: tarball } unless File.exist?(tarball)
+ return { available: false, reason: :missing_platform_slice, path: tarball } unless xcframework_supports_target_platform?(tarball)
{ available: true, resolved: [pod_info, product_name, tarball] }
end
@@ -1842,6 +1860,74 @@ def resolve_prebuilt_tarball(pod_info, product_name, flavor, pod_name = nil)
remote_tarball
end
+ def normalize_xcframework_platform(platform)
+ name = platform.respond_to?(:name) ? platform.name : platform
+ name = name.string_name if name.respond_to?(:string_name)
+ normalized = name.to_s.downcase
+ normalized == 'osx' ? 'macos' : normalized
+ end
+
+ def xcframework_supports_target_platform?(path)
+ return true unless @target_platform
+
+ @xcframework_slice_cache ||= {}
+ cache_key = [path, @target_platform]
+ return @xcframework_slice_cache[cache_key] if @xcframework_slice_cache.key?(cache_key)
+
+ @xcframework_slice_cache[cache_key] = begin
+ info_plists = File.directory?(path) ? [read_plist(File.join(path, 'Info.plist'))] : read_xcframework_info_plists_from_tarball(path)
+ info_plists.any? && info_plists.all? { |info_plist| info_plist_supports_target_platform?(info_plist) }
+ end
+ end
+
+ def info_plist_supports_target_platform?(info_plist)
+ return false unless info_plist
+
+ available_libraries = info_plist['AvailableLibraries']
+ return false unless available_libraries.is_a?(Array)
+
+ available_libraries.any? do |library|
+ normalize_xcframework_platform(library['SupportedPlatform']) == @target_platform
+ end
+ end
+
+ def read_xcframework_info_plists_from_tarball(tarball)
+ entries_output, status = Open3.capture2e('tar', 'tzf', tarball)
+ unless status.success?
+ Pod::UI.warn "[Expo-precompiled] Failed to inspect #{File.basename(tarball)}: #{entries_output.strip}"
+ return []
+ end
+
+ entries = entries_output.lines.map(&:strip)
+ plist_entries = entries.select { |entry| entry.end_with?('.xcframework/Info.plist') }
+ Pod::UI.warn "[Expo-precompiled] No XCFramework Info.plist found in #{File.basename(tarball)}" if plist_entries.empty?
+
+ plist_entries.filter_map do |entry|
+ plist_data, plist_status = Open3.capture2e('tar', 'xOzf', tarball, entry)
+ unless plist_status.success?
+ Pod::UI.warn "[Expo-precompiled] Failed to extract #{entry} from #{File.basename(tarball)}: #{plist_data.strip}"
+ next
+ end
+
+ Tempfile.create(['expo-xcframework-info', '.plist']) do |file|
+ file.binmode
+ file.write(plist_data)
+ file.flush
+ read_plist(file.path)
+ end
+ end
+ rescue StandardError => e
+ Pod::UI.warn "[Expo-precompiled] Failed to inspect #{File.basename(tarball)}: #{e.message}"
+ []
+ end
+
+ def read_plist(path)
+ Xcodeproj::Plist.read_from_path(path)
+ rescue StandardError => e
+ Pod::UI.warn "[Expo-precompiled] Failed to read #{File.basename(path)}: #{e.message}"
+ nil
+ end
+
def failed_remote_downloads
@failed_remote_downloads ||= Set.new
end
@@ -2120,6 +2206,8 @@ def format_prebuilt_unavailable_reason(info)
'prebuilt config not found'
when :missing_tarball
'prebuilt tarball not found'
+ when :missing_platform_slice
+ "prebuilt xcframework does not contain a slice for #{@target_platform}"
when :dependency_unavailable
reason = format_prebuilt_unavailable_reason(reason: info[:dependency_reason], path: info[:dependency_path])
"dependency #{info[:dependency]} is not using prebuilt: #{reason}"
From 1a89c487ab1a7e677120f008d9d94ebe2539d9c9 Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Tue, 12 May 2026 14:43:28 +0200
Subject: [PATCH 06/11] [docs] fix expo-ui drop-in replacement listing (#45672)
# Why
The Expo UI overview page listed only `DateTimePicker` under "Drop-in replacements",
while the dedicated `drop-in-replacements` page listed the full set. Two lists drifted
out of sync.
# How
# Test Plan
# Checklist
- [ ] 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)
---
docs/pages/versions/unversioned/sdk/ui/index.mdx | 4 +---
docs/pages/versions/v55.0.0/sdk/ui/index.mdx | 4 +---
docs/pages/versions/v56.0.0/sdk/ui/index.mdx | 4 +---
3 files changed, 3 insertions(+), 9 deletions(-)
diff --git a/docs/pages/versions/unversioned/sdk/ui/index.mdx b/docs/pages/versions/unversioned/sdk/ui/index.mdx
index 54e1277061ae11..ff7724b0e7a6ef 100644
--- a/docs/pages/versions/unversioned/sdk/ui/index.mdx
+++ b/docs/pages/versions/unversioned/sdk/ui/index.mdx
@@ -20,6 +20,4 @@ Components are available for the following platforms:
## Drop-in replacements
-API-compatible replacements for popular React Native community libraries:
-
-- **[DateTimePicker](drop-in-replacements/datetimepicker)**: Compatible with `@react-native-community/datetimepicker`
+See **[Drop-in replacements](drop-in-replacements)** for API-compatible replacements for popular React Native community libraries.
diff --git a/docs/pages/versions/v55.0.0/sdk/ui/index.mdx b/docs/pages/versions/v55.0.0/sdk/ui/index.mdx
index 6fabc06332113b..017e3b5948f0eb 100644
--- a/docs/pages/versions/v55.0.0/sdk/ui/index.mdx
+++ b/docs/pages/versions/v55.0.0/sdk/ui/index.mdx
@@ -19,6 +19,4 @@ Components are available for the following platforms:
## Drop-in replacements
-API-compatible replacements for popular React Native community libraries:
-
-- **[DateTimePicker](drop-in-replacements/datetimepicker)**: Compatible with `@react-native-community/datetimepicker`
+See **[Drop-in replacements](drop-in-replacements)** for API-compatible replacements for popular React Native community libraries.
diff --git a/docs/pages/versions/v56.0.0/sdk/ui/index.mdx b/docs/pages/versions/v56.0.0/sdk/ui/index.mdx
index 8b2b1c99f49b47..0ae8e85ef86c83 100644
--- a/docs/pages/versions/v56.0.0/sdk/ui/index.mdx
+++ b/docs/pages/versions/v56.0.0/sdk/ui/index.mdx
@@ -20,6 +20,4 @@ Components are available for the following platforms:
## Drop-in replacements
-API-compatible replacements for popular React Native community libraries:
-
-- **[DateTimePicker](drop-in-replacements/datetimepicker)**: Compatible with `@react-native-community/datetimepicker`
+See **[Drop-in replacements](drop-in-replacements)** for API-compatible replacements for popular React Native community libraries.
From 8ff528f5aba117c0f1a11dc01cda5416d0a5342d Mon Sep 17 00:00:00 2001
From: HubertBer <115428831+HubertBer@users.noreply.github.com>
Date: Tue, 12 May 2026 14:48:37 +0200
Subject: [PATCH 07/11] [expo-type-information] add readme, update cli
description (#45663)
---
packages/expo-type-information/README.md | 32 ++++++++++++++++++-
packages/expo-type-information/build/cli.js | 2 +-
.../expo-type-information/build/cli.js.map | 2 +-
packages/expo-type-information/src/cli.ts | 2 +-
4 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/packages/expo-type-information/README.md b/packages/expo-type-information/README.md
index a3f559f0664d1e..4965bfe7e899eb 100644
--- a/packages/expo-type-information/README.md
+++ b/packages/expo-type-information/README.md
@@ -1 +1,31 @@
-# expo-type-information
+# expo-type-information
+
+> [!WARNING]
+> This package only works on macOS.
+
+This package provides a CLI tool and exports functions which can:
+
+- Parse Swift expo modules and retrieve type information from them
+- Emit typescript code (types, wrapper functions, mocks)
+
+## Installation
+
+First, add the package to your npm dependencies:
+
+```
+npm install expo-type-information
+```
+
+To use this package, you need to have `sourcekitten` installed. You can install it using Homebrew:
+
+```
+brew install sourcekitten
+```
+
+## Usage
+
+With `sourcekitten` installed you can run the CLI tool and see the available commands:
+
+```
+npx expo-type-information
+```
diff --git a/packages/expo-type-information/build/cli.js b/packages/expo-type-information/build/cli.js
index 7d9042d8a31d1e..477874a481c0de 100644
--- a/packages/expo-type-information/build/cli.js
+++ b/packages/expo-type-information/build/cli.js
@@ -19,7 +19,7 @@ async function main(args) {
cli
.name('expo-type-information')
.version(require('../package.json').version)
- .description('CLI commands for retrieving type information from native files.');
+ .description('Retrieve type information from Swift Expo modules to generate TypeScript.');
(0, moduleInterfaceCommand_1.moduleInterfaceCommand)(cli);
(0, inlineModulesInterfaceCommand_1.inlineModulesInterfaceCommand)(cli);
(0, shortModuleInterfaceCommand_1.shortModuleInterfaceCommand)(cli);
diff --git a/packages/expo-type-information/build/cli.js.map b/packages/expo-type-information/build/cli.js.map
index e17661d964aa89..6ef44c11c2eb74 100644
--- a/packages/expo-type-information/build/cli.js.map
+++ b/packages/expo-type-information/build/cli.js.map
@@ -1 +1 @@
-{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AAEpC,0DAAkE;AAClE,0FAAgF;AAChF,wFAAqF;AACrF,sFAAmF;AACnF,kFAA+E;AAC/E,4FAAyF;AACzF,8EAA2E;AAC3E,wFAAqF;AACrF,8EAA2E;AAE3E,KAAK,UAAU,IAAI,CAAC,IAAc;IAChC,IAAI,CAAC,IAAA,sCAAuB,GAAE,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;QACvF,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,mBAAO,EAAE,CAAC;IAC1B,GAAG;SACA,IAAI,CAAC,uBAAuB,CAAC;SAC7B,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;SAC3C,WAAW,CAAC,iEAAiE,CAAC,CAAC;IAElF,IAAA,+CAAsB,EAAC,GAAG,CAAC,CAAC;IAC5B,IAAA,6DAA6B,EAAC,GAAG,CAAC,CAAC;IACnC,IAAA,yDAA2B,EAAC,GAAG,CAAC,CAAC;IACjC,IAAA,yDAA2B,EAAC,GAAG,CAAC,CAAC;IAEjC,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;IAC7F,IAAA,+CAAsB,EAAC,aAAa,CAAC,CAAC;IACtC,IAAA,uDAA0B,EAAC,aAAa,CAAC,CAAC;IAC1C,IAAA,mDAAwB,EAAC,aAAa,CAAC,CAAC;IACxC,IAAA,oDAAqB,EAAC,aAAa,CAAC,CAAC;IAErC,MAAM,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
\ No newline at end of file
+{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AAEpC,0DAAkE;AAClE,0FAAgF;AAChF,wFAAqF;AACrF,sFAAmF;AACnF,kFAA+E;AAC/E,4FAAyF;AACzF,8EAA2E;AAC3E,wFAAqF;AACrF,8EAA2E;AAE3E,KAAK,UAAU,IAAI,CAAC,IAAc;IAChC,IAAI,CAAC,IAAA,sCAAuB,GAAE,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;QACvF,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,mBAAO,EAAE,CAAC;IAC1B,GAAG;SACA,IAAI,CAAC,uBAAuB,CAAC;SAC7B,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;SAC3C,WAAW,CAAC,2EAA2E,CAAC,CAAC;IAE5F,IAAA,+CAAsB,EAAC,GAAG,CAAC,CAAC;IAC5B,IAAA,6DAA6B,EAAC,GAAG,CAAC,CAAC;IACnC,IAAA,yDAA2B,EAAC,GAAG,CAAC,CAAC;IACjC,IAAA,yDAA2B,EAAC,GAAG,CAAC,CAAC;IAEjC,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;IAC7F,IAAA,+CAAsB,EAAC,aAAa,CAAC,CAAC;IACtC,IAAA,uDAA0B,EAAC,aAAa,CAAC,CAAC;IAC1C,IAAA,mDAAwB,EAAC,aAAa,CAAC,CAAC;IACxC,IAAA,oDAAqB,EAAC,aAAa,CAAC,CAAC;IAErC,MAAM,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-type-information/src/cli.ts b/packages/expo-type-information/src/cli.ts
index bd4053df965771..333bde6503b805 100644
--- a/packages/expo-type-information/src/cli.ts
+++ b/packages/expo-type-information/src/cli.ts
@@ -19,7 +19,7 @@ async function main(args: string[]) {
cli
.name('expo-type-information')
.version(require('../package.json').version)
- .description('CLI commands for retrieving type information from native files.');
+ .description('Retrieve type information from Swift Expo modules to generate TypeScript.');
moduleInterfaceCommand(cli);
inlineModulesInterfaceCommand(cli);
From cfae0318e1ff727e12c1e47b0e56a6cc0f21e820 Mon Sep 17 00:00:00 2001
From: Jakub Tkacz <32908614+Ubax@users.noreply.github.com>
Date: Tue, 12 May 2026 14:56:18 +0200
Subject: [PATCH 08/11] [expo-app-metrics][android] remove start and stop
session functions (#45633)
# Why
Turns out that `startSession` and `stopSession` are not needed for
router integration anymore.
# How
1. Remove android native implementation
2. Update types
3. Update module.web
# Test Plan
CI
# Checklist
- [ ] 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)
---
.../modules/appmetrics/AppMetricsModule.kt | 22 -------------------
.../expo-app-metrics/build/module.web.d.ts | 2 --
.../build/module.web.d.ts.map | 2 +-
packages/expo-app-metrics/build/module.web.js | 4 ----
.../expo-app-metrics/build/module.web.js.map | 2 +-
packages/expo-app-metrics/build/types.d.ts | 13 -----------
.../expo-app-metrics/build/types.d.ts.map | 2 +-
packages/expo-app-metrics/build/types.js.map | 2 +-
packages/expo-app-metrics/src/module.web.ts | 4 ----
packages/expo-app-metrics/src/types.ts | 13 -----------
10 files changed, 4 insertions(+), 62 deletions(-)
diff --git a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt
index 9eba8a9804bb5d..8cc97260c0742f 100644
--- a/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt
+++ b/packages/expo-app-metrics/android/src/main/java/expo/modules/appmetrics/AppMetricsModule.kt
@@ -170,28 +170,6 @@ class AppMetricsModule : Module(), UpdatesStateChangeListener {
AsyncFunction("clearStoredEntries") Coroutine { -> sessionManager.clearAllData() }
- Function("startSession") {
- val sessionId = sessionManager.createSessionId()
- val timestamp = TimeUtils.getCurrentTimestampInISOFormat()
- val sessionMetadata = metadata
-
- scope.launch {
- sessionManager.startSessionWithIdAt(
- sessionId = sessionId,
- timestamp = timestamp,
- metadata = sessionMetadata
- )
- }
-
- return@Function sessionId
- }
-
- Function("stopSession") { sessionId: String ->
- scope.launch {
- sessionManager.stopSession(sessionId = sessionId)
- }
- }
-
AsyncFunction("addCustomMetricToSession") Coroutine { metric: JsMetric ->
sessionManager.addMetrics(listOf(metric.toMetric()), sessionId = metric.sessionId)
}
diff --git a/packages/expo-app-metrics/build/module.web.d.ts b/packages/expo-app-metrics/build/module.web.d.ts
index 27eab1950936b8..7f69340b00de2c 100644
--- a/packages/expo-app-metrics/build/module.web.d.ts
+++ b/packages/expo-app-metrics/build/module.web.d.ts
@@ -11,8 +11,6 @@ declare class ExpoAppMetricsModule extends NativeModule implements ExpoAppMetric
getAllSessions(): Promise;
simulateCrashReport(): void;
triggerCrash(): void;
- startSession(metadata?: string): string;
- stopSession(sessionId: string): void;
getMainSession(): Promise;
}
declare const _default: typeof ExpoAppMetricsModule;
diff --git a/packages/expo-app-metrics/build/module.web.d.ts.map b/packages/expo-app-metrics/build/module.web.d.ts.map
index abc89bc923cf30..5f09f5a6cd5bb3 100644
--- a/packages/expo-app-metrics/build/module.web.d.ts.map
+++ b/packages/expo-app-metrics/build/module.web.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"module.web.d.ts","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,MAAM,CAAC;AAEvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEnG,cAAc,SAAS,CAAC;AAExB,cAAM,oBAAqB,SAAQ,YAAa,YAAW,wBAAwB;IACjF,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAGjD,eAAe;IACf,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe;IAC1C,gBAAgB;IAGhB,kBAAkB;IAClB,cAAc;IAGpB,mBAAmB;IACnB,YAAY;IACZ,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM;IAG9B,WAAW,CAAC,SAAS,EAAE,MAAM;IACvB,cAAc;CAGrB;;AAED,wBAAyE"}
\ No newline at end of file
+{"version":3,"file":"module.web.d.ts","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,MAAM,CAAC;AAEvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEnG,cAAc,SAAS,CAAC;AAExB,cAAM,oBAAqB,SAAQ,YAAa,YAAW,wBAAwB;IACjF,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAGjD,eAAe;IACf,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe;IAC1C,gBAAgB;IAGhB,kBAAkB;IAClB,cAAc;IAGpB,mBAAmB;IACnB,YAAY;IACN,cAAc;CAGrB;;AAED,wBAAyE"}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/build/module.web.js b/packages/expo-app-metrics/build/module.web.js
index e3a64647e81134..d4ed0a8265a9ef 100644
--- a/packages/expo-app-metrics/build/module.web.js
+++ b/packages/expo-app-metrics/build/module.web.js
@@ -16,10 +16,6 @@ class ExpoAppMetricsModule extends NativeModule {
}
simulateCrashReport() { }
triggerCrash() { }
- startSession(metadata) {
- return '';
- }
- stopSession(sessionId) { }
async getMainSession() {
return null;
}
diff --git a/packages/expo-app-metrics/build/module.web.js.map b/packages/expo-app-metrics/build/module.web.js.map
index 835d197cc84e78..957831704bc838 100644
--- a/packages/expo-app-metrics/build/module.web.js.map
+++ b/packages/expo-app-metrics/build/module.web.js.map
@@ -1 +1 @@
-{"version":3,"file":"module.web.js","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAIvD,cAAc,SAAS,CAAC;AAExB,MAAM,oBAAqB,SAAQ,YAAY;IAC7C,wBAAwB,CAAC,MAAc;QACrC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,CAAC,eAAe,KAAI,CAAC;IAC1B,KAAK,CAAC,eAAe,CAAC,UAA6B,IAAG,CAAC;IACvD,QAAQ,CAAC,IAAY,EAAE,OAAyB,IAAG,CAAC;IACpD,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,CAAC,kBAAkB,KAAI,CAAC;IAC7B,KAAK,CAAC,cAAc;QAClB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,mBAAmB,KAAI,CAAC;IACxB,YAAY,KAAI,CAAC;IACjB,YAAY,CAAC,QAAiB;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,WAAW,CAAC,SAAiB,IAAG,CAAC;IACjC,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC","sourcesContent":["import { NativeModule, registerWebModule } from 'expo';\n\nimport type { ExpoAppMetricsModuleType, LogEventOptions, Metric, MetricAttributes } from './types';\n\nexport * from './types';\n\nclass ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleType {\n addCustomMetricToSession(metric: Metric): Promise {\n throw new Error('Method not implemented.');\n }\n async markFirstRender() {}\n async markInteractive(attributes?: MetricAttributes) {}\n logEvent(name: string, options?: LogEventOptions) {}\n async getStoredEntries() {\n return [];\n }\n async clearStoredEntries() {}\n async getAllSessions() {\n return [];\n }\n simulateCrashReport() {}\n triggerCrash() {}\n startSession(metadata?: string) {\n return '';\n }\n stopSession(sessionId: string) {}\n async getMainSession() {\n return null;\n }\n}\n\nexport default registerWebModule(ExpoAppMetricsModule, 'ExpoAppMetrics');\n"]}
\ No newline at end of file
+{"version":3,"file":"module.web.js","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAIvD,cAAc,SAAS,CAAC;AAExB,MAAM,oBAAqB,SAAQ,YAAY;IAC7C,wBAAwB,CAAC,MAAc;QACrC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,CAAC,eAAe,KAAI,CAAC;IAC1B,KAAK,CAAC,eAAe,CAAC,UAA6B,IAAG,CAAC;IACvD,QAAQ,CAAC,IAAY,EAAE,OAAyB,IAAG,CAAC;IACpD,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,CAAC,kBAAkB,KAAI,CAAC;IAC7B,KAAK,CAAC,cAAc;QAClB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,mBAAmB,KAAI,CAAC;IACxB,YAAY,KAAI,CAAC;IACjB,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC","sourcesContent":["import { NativeModule, registerWebModule } from 'expo';\n\nimport type { ExpoAppMetricsModuleType, LogEventOptions, Metric, MetricAttributes } from './types';\n\nexport * from './types';\n\nclass ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleType {\n addCustomMetricToSession(metric: Metric): Promise {\n throw new Error('Method not implemented.');\n }\n async markFirstRender() {}\n async markInteractive(attributes?: MetricAttributes) {}\n logEvent(name: string, options?: LogEventOptions) {}\n async getStoredEntries() {\n return [];\n }\n async clearStoredEntries() {}\n async getAllSessions() {\n return [];\n }\n simulateCrashReport() {}\n triggerCrash() {}\n async getMainSession() {\n return null;\n }\n}\n\nexport default registerWebModule(ExpoAppMetricsModule, 'ExpoAppMetrics');\n"]}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/build/types.d.ts b/packages/expo-app-metrics/build/types.d.ts
index 9fd82b23de3d51..458f3fb813584e 100644
--- a/packages/expo-app-metrics/build/types.d.ts
+++ b/packages/expo-app-metrics/build/types.d.ts
@@ -277,19 +277,6 @@ export interface ExpoAppMetricsModuleType {
* @platform ios
*/
triggerCrash(kind: CrashKind): void;
- /**
- * Starts a new app metrics session. Returns the session ID.
- *
- * @private This API is unstable and may change without notice.
- * @platform android
- */
- startSession(): string;
- /**
- * Stops the app metrics session with the given session ID.
- *
- * @platform android
- */
- stopSession(sessionId: string): void;
/**
* @private This API is unstable and may change without notice.
* @platform android
diff --git a/packages/expo-app-metrics/build/types.d.ts.map b/packages/expo-app-metrics/build/types.d.ts.map
index 9dc6d1589c26ae..40e7d594a641c4 100644
--- a/packages/expo-app-metrics/build/types.d.ts.map
+++ b/packages/expo-app-metrics/build/types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/build/types.js.map b/packages/expo-app-metrics/build/types.js.map
index b4f2ab0325129c..1c9100f3482e96 100644
--- a/packages/expo-app-metrics/build/types.js.map
+++ b/packages/expo-app-metrics/build/types.js.map
@@ -1 +1 @@
-{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type AppStartupTimes = {\n /**\n * Time from when the user taps the app to the moment the app starts executing the main code.\n * It includes loading native dynamic libraries, executing C++ static constructors\n * and Objective-C `+load` methods defined in classes or categories.\n *\n * @platform iOS\n */\n loadTime?: number;\n /**\n * Full time from process start until the root view of the React Native instance is created,\n * recorded when the app is launched fresh by the user.\n */\n coldLaunchTime?: number;\n /**\n * Launch time recorded when the process was already running in the background\n * before the user launches the app.\n */\n warmLaunchTime?: number;\n /**\n * Duration to evaluate the JavaScript bundle by the runtime.\n */\n bundleLoadTime?: number;\n /**\n * Time until the first React render occurs.\n */\n timeToFirstRender?: number;\n /**\n * Time until the app is interactive (after first render).\n */\n timeToInteractive?: number;\n};\n\nexport type MemoryUsageSnapshot = {\n /**\n * Memory in bytes allocated by the app, including both the physical memory and additional memory that the app might be using,\n * such as memory that has been paged out (swapped) to disk or memory that is shared with other processes.\n *\n * @platform iOS\n */\n allocated?: number;\n /**\n * Physical memory in bytes pages currently in use (resident size).\n */\n physical: number;\n /**\n * The amount of available memory in bytes that app can still allocate.\n */\n available: number;\n /**\n * The amount of memory in bytes currently used by the Java heap.\n *\n * @platform android\n */\n javaHeap?: number;\n};\n\nexport type FrameRateMetrics = {\n /**\n * Total amount of frames rendered.\n */\n renderedFrames: number;\n /**\n * Expected amount of frames rendered if everything renders without any delay.\n */\n expectedFrames: number;\n /**\n * Number of frames which were skipped because the main thread was busy with some work.\n */\n droppedFrames: number;\n /**\n * Total amount of frozen frames. Frozen frame is frame that takes at least 700ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/frozen).\n */\n frozenFrames: number;\n /**\n * Total amount of slow frames. Slow frame is frame that takes at least 17ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/render).\n */\n slowFrames: number;\n /**\n * Total amount of freeze durations, in seconds. Freeze is an amount of time every frame rendering was delayed by in comparison with the ideal performant frame.\n * For example if expected frame duration was 16ms, but in reality we've rendered this frame in 320ms, we have a freeze with 304ms duration.\n */\n freezeTime: number;\n /**\n * Total duration of the screen session, in seconds. It is counted by summing up all rendered frames duration.\n */\n sessionDuration: number;\n};\n\nexport interface Metric {\n timestamp: string;\n category: string;\n name: string;\n value: number;\n sessionId: string;\n routeName?: string;\n params?: Record;\n}\n\nexport type MetricAttributes = {\n /**\n * Name of the route associated with the metric. Some metrics populate this\n * with a sensible default when omitted â for example, the TTI metric falls\n * back to the initial route name detected from the router.\n */\n routeName?: string;\n /**\n * Custom parameters to attach to the metric.\n */\n params?: Record;\n};\n\n/**\n * Severity of a log event, ordered from least to most severe:\n *\n * - `\"trace\"` â Fine-grained tracing, typically only useful while reproducing\n * a specific issue.\n * - `\"debug\"` â Diagnostic detail useful during development; usually filtered\n * out in production.\n * - `\"info\"` â Routine, expected events that record normal app behavior.\n * - `\"warn\"` â Unexpected but recoverable conditions worth investigating.\n * - `\"error\"` â An operation failed; the app continues running but is in a\n * degraded state.\n * - `\"fatal\"` â A severe failure, often immediately followed by app termination.\n */\nexport type LogSeverity = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\n/**\n * Value types accepted in a log event's `attributes` map. Strings, numbers,\n * and booleans are stored as typed primitives; arrays and nested maps preserve\n * their structure. Other JS values (functions, `Date`, `undefined`, etc.) are\n * not supported and may be dropped by downstream consumers.\n */\nexport type LogAttributeValue =\n | string\n | number\n | boolean\n | LogAttributeValue[]\n | { [key: string]: LogAttributeValue };\n\n/**\n * A single log event collected during a session.\n */\nexport type LogRecord = {\n /**\n * ISO 8601 timestamp of when the record was created.\n */\n timestamp: string;\n /**\n * Event name.\n */\n name: string;\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n */\n severity: LogSeverity;\n};\n\n/**\n * Optional configuration accepted by `logEvent`. The event name is passed as\n * the first positional argument since it's required and the only field most\n * callers set.\n */\nexport type LogEventOptions = {\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n *\n * @default \"info\"\n */\n severity?: LogSeverity | null;\n};\n\nexport type SessionType = 'main' | 'foreground' | 'screen' | 'custom' | 'unknown';\n\nexport type CrashKind =\n | 'badAccess'\n | 'fatalError'\n | 'divideByZero'\n | 'forceUnwrapNil'\n | 'arrayOutOfBounds'\n | 'objcException'\n | 'stackOverflow';\n\ntype SessionBase = {\n id: string;\n startDate: string;\n endDate?: string | null;\n metrics: Metric[];\n logs: LogRecord[];\n};\n\nexport type MainSession = SessionBase & {\n type: 'main';\n crashReport?: CrashReport | null;\n};\n\nexport type GenericSession = SessionBase & {\n type: Exclude;\n};\n\nexport type Session = MainSession | GenericSession;\n\nexport type CallStackFrame = {\n binaryName?: string | null;\n binaryUUID?: string | null;\n address?: number | null;\n offsetIntoBinaryTextSegment?: number | null;\n sampleCount?: number | null;\n subFrames?: CallStackFrame[] | null;\n /**\n * Resolved symbol from on-device `dladdr` symbolication. Swift and Itanium-ABI C++\n * names are demangled; Objective-C selectors and plain C symbols are returned as-is.\n * `null` when the binary is not loaded in this process or `dladdr` could not resolve it.\n *\n * @platform ios\n */\n symbol?: string | null;\n};\n\nexport type CallStack = {\n threadAttributed?: boolean | null;\n callStackRootFrames?: CallStackFrame[] | null;\n};\n\nexport type CallStackTree = {\n callStacks?: CallStack[] | null;\n};\n\nexport type CrashReport = {\n exceptionType?: number | null;\n exceptionCode?: number | null;\n signal?: number | null;\n terminationReason?: string | null;\n virtualMemoryRegionInfo?: string | null;\n exceptionReason?: {\n composedMessage: string;\n formatString: string;\n arguments: string[];\n exceptionType: string;\n className: string;\n exceptionName: string;\n } | null;\n callStackTree?: CallStackTree | null;\n timestampBegin: string;\n timestampEnd: string;\n ingestedAt: string;\n};\n\nexport interface ExpoAppMetricsModuleType {\n markFirstRender(): void;\n markInteractive(attributes?: MetricAttributes): void;\n /**\n * Records a log event against the current main session. The event is\n * persisted locally and dispatched on the next `dispatchEvents()` flush as an\n * OpenTelemetry log record sent to the `/v1/logs` endpoint.\n *\n * Severity defaults to `\"info\"` when not provided.\n *\n * @param name Event name. Maps to the OpenTelemetry `event.name` attribute.\n * @param options Optional body, attributes, and severity overrides.\n */\n logEvent(name: string, options?: LogEventOptions): void;\n getStoredEntries(): Promise;\n clearStoredEntries(): Promise;\n /**\n * Returns all sessions across the current and historical entries,\n * ordered with the current launch first.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n getAllSessions(): Promise;\n\n /**\n * Simulates a crash report, attributing it to the current main session.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n simulateCrashReport(): void;\n\n /**\n * Intentionally crashes the app to produce a real MetricKit diagnostic.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n triggerCrash(kind: CrashKind): void;\n\n /**\n * Starts a new app metrics session. Returns the session ID.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n startSession(): string;\n /**\n * Stops the app metrics session with the given session ID.\n *\n * @platform android\n */\n stopSession(sessionId: string): void;\n /**\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n addCustomMetricToSession(metric: Metric): Promise;\n /**\n * Returns the current main session, including its metrics. Resolves to\n * `null` if the session row hasn't been persisted yet.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n getMainSession(): Promise;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type AppStartupTimes = {\n /**\n * Time from when the user taps the app to the moment the app starts executing the main code.\n * It includes loading native dynamic libraries, executing C++ static constructors\n * and Objective-C `+load` methods defined in classes or categories.\n *\n * @platform iOS\n */\n loadTime?: number;\n /**\n * Full time from process start until the root view of the React Native instance is created,\n * recorded when the app is launched fresh by the user.\n */\n coldLaunchTime?: number;\n /**\n * Launch time recorded when the process was already running in the background\n * before the user launches the app.\n */\n warmLaunchTime?: number;\n /**\n * Duration to evaluate the JavaScript bundle by the runtime.\n */\n bundleLoadTime?: number;\n /**\n * Time until the first React render occurs.\n */\n timeToFirstRender?: number;\n /**\n * Time until the app is interactive (after first render).\n */\n timeToInteractive?: number;\n};\n\nexport type MemoryUsageSnapshot = {\n /**\n * Memory in bytes allocated by the app, including both the physical memory and additional memory that the app might be using,\n * such as memory that has been paged out (swapped) to disk or memory that is shared with other processes.\n *\n * @platform iOS\n */\n allocated?: number;\n /**\n * Physical memory in bytes pages currently in use (resident size).\n */\n physical: number;\n /**\n * The amount of available memory in bytes that app can still allocate.\n */\n available: number;\n /**\n * The amount of memory in bytes currently used by the Java heap.\n *\n * @platform android\n */\n javaHeap?: number;\n};\n\nexport type FrameRateMetrics = {\n /**\n * Total amount of frames rendered.\n */\n renderedFrames: number;\n /**\n * Expected amount of frames rendered if everything renders without any delay.\n */\n expectedFrames: number;\n /**\n * Number of frames which were skipped because the main thread was busy with some work.\n */\n droppedFrames: number;\n /**\n * Total amount of frozen frames. Frozen frame is frame that takes at least 700ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/frozen).\n */\n frozenFrames: number;\n /**\n * Total amount of slow frames. Slow frame is frame that takes at least 17ms to render.\n * It is a term from [Android development](https://developer.android.com/topic/performance/vitals/render).\n */\n slowFrames: number;\n /**\n * Total amount of freeze durations, in seconds. Freeze is an amount of time every frame rendering was delayed by in comparison with the ideal performant frame.\n * For example if expected frame duration was 16ms, but in reality we've rendered this frame in 320ms, we have a freeze with 304ms duration.\n */\n freezeTime: number;\n /**\n * Total duration of the screen session, in seconds. It is counted by summing up all rendered frames duration.\n */\n sessionDuration: number;\n};\n\nexport interface Metric {\n timestamp: string;\n category: string;\n name: string;\n value: number;\n sessionId: string;\n routeName?: string;\n params?: Record;\n}\n\nexport type MetricAttributes = {\n /**\n * Name of the route associated with the metric. Some metrics populate this\n * with a sensible default when omitted â for example, the TTI metric falls\n * back to the initial route name detected from the router.\n */\n routeName?: string;\n /**\n * Custom parameters to attach to the metric.\n */\n params?: Record;\n};\n\n/**\n * Severity of a log event, ordered from least to most severe:\n *\n * - `\"trace\"` â Fine-grained tracing, typically only useful while reproducing\n * a specific issue.\n * - `\"debug\"` â Diagnostic detail useful during development; usually filtered\n * out in production.\n * - `\"info\"` â Routine, expected events that record normal app behavior.\n * - `\"warn\"` â Unexpected but recoverable conditions worth investigating.\n * - `\"error\"` â An operation failed; the app continues running but is in a\n * degraded state.\n * - `\"fatal\"` â A severe failure, often immediately followed by app termination.\n */\nexport type LogSeverity = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\n/**\n * Value types accepted in a log event's `attributes` map. Strings, numbers,\n * and booleans are stored as typed primitives; arrays and nested maps preserve\n * their structure. Other JS values (functions, `Date`, `undefined`, etc.) are\n * not supported and may be dropped by downstream consumers.\n */\nexport type LogAttributeValue =\n | string\n | number\n | boolean\n | LogAttributeValue[]\n | { [key: string]: LogAttributeValue };\n\n/**\n * A single log event collected during a session.\n */\nexport type LogRecord = {\n /**\n * ISO 8601 timestamp of when the record was created.\n */\n timestamp: string;\n /**\n * Event name.\n */\n name: string;\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n */\n severity: LogSeverity;\n};\n\n/**\n * Optional configuration accepted by `logEvent`. The event name is passed as\n * the first positional argument since it's required and the only field most\n * callers set.\n */\nexport type LogEventOptions = {\n /**\n * Optional free-form message describing the event.\n */\n body?: string | null;\n /**\n * Custom attributes attached to the event. Each entry is preserved with its\n * original value type â see `LogAttributeValue` for the supported shapes.\n */\n attributes?: Record | null;\n /**\n * Severity of the event.\n *\n * @default \"info\"\n */\n severity?: LogSeverity | null;\n};\n\nexport type SessionType = 'main' | 'foreground' | 'screen' | 'custom' | 'unknown';\n\nexport type CrashKind =\n | 'badAccess'\n | 'fatalError'\n | 'divideByZero'\n | 'forceUnwrapNil'\n | 'arrayOutOfBounds'\n | 'objcException'\n | 'stackOverflow';\n\ntype SessionBase = {\n id: string;\n startDate: string;\n endDate?: string | null;\n metrics: Metric[];\n logs: LogRecord[];\n};\n\nexport type MainSession = SessionBase & {\n type: 'main';\n crashReport?: CrashReport | null;\n};\n\nexport type GenericSession = SessionBase & {\n type: Exclude;\n};\n\nexport type Session = MainSession | GenericSession;\n\nexport type CallStackFrame = {\n binaryName?: string | null;\n binaryUUID?: string | null;\n address?: number | null;\n offsetIntoBinaryTextSegment?: number | null;\n sampleCount?: number | null;\n subFrames?: CallStackFrame[] | null;\n /**\n * Resolved symbol from on-device `dladdr` symbolication. Swift and Itanium-ABI C++\n * names are demangled; Objective-C selectors and plain C symbols are returned as-is.\n * `null` when the binary is not loaded in this process or `dladdr` could not resolve it.\n *\n * @platform ios\n */\n symbol?: string | null;\n};\n\nexport type CallStack = {\n threadAttributed?: boolean | null;\n callStackRootFrames?: CallStackFrame[] | null;\n};\n\nexport type CallStackTree = {\n callStacks?: CallStack[] | null;\n};\n\nexport type CrashReport = {\n exceptionType?: number | null;\n exceptionCode?: number | null;\n signal?: number | null;\n terminationReason?: string | null;\n virtualMemoryRegionInfo?: string | null;\n exceptionReason?: {\n composedMessage: string;\n formatString: string;\n arguments: string[];\n exceptionType: string;\n className: string;\n exceptionName: string;\n } | null;\n callStackTree?: CallStackTree | null;\n timestampBegin: string;\n timestampEnd: string;\n ingestedAt: string;\n};\n\nexport interface ExpoAppMetricsModuleType {\n markFirstRender(): void;\n markInteractive(attributes?: MetricAttributes): void;\n /**\n * Records a log event against the current main session. The event is\n * persisted locally and dispatched on the next `dispatchEvents()` flush as an\n * OpenTelemetry log record sent to the `/v1/logs` endpoint.\n *\n * Severity defaults to `\"info\"` when not provided.\n *\n * @param name Event name. Maps to the OpenTelemetry `event.name` attribute.\n * @param options Optional body, attributes, and severity overrides.\n */\n logEvent(name: string, options?: LogEventOptions): void;\n getStoredEntries(): Promise;\n clearStoredEntries(): Promise;\n /**\n * Returns all sessions across the current and historical entries,\n * ordered with the current launch first.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n getAllSessions(): Promise;\n\n /**\n * Simulates a crash report, attributing it to the current main session.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n simulateCrashReport(): void;\n\n /**\n * Intentionally crashes the app to produce a real MetricKit diagnostic.\n * Intended for development and debugging only.\n *\n * @private This API is unstable and may change without notice.\n * @platform ios\n */\n triggerCrash(kind: CrashKind): void;\n\n /**\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n addCustomMetricToSession(metric: Metric): Promise;\n /**\n * Returns the current main session, including its metrics. Resolves to\n * `null` if the session row hasn't been persisted yet.\n *\n * @private This API is unstable and may change without notice.\n * @platform android\n */\n getMainSession(): Promise;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-app-metrics/src/module.web.ts b/packages/expo-app-metrics/src/module.web.ts
index 6c03faea8e5cdc..4f5fd53ffc8e17 100644
--- a/packages/expo-app-metrics/src/module.web.ts
+++ b/packages/expo-app-metrics/src/module.web.ts
@@ -20,10 +20,6 @@ class ExpoAppMetricsModule extends NativeModule implements ExpoAppMetricsModuleT
}
simulateCrashReport() {}
triggerCrash() {}
- startSession(metadata?: string) {
- return '';
- }
- stopSession(sessionId: string) {}
async getMainSession() {
return null;
}
diff --git a/packages/expo-app-metrics/src/types.ts b/packages/expo-app-metrics/src/types.ts
index c8f539301b295e..4aa02663ae5380 100644
--- a/packages/expo-app-metrics/src/types.ts
+++ b/packages/expo-app-metrics/src/types.ts
@@ -309,19 +309,6 @@ export interface ExpoAppMetricsModuleType {
*/
triggerCrash(kind: CrashKind): void;
- /**
- * Starts a new app metrics session. Returns the session ID.
- *
- * @private This API is unstable and may change without notice.
- * @platform android
- */
- startSession(): string;
- /**
- * Stops the app metrics session with the given session ID.
- *
- * @platform android
- */
- stopSession(sessionId: string): void;
/**
* @private This API is unstable and may change without notice.
* @platform android
From bad1f627551164148533521a11bf50dec606f044 Mon Sep 17 00:00:00 2001
From: Wiktor Smaga
Date: Tue, 12 May 2026 15:43:19 +0200
Subject: [PATCH 09/11] [dev-menu] Fix FAB safe-area bounds (#45647)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Why
There was a bug in calculating the safe area bounds for the FAB. In the
first invocation of `rememberFabState`, `SafeBarInsets` were (0,0).
This value stayed in `FabState` forever because the `remember` clause
lacked change keys, causing the FAB to be positioned above the
statusBar:
The easier fix would be to make bounds mutable:
```kotlin
val state = remember { FabState(initialOffset, fabBounds, prefs) }
state.fabBounds = fabBounds
```
However, there is a better solution: slightly changing the architecture
to move safe-area responsibility to `MovableFloatingActionButton`. This
is achieved with the `safeDrawingPadding` modifier. It works like
`WindowInsets.systemBars` but removes the need for manual calculations
in `rememberFabState`. This way, the source of truth for bounds is the
`BoxWithConstraints` size, not `rememberFabState` logic.
# How
- Simplified FabState to properties used inside the class; moved the
rest of responsibilities to `MovableFloatingActionButton`
- Made state bounds mutable so they can update when they change (as seen
in the screenshot above).
# Test Plan
BareExpo â
[Screen_recording_20260512_104957.webm](https://github.com/user-attachments/assets/352ed42a-fec9-479c-9216-8c950a7f9fe3)
---
packages/expo-dev-menu/CHANGELOG.md | 2 +
.../java/expo/modules/devmenu/fab/FabState.kt | 51 +++++--------------
.../java/expo/modules/devmenu/fab/FabUtils.kt | 10 ++++
.../fab/MovableFloatingActionButton.kt | 39 ++++++++++----
4 files changed, 53 insertions(+), 49 deletions(-)
diff --git a/packages/expo-dev-menu/CHANGELOG.md b/packages/expo-dev-menu/CHANGELOG.md
index 9973367a4d0eea..9049187aa77240 100644
--- a/packages/expo-dev-menu/CHANGELOG.md
+++ b/packages/expo-dev-menu/CHANGELOG.md
@@ -8,6 +8,8 @@
### đ Bug fixes
+- Fix FAB safe-area bounds ([#45647](https://github.com/expo/expo/pull/45647) by [@Wenszel](https://github.com/Wenszel))
+
### đĄ Others
## 56.0.6 â 2026-05-11
diff --git a/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabState.kt b/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabState.kt
index 233262b829c29b..7f7cc1df5f7fe9 100644
--- a/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabState.kt
+++ b/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabState.kt
@@ -4,8 +4,6 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.VectorConverter
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.systemBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -15,7 +13,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalDensity
import androidx.core.content.edit
import kotlinx.coroutines.delay
@@ -25,17 +22,9 @@ private const val FAB_POSITION_X = "fabPositionX"
private const val FAB_POSITION_Y = "fabPositionY"
private const val FAB_POSITION_UNSET = -1f
-data class FabBounds(
- val screen: Offset,
- val safe: Offset,
- val safeMinY: Float,
- val drag: Offset,
- val halfFab: Offset
-)
-
class FabState(
initialOffset: Offset,
- val bounds: FabBounds,
+ var fabAreaBounds: Offset,
val prefs: SharedPreferences
) {
val animatedOffset = Animatable(initialOffset, Offset.VectorConverter)
@@ -51,12 +40,9 @@ class FabState(
}
fun savePosition(offset: Offset) {
- val safeWidth = bounds.safe.x
- val safeHeight = bounds.safe.y - bounds.safeMinY
-
// Store position as 0â1 ratios within the safe area so it survives screen size changes
- val normalizedX = if (safeWidth > 0f) offset.x / safeWidth else 0f
- val normalizedY = if (safeHeight > 0f) (offset.y - bounds.safeMinY) / safeHeight else 0f
+ val normalizedX = if (fabAreaBounds.x > 0f) offset.x / fabAreaBounds.x else 0f
+ val normalizedY = if (fabAreaBounds.y > 0f) offset.y / fabAreaBounds.y else 0f
prefs.edit {
putFloat(FAB_POSITION_X, normalizedX)
@@ -66,40 +52,27 @@ class FabState(
}
@Composable
-fun rememberFabState(screenBounds: Offset, totalFabSizePx: Offset): FabState {
- val density = LocalDensity.current
- val systemBarInsets = WindowInsets.systemBars
- val safeInsetTop = with(density) { systemBarInsets.getTop(this).toFloat() }
- val safeInsetBottom = with(density) { systemBarInsets.getBottom(this).toFloat() }
- val halfFab = Offset(totalFabSizePx.x / 2f, totalFabSizePx.y / 2f)
-
- val fabBounds = FabBounds(
- screen = screenBounds,
- safe = Offset(screenBounds.x, screenBounds.y - safeInsetBottom),
- safeMinY = safeInsetTop,
- drag = Offset(screenBounds.x + halfFab.x, screenBounds.y + halfFab.y),
- halfFab = halfFab
- )
-
+fun rememberFabState(fabAreaBounds: Offset): FabState {
val context = LocalContext.current
val prefs = remember { context.getSharedPreferences(FAB_PREFS, Context.MODE_PRIVATE) }
- val initialOffset = remember(fabBounds.safe, fabBounds.safeMinY) {
+ val initialOffset = remember {
val savedX = prefs.getFloat(FAB_POSITION_X, FAB_POSITION_UNSET)
val savedY = prefs.getFloat(FAB_POSITION_Y, FAB_POSITION_UNSET)
- val safeMaxX = maxOf(0f, fabBounds.safe.x)
- val safeMaxY = maxOf(fabBounds.safeMinY, fabBounds.safe.y)
if (savedX != FAB_POSITION_UNSET && savedY != FAB_POSITION_UNSET) {
Offset(
- x = (savedX * safeMaxX).coerceIn(0f, safeMaxX),
- y = (savedY * (safeMaxY - fabBounds.safeMinY) + fabBounds.safeMinY).coerceIn(fabBounds.safeMinY, safeMaxY)
+ x = (savedX * fabAreaBounds.x).coerceIn(0f, fabAreaBounds.x),
+ y = (savedY * fabAreaBounds.y).coerceIn(0f, fabAreaBounds.y)
)
} else {
- Offset(fabBounds.safe.x, fabBounds.safeMinY)
+ Offset(fabAreaBounds.x, 0f)
}
}
- val state = remember { FabState(initialOffset, fabBounds, prefs) }
+ val state = remember { FabState(initialOffset, fabAreaBounds, prefs) }
+ // We can't simply update the entire state when the bounds change,
+ // because it would result in resetting the interaction state (isPressed, isDragging).
+ state.fabAreaBounds = fabAreaBounds
LaunchedEffect(state.lastInteractionTime) {
state.isIdle = false
diff --git a/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabUtils.kt b/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabUtils.kt
index 69ae479d86c239..7cd25c71ec1b4f 100644
--- a/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabUtils.kt
+++ b/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/FabUtils.kt
@@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.unit.IntOffset
import expo.modules.devmenu.fab.ExpoVelocityTracker.PointF
import kotlin.math.roundToInt
@@ -49,6 +50,15 @@ internal fun Offset.coerceIn(minX: Float = 0f, maxX: Float, minY: Float = 0f, ma
)
}
+internal fun Offset.coerceIn(rect: Rect): Offset {
+ return this.coerceIn(
+ minX = rect.left,
+ maxX = rect.right,
+ minY = rect.top,
+ maxY = rect.bottom
+ )
+}
+
@Composable
internal fun rememberPrevious(current: T): T? {
val previousRef = remember { mutableStateOf(null) }
diff --git a/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/MovableFloatingActionButton.kt b/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/MovableFloatingActionButton.kt
index 83fb8ff3db2bc6..c26b78641e0beb 100644
--- a/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/MovableFloatingActionButton.kt
+++ b/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/fab/MovableFloatingActionButton.kt
@@ -14,12 +14,14 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.platform.LocalDensity
@@ -48,7 +50,11 @@ fun MovableFloatingActionButton(
margin: Dp = Margin,
onPress: () -> Unit = {}
) {
- BoxWithConstraints(modifier = modifier.fillMaxSize()) {
+ BoxWithConstraints(
+ modifier = modifier
+ .safeDrawingPadding()
+ .fillMaxSize()
+ ) {
val totalFabSize = DpSize(fabSize.width + margin * 2, fabSize.height + margin * 2)
val totalFabSizePx = with(LocalDensity.current) {
Offset(totalFabSize.width.toPx(), totalFabSize.height.toPx())
@@ -58,7 +64,16 @@ fun MovableFloatingActionButton(
y = constraints.maxHeight - totalFabSizePx.y
)
- val fab = rememberFabState(bounds, totalFabSizePx)
+ val halfFab = Offset(totalFabSizePx.x / 2f, totalFabSizePx.y / 2f)
+ val dragBounds = Rect(
+ left = -halfFab.x,
+ top = -halfFab.y,
+ right = bounds.x + halfFab.x,
+ bottom = bounds.y + halfFab.y
+ )
+
+ val fab = rememberFabState(bounds)
+
val isFabDisplayable = state.showFab &&
!state.isInPictureInPictureMode &&
bounds.x >= 0f &&
@@ -70,7 +85,7 @@ fun MovableFloatingActionButton(
LaunchedEffect(state.isOpen) {
if (state.isOpen) {
fab.restingOffset = fab.animatedOffset.value
- val isOnLeftSide = fab.animatedOffset.value.x < fab.bounds.safe.x / 2f
+ val isOnLeftSide = fab.animatedOffset.value.x < fab.fabAreaBounds.x / 2f
val offScreenX = if (isOnLeftSide) -totalFabSizePx.x else constraints.maxWidth.toFloat()
fab.animatedOffset.animateTo(
targetValue = Offset(offScreenX, fab.animatedOffset.value.y),
@@ -97,14 +112,13 @@ fun MovableFloatingActionButton(
previousBounds?.let {
val oldX = fab.animatedOffset.value.x
val oldY = fab.animatedOffset.value.y
- val newX = (oldX / previousBounds.x) * fab.bounds.safe.x
- val newY = (oldY / previousBounds.y) * fab.bounds.safe.y
+ val newX = (oldX / previousBounds.x) * fab.fabAreaBounds.x
+ val newY = (oldY / previousBounds.y) * fab.fabAreaBounds.y
val newTarget = calculateTargetPosition(
currentPosition = Offset(newX, newY),
velocity = ExpoVelocityTracker.PointF(0f, 0f),
- bounds = fab.bounds.safe,
- totalFabWidth = totalFabSizePx.x,
- minY = fab.bounds.safeMinY
+ bounds = fab.fabAreaBounds,
+ totalFabWidth = totalFabSizePx.x
)
fab.animatedOffset.snapTo(newTarget)
@@ -140,7 +154,7 @@ fun MovableFloatingActionButton(
drag(pointerId) { change ->
dragOffset = (dragOffset + change.positionChange())
- .coerceIn(minX = -fab.bounds.halfFab.x, maxX = fab.bounds.drag.x, minY = -fab.bounds.halfFab.y, maxY = fab.bounds.drag.y)
+ .coerceIn(dragBounds)
dragDistance += change.positionChange().getDistance()
velocityTracker.registerPosition(dragOffset.x, dragOffset.y)
@@ -187,7 +201,12 @@ private fun CoroutineScope.handleRelease(
totalFabSizePx: Offset
) {
val velocity = velocityTracker.calculateVelocity()
- val newOffset = calculateTargetPosition(fab.animatedOffset.value, velocity, fab.bounds.safe, totalFabSizePx.x, fab.bounds.safeMinY)
+ val newOffset = calculateTargetPosition(
+ currentPosition = fab.animatedOffset.value,
+ velocity = velocity,
+ bounds = fab.fabAreaBounds,
+ totalFabWidth = totalFabSizePx.x
+ )
velocityTracker.clear()
launch {
From 167437e99e51f7ddafb4390f2c46bca4d4246dff Mon Sep 17 00:00:00 2001
From: Wiktor Smaga
Date: Tue, 12 May 2026 16:31:37 +0200
Subject: [PATCH 10/11] [expo] Reexport permission types and hooks from
expo-modules-core (#45564)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Why
Types and functions related to permissions should be re-exported from the `expo` package as discussed here:
https://github.com/expo/expo/pull/44403
# How
Re-exported the types and functions from `expo-modules-core` to the `expo` package.
# Test Plan
BareExpo â
---
packages/expo/CHANGELOG.md | 2 ++
packages/expo/build/Expo.d.ts | 2 ++
packages/expo/build/Expo.d.ts.map | 2 +-
packages/expo/src/Expo.ts | 9 +++++++++
4 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/packages/expo/CHANGELOG.md b/packages/expo/CHANGELOG.md
index 53f50b1b7e9b3f..cbdea04fc715f6 100644
--- a/packages/expo/CHANGELOG.md
+++ b/packages/expo/CHANGELOG.md
@@ -12,6 +12,8 @@
### đĄ Others
+- Reexport permission hooks and permission types from `expo-modules-core` ([#45564](https://github.com/expo/expo/pull/45564) by [@Wenszel](https://github.com/Wenszel))
+
## 56.0.0-preview.9 â 2026-05-12
_This version does not introduce any user-facing changes._
diff --git a/packages/expo/build/Expo.d.ts b/packages/expo/build/Expo.d.ts
index c9570c2f870d84..0a6ccae4ee0c41 100644
--- a/packages/expo/build/Expo.d.ts
+++ b/packages/expo/build/Expo.d.ts
@@ -12,5 +12,7 @@ EventEmitter as EventEmitterType,
NativeModule as NativeModuleType,
/** @deprecated Move to `SharedObject` with a type-only import instead */
SharedObject as SharedObjectType, } from 'expo-modules-core/types';
+export { PermissionStatus, type PermissionExpiration, type PermissionResponse, type PermissionHookOptions, } from 'expo-modules-core';
+export { createPermissionHook } from 'expo-modules-core';
export { useEvent, useEventListener } from './hooks/useEvent';
//# sourceMappingURL=Expo.d.ts.map
\ No newline at end of file
diff --git a/packages/expo/build/Expo.d.ts.map b/packages/expo/build/Expo.d.ts.map
index c24ad97236c0b1..0bb823e3f410ad 100644
--- a/packages/expo/build/Expo.d.ts.map
+++ b/packages/expo/build/Expo.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"Expo.d.ts","sourceRoot":"","sources":["../src/Expo.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAC;AAEnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAElF,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EAEL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,EAGZ,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,IAAI,iBAAiB,EAC7C,iBAAiB,EACjB,cAAc,EAGd,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,YAAY;AACV,sEAAsE;AACtE,SAAS,IAAI,aAAa;AAC1B,yEAAyE;AACzE,YAAY,IAAI,gBAAgB;AAChC,yEAAyE;AACzE,YAAY,IAAI,gBAAgB;AAChC,yEAAyE;AACzE,YAAY,IAAI,gBAAgB,GACjC,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"Expo.d.ts","sourceRoot":"","sources":["../src/Expo.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAC;AAEnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAElF,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EAEL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,EAGZ,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,IAAI,iBAAiB,EAC7C,iBAAiB,EACjB,cAAc,EAGd,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,YAAY;AACV,sEAAsE;AACtE,SAAS,IAAI,aAAa;AAC1B,yEAAyE;AACzE,YAAY,IAAI,gBAAgB;AAChC,yEAAyE;AACzE,YAAY,IAAI,gBAAgB;AAChC,yEAAyE;AACzE,YAAY,IAAI,gBAAgB,GACjC,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
\ No newline at end of file
diff --git a/packages/expo/src/Expo.ts b/packages/expo/src/Expo.ts
index ddd819acc44b81..723d4f213e5591 100644
--- a/packages/expo/src/Expo.ts
+++ b/packages/expo/src/Expo.ts
@@ -34,4 +34,13 @@ export type {
SharedObject as SharedObjectType,
} from 'expo-modules-core/types';
+export {
+ PermissionStatus,
+ type PermissionExpiration,
+ type PermissionResponse,
+ type PermissionHookOptions,
+} from 'expo-modules-core';
+
+export { createPermissionHook } from 'expo-modules-core';
+
export { useEvent, useEventListener } from './hooks/useEvent';
From e519d0949eb0c70aac46d944e2693b8e31d273b4 Mon Sep 17 00:00:00 2001
From: Wiktor Smaga
Date: Tue, 12 May 2026 16:33:38 +0200
Subject: [PATCH 11/11] [expo] Update permission types and hooks imports to
expo (#45565)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Why
Follow-up to the #45564, updating the imports from expo-modules-core to expo.
# How
- Updated the imports in packages that use permissions-related types and functions from expo-modules-core.
# Test Plan
BareExpo â
---
packages/expo-audio/CHANGELOG.md | 2 ++
.../expo-audio/build/AudioModule.types.d.ts | 2 +-
.../expo-audio/build/AudioModule.types.d.ts.map | 2 +-
.../expo-audio/build/AudioModule.types.js.map | 2 +-
packages/expo-audio/build/AudioModule.web.d.ts | 2 +-
.../expo-audio/build/AudioModule.web.d.ts.map | 2 +-
packages/expo-audio/build/AudioModule.web.js | 2 +-
.../expo-audio/build/AudioModule.web.js.map | 2 +-
packages/expo-audio/build/ExpoAudio.d.ts | 2 +-
packages/expo-audio/build/ExpoAudio.d.ts.map | 2 +-
packages/expo-audio/build/ExpoAudio.js.map | 2 +-
packages/expo-audio/build/ExpoAudio.web.d.ts | 2 +-
.../expo-audio/build/ExpoAudio.web.d.ts.map | 2 +-
packages/expo-audio/build/ExpoAudio.web.js.map | 2 +-
packages/expo-audio/build/index.d.ts | 2 +-
packages/expo-audio/build/index.d.ts.map | 2 +-
packages/expo-audio/build/index.js | 2 +-
packages/expo-audio/build/index.js.map | 2 +-
packages/expo-audio/src/AudioModule.types.ts | 2 +-
packages/expo-audio/src/AudioModule.web.ts | 3 +--
packages/expo-audio/src/ExpoAudio.ts | 3 +--
packages/expo-audio/src/ExpoAudio.web.ts | 3 +--
packages/expo-audio/src/index.ts | 6 +-----
packages/expo-brightness/CHANGELOG.md | 2 ++
packages/expo-brightness/build/Brightness.d.ts | 5 +++--
.../expo-brightness/build/Brightness.d.ts.map | 2 +-
packages/expo-brightness/build/Brightness.js | 5 +++--
.../expo-brightness/build/Brightness.js.map | 2 +-
.../build/ExpoBrightness.web.d.ts | 2 +-
.../build/ExpoBrightness.web.d.ts.map | 2 +-
.../expo-brightness/build/ExpoBrightness.web.js | 2 +-
.../build/ExpoBrightness.web.js.map | 2 +-
packages/expo-brightness/src/Brightness.ts | 10 +++-------
.../expo-brightness/src/ExpoBrightness.web.ts | 3 +--
packages/expo-calendar/CHANGELOG.md | 2 ++
packages/expo-calendar/build/Calendar.d.ts | 4 ++--
packages/expo-calendar/build/Calendar.d.ts.map | 2 +-
packages/expo-calendar/build/Calendar.js | 3 ++-
packages/expo-calendar/build/Calendar.js.map | 2 +-
.../expo-calendar/build/ExpoCalendar.web.d.ts | 2 +-
.../build/ExpoCalendar.web.d.ts.map | 2 +-
.../expo-calendar/build/ExpoCalendar.web.js | 2 +-
.../expo-calendar/build/ExpoCalendar.web.js.map | 2 +-
.../expo-calendar/build/next/Calendar.d.ts.map | 2 +-
packages/expo-calendar/build/next/Calendar.js | 3 ++-
.../expo-calendar/build/next/Calendar.js.map | 2 +-
.../expo-calendar/build/next/ExpoCalendar.d.ts | 2 +-
.../build/next/ExpoCalendar.d.ts.map | 2 +-
.../build/next/ExpoCalendar.js.map | 2 +-
.../build/next/ExpoCalendar.web.d.ts | 2 +-
.../build/next/ExpoCalendar.web.d.ts.map | 2 +-
.../build/next/ExpoCalendar.web.js | 2 +-
.../build/next/ExpoCalendar.web.js.map | 2 +-
.../build/next/ExpoGoCalendarNextStub.d.ts | 2 +-
.../build/next/ExpoGoCalendarNextStub.d.ts.map | 2 +-
.../build/next/ExpoGoCalendarNextStub.js.map | 2 +-
packages/expo-calendar/src/Calendar.ts | 6 +++---
packages/expo-calendar/src/ExpoCalendar.web.ts | 3 +--
packages/expo-calendar/src/next/Calendar.ts | 3 ++-
packages/expo-calendar/src/next/ExpoCalendar.ts | 3 +--
.../expo-calendar/src/next/ExpoCalendar.web.ts | 3 +--
.../src/next/ExpoGoCalendarNextStub.ts | 2 +-
packages/expo-camera/CHANGELOG.md | 2 ++
packages/expo-camera/build/Camera.types.d.ts | 6 ++++--
.../expo-camera/build/Camera.types.d.ts.map | 2 +-
packages/expo-camera/build/Camera.types.js | 4 +++-
packages/expo-camera/build/Camera.types.js.map | 2 +-
packages/expo-camera/build/index.d.ts | 2 +-
packages/expo-camera/build/index.d.ts.map | 2 +-
packages/expo-camera/build/index.js | 2 +-
packages/expo-camera/build/index.js.map | 2 +-
packages/expo-camera/src/Camera.types.ts | 7 ++++---
packages/expo-camera/src/index.ts | 3 +--
packages/expo-cellular/CHANGELOG.md | 2 ++
packages/expo-cellular/build/Cellular.d.ts.map | 2 +-
packages/expo-cellular/build/Cellular.js | 3 ++-
packages/expo-cellular/build/Cellular.js.map | 2 +-
.../expo-cellular/build/Cellular.types.d.ts | 2 +-
.../expo-cellular/build/Cellular.types.d.ts.map | 2 +-
packages/expo-cellular/build/Cellular.types.js | 2 +-
.../expo-cellular/build/Cellular.types.js.map | 2 +-
packages/expo-cellular/src/Cellular.ts | 8 ++------
packages/expo-cellular/src/Cellular.types.ts | 2 +-
packages/expo-contacts/CHANGELOG.md | 2 ++
packages/expo-contacts/build/Contacts.d.ts | 5 +++--
packages/expo-contacts/build/Contacts.d.ts.map | 2 +-
.../expo-contacts/build/ExpoContacts.web.d.ts | 2 +-
.../build/ExpoContacts.web.d.ts.map | 2 +-
.../build/next/types/Permissions.d.ts | 2 +-
.../build/next/types/Permissions.d.ts.map | 2 +-
packages/expo-contacts/src/Contacts.ts | 14 +++-----------
packages/expo-contacts/src/ExpoContacts.web.ts | 3 +--
.../expo-contacts/src/next/types/Permissions.ts | 2 +-
packages/expo-image-picker/CHANGELOG.md | 2 ++
.../build/ExponentImagePicker.web.d.ts | 2 +-
.../build/ExponentImagePicker.web.d.ts.map | 2 +-
.../build/ExponentImagePicker.web.js | 3 ++-
.../build/ExponentImagePicker.web.js.map | 2 +-
.../expo-image-picker/build/ImagePicker.d.ts | 3 +--
.../build/ImagePicker.d.ts.map | 2 +-
packages/expo-image-picker/build/ImagePicker.js | 3 ++-
.../expo-image-picker/build/ImagePicker.js.map | 2 +-
.../build/ImagePicker.types.d.ts | 2 +-
.../build/ImagePicker.types.d.ts.map | 2 +-
.../build/ImagePicker.types.js.map | 2 +-
.../src/ExponentImagePicker.web.ts | 4 ++--
packages/expo-image-picker/src/ImagePicker.ts | 15 ++++++---------
.../expo-image-picker/src/ImagePicker.types.ts | 2 +-
packages/expo-location/CHANGELOG.md | 2 ++
.../expo-location/build/ExpoLocation.web.d.ts | 2 +-
.../build/ExpoLocation.web.d.ts.map | 2 +-
.../expo-location/build/ExpoLocation.web.js | 3 ++-
.../expo-location/build/ExpoLocation.web.js.map | 2 +-
packages/expo-location/build/Location.d.ts | 2 +-
packages/expo-location/build/Location.d.ts.map | 2 +-
packages/expo-location/build/Location.js | 4 ++--
packages/expo-location/build/Location.js.map | 2 +-
.../expo-location/build/Location.types.d.ts | 2 +-
.../expo-location/build/Location.types.d.ts.map | 2 +-
.../expo-location/build/Location.types.js.map | 2 +-
packages/expo-location/build/index.d.ts | 2 +-
packages/expo-location/build/index.d.ts.map | 2 +-
packages/expo-location/build/index.js | 2 +-
packages/expo-location/build/index.js.map | 2 +-
packages/expo-location/src/ExpoLocation.web.ts | 4 ++--
packages/expo-location/src/Location.ts | 5 ++---
packages/expo-location/src/Location.types.ts | 2 +-
packages/expo-location/src/index.ts | 6 +-----
packages/expo-maps/CHANGELOG.md | 2 ++
packages/expo-maps/build/index.js | 2 +-
packages/expo-maps/build/index.js.map | 2 +-
packages/expo-maps/build/shared.types.d.ts | 2 +-
packages/expo-maps/build/shared.types.d.ts.map | 2 +-
packages/expo-maps/build/shared.types.js.map | 2 +-
packages/expo-maps/src/index.ts | 2 +-
packages/expo-maps/src/shared.types.ts | 2 +-
packages/expo-media-library/CHANGELOG.md | 2 ++
.../build/ExpoMediaLibrary.web.d.ts | 2 +-
.../build/ExpoMediaLibrary.web.d.ts.map | 2 +-
.../build/ExpoMediaLibrary.web.js | 2 +-
.../build/ExpoMediaLibrary.web.js.map | 2 +-
.../expo-media-library/build/MediaLibrary.d.ts | 6 ++++--
.../build/MediaLibrary.d.ts.map | 2 +-
.../expo-media-library/build/MediaLibrary.js | 6 ++++--
.../build/MediaLibrary.js.map | 2 +-
.../build/next/ExpoMediaLibraryNext.d.ts | 2 +-
.../build/next/ExpoMediaLibraryNext.d.ts.map | 2 +-
.../build/next/ExpoMediaLibraryNext.js.map | 2 +-
.../expo-media-library/build/next/index.d.ts | 3 ++-
.../build/next/index.d.ts.map | 2 +-
packages/expo-media-library/build/next/index.js | 3 ++-
.../expo-media-library/build/next/index.js.map | 2 +-
.../src/ExpoMediaLibrary.web.ts | 3 +--
packages/expo-media-library/src/MediaLibrary.ts | 12 ++++--------
.../src/next/ExpoMediaLibraryNext.ts | 2 +-
packages/expo-media-library/src/next/index.ts | 8 ++------
packages/expo-notifications/CHANGELOG.md | 2 ++
.../build/NotificationPermissions.types.d.ts | 2 +-
.../NotificationPermissions.types.d.ts.map | 2 +-
.../build/NotificationPermissions.types.js.map | 2 +-
.../NotificationPermissionsModule.d.ts.map | 2 +-
.../build/NotificationPermissionsModule.js | 3 ++-
.../build/NotificationPermissionsModule.js.map | 2 +-
.../build/Notifications.types.d.ts | 3 ++-
.../build/Notifications.types.d.ts.map | 2 +-
.../build/Notifications.types.js | 3 ++-
.../build/Notifications.types.js.map | 2 +-
.../src/NotificationPermissions.types.ts | 2 +-
.../src/NotificationPermissionsModule.ts | 3 ++-
.../src/Notifications.types.ts | 8 ++------
packages/expo-screen-capture/CHANGELOG.md | 2 ++
.../build/ScreenCapture.d.ts | 6 ++++--
.../build/ScreenCapture.d.ts.map | 2 +-
.../expo-screen-capture/build/ScreenCapture.js | 6 ++++--
.../build/ScreenCapture.js.map | 2 +-
.../expo-screen-capture/src/ScreenCapture.ts | 17 ++++-------------
packages/expo-sensors/CHANGELOG.md | 2 ++
packages/expo-sensors/build/DeviceSensor.d.ts | 3 ++-
.../expo-sensors/build/DeviceSensor.d.ts.map | 2 +-
packages/expo-sensors/build/DeviceSensor.js | 3 ++-
packages/expo-sensors/build/DeviceSensor.js.map | 2 +-
packages/expo-sensors/build/Pedometer.d.ts | 6 ++++--
packages/expo-sensors/build/Pedometer.d.ts.map | 2 +-
packages/expo-sensors/build/Pedometer.js | 6 ++++--
packages/expo-sensors/build/Pedometer.js.map | 2 +-
.../build/utils/isSensorEnabledAsync.web.d.ts | 2 +-
.../utils/isSensorEnabledAsync.web.d.ts.map | 2 +-
.../build/utils/isSensorEnabledAsync.web.js | 3 ++-
.../build/utils/isSensorEnabledAsync.web.js.map | 2 +-
packages/expo-sensors/src/DeviceSensor.ts | 9 ++-------
packages/expo-sensors/src/Pedometer.ts | 16 ++++------------
.../src/utils/isSensorEnabledAsync.web.ts | 4 ++--
.../expo-tracking-transparency/CHANGELOG.md | 2 ++
.../build/TrackingTransparency.d.ts | 2 +-
.../build/TrackingTransparency.d.ts.map | 2 +-
.../build/TrackingTransparency.js | 3 ++-
.../build/TrackingTransparency.js.map | 2 +-
.../src/TrackingTransparency.ts | 6 +++---
198 files changed, 293 insertions(+), 301 deletions(-)
diff --git a/packages/expo-audio/CHANGELOG.md b/packages/expo-audio/CHANGELOG.md
index 9aa465034bb9dc..ce99b86ffa9de0 100644
--- a/packages/expo-audio/CHANGELOG.md
+++ b/packages/expo-audio/CHANGELOG.md
@@ -10,6 +10,8 @@
### đĄ Others
+- Updated permission type and permission status imports to be imported from `expo` instead of `expo-modules-core` ([#45565](https://github.com/expo/expo/pull/45565) by [@Wenszel](https://github.com/Wenszel))
+
## 56.0.4 â 2026-05-11
_This version does not introduce any user-facing changes._
diff --git a/packages/expo-audio/build/AudioModule.types.d.ts b/packages/expo-audio/build/AudioModule.types.d.ts
index b932c5d42be297..f3ae4ee4044d86 100644
--- a/packages/expo-audio/build/AudioModule.types.d.ts
+++ b/packages/expo-audio/build/AudioModule.types.d.ts
@@ -1,4 +1,4 @@
-import type { PermissionResponse } from 'expo-modules-core';
+import type { PermissionResponse } from 'expo';
import { NativeModule, SharedObject } from 'expo-modules-core';
import type { AudioMetadata, AudioMode, AudioPlaylistLoopMode, AudioPlaylistStatus, AudioSource, AudioSourceInfo, AudioStatus, PitchCorrectionQuality, RecorderState, RecordingInput, RecordingOptions, RecordingStartOptions, RecordingStatus } from './Audio.types';
import type { AudioLockScreenOptions } from './AudioConstants';
diff --git a/packages/expo-audio/build/AudioModule.types.d.ts.map b/packages/expo-audio/build/AudioModule.types.d.ts.map
index 38fa88f11d003e..761680ff0f9d78 100644
--- a/packages/expo-audio/build/AudioModule.types.d.ts.map
+++ b/packages/expo-audio/build/AudioModule.types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"AudioModule.types.d.ts","sourceRoot":"","sources":["../src/AudioModule.types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"AudioModule.types.d.ts","sourceRoot":"","sources":["../src/AudioModule.types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-audio/build/AudioModule.types.js.map b/packages/expo-audio/build/AudioModule.types.js.map
index 6431dbac52666f..33554a408330c5 100644
--- a/packages/expo-audio/build/AudioModule.types.js.map
+++ b/packages/expo-audio/build/AudioModule.types.js.map
@@ -1 +1 @@
-{"version":3,"file":"AudioModule.types.js","sourceRoot":"","sources":["../src/AudioModule.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["import type { PermissionResponse } from 'expo-modules-core';\nimport { NativeModule, SharedObject } from 'expo-modules-core';\n\nimport type {\n AudioMetadata,\n AudioMode,\n AudioPlaylistLoopMode,\n AudioPlaylistStatus,\n AudioSource,\n AudioSourceInfo,\n AudioStatus,\n PitchCorrectionQuality,\n RecorderState,\n RecordingInput,\n RecordingOptions,\n RecordingStartOptions,\n RecordingStatus,\n} from './Audio.types';\nimport type { AudioLockScreenOptions } from './AudioConstants';\nimport type { AudioStream } from './AudioStream.types';\n\n/**\n * @hidden\n */\nexport declare class NativeAudioModule extends NativeModule {\n setIsAudioActiveAsync(active: boolean): Promise;\n setAudioModeAsync(category: Partial): Promise;\n requestRecordingPermissionsAsync(): Promise;\n requestNotificationPermissionsAsync(): Promise;\n getRecordingPermissionsAsync(): Promise;\n preload(source: AudioSource, preferredForwardBufferDuration: number): Promise;\n clearPreloadedSource(source: AudioSource): Promise;\n clearAllPreloadedSources(): Promise;\n getPreloadedSources(): Promise;\n\n readonly AudioPlayer: typeof AudioPlayer;\n readonly AudioRecorder: typeof AudioRecorder;\n readonly AudioPlaylist: typeof AudioPlaylist;\n readonly AudioStream: typeof AudioStream;\n}\n\nexport declare class AudioPlayer extends SharedObject {\n /**\n * Initializes a new audio player instance with the given source.\n * @hidden\n */\n constructor(\n source: AudioSource,\n updateInterval: number,\n keepAudioSessionActive: boolean,\n preferredForwardBufferDuration: number\n );\n\n /**\n * Unique identifier for the player object.\n */\n id: string;\n\n /**\n * Boolean value indicating whether the player is currently playing.\n */\n playing: boolean;\n\n /**\n * Boolean value indicating whether the player is currently muted.\n */\n muted: boolean;\n\n /**\n * Boolean value indicating whether the player is currently looping.\n */\n loop: boolean;\n\n /**\n * Boolean value indicating whether the player is currently paused.\n */\n paused: boolean;\n\n /**\n * Boolean value indicating whether the player is finished loading.\n */\n isLoaded: boolean;\n\n /**\n * Boolean value indicating whether audio sampling is supported on the platform.\n */\n isAudioSamplingSupported: boolean;\n\n /**\n * Boolean value indicating whether the player is buffering.\n */\n isBuffering: boolean;\n\n /**\n * The current position through the audio item in seconds.\n */\n currentTime: number;\n\n /**\n * The total duration of the audio in seconds.\n */\n duration: number;\n\n /**\n * The current volume of the audio.\n *\n * **Range:** `0.0` to `1.0`. For example, `0.0` is completely silent (0%), `0.5` is half volume (50%), and `1.0` is full volume (100%).\n *\n *\n * @example\n * ```tsx\n * import { useAudioPlayer } from 'expo-audio';\n *\n * export default function App() {\n * const player = useAudioPlayer(source);\n *\n * // Mute the audio\n * player.volume = 0.0;\n *\n * // Set volume to 50%\n * player.volume = 0.5;\n *\n * // Set to full volume\n * player.volume = 1.0;\n * }\n * ```\n */\n volume: number;\n\n /**\n * The current playback rate of the audio. It accepts different values depending on the platform:\n * - **Android**: `0.1` to `2.0`\n * - **iOS**: `0.0` to `2.0`\n * - **Web**: Follows browser implementation\n *\n * @example\n * ```tsx\n * import { useAudioPlayer } from 'expo-audio';\n *\n * export default function App() {\n * const player = useAudioPlayer(source);\n *\n * // Normal playback speed\n * player.playbackRate = 1.0;\n *\n * // Slow motion (half speed)\n * player.playbackRate = 0.5;\n *\n * // Fast playback (1.5x speed)\n * player.playbackRate = 1.5;\n *\n * // Maximum speed on mobile\n * player.playbackRate = 2.0;\n * }\n * ```\n */\n playbackRate: number;\n\n /**\n * A boolean describing if we are correcting the pitch for a changed rate.\n */\n shouldCorrectPitch: boolean;\n\n /**\n * The current status of the audio player.\n * @hidden\n */\n currentStatus: AudioStatus;\n\n /**\n * Start playing audio.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current audio source with a new one.\n */\n replace(source: AudioSource): void;\n\n /**\n * Seeks the playback by the given number of seconds.\n * @param seconds The number of seconds to seek by.\n * @param toleranceMillisBefore The tolerance allowed before the requested seek time, in milliseconds. iOS only.\n * @param toleranceMillisAfter The tolerance allowed after the requested seek time, in milliseconds. iOS only.\n */\n seekTo(\n seconds: number,\n toleranceMillisBefore?: number,\n toleranceMillisAfter?: number\n ): Promise;\n\n /**\n * Sets the current playback rate of the audio.\n *\n * @param rate The playback rate of the audio. See [`playbackRate`](#playbackrate) property for detailed range information.\n * @param pitchCorrectionQuality The quality of the pitch correction.\n */\n setPlaybackRate(rate: number, pitchCorrectionQuality?: PitchCorrectionQuality): void;\n\n /**\n *\n * @hidden\n */\n setAudioSamplingEnabled(enabled: boolean): void;\n\n /**\n * Sets or removes this audio player as the active player for lock screen controls.\n * Only one player can control the lock screen at a time.\n *\n * > **Note:** For lock screen controls to work correctly, [`interruptionMode`](#interruptionmode) must be set to `doNotMix` using [`setAudioModeAsync`](#audiosetaudiomodeasyncmode).\n * > Without this, the OS might not associate lock screen controls with your player.\n *\n * @param active Whether this player should be active for lock screen controls.\n * @param metadata Optional metadata to display on the lock screen (title, artist, album, artwork).\n * @param options Optional configuration to configure the lock screen controls.\n */\n setActiveForLockScreen(\n active: boolean,\n metadata?: AudioMetadata,\n options?: AudioLockScreenOptions\n ): void;\n\n /**\n * Updates the metadata displayed on the lock screen for this player.\n * This method only has an effect if this player is currently active for lock screen controls.\n * @param metadata The metadata to display (title, artist, album, artwork).\n */\n updateLockScreenMetadata(metadata: AudioMetadata): void;\n\n /**\n * Removes this player from lock screen controls if it's currently active.\n * This will clear the lock screen's now playing info.\n */\n clearLockScreenControls(): void;\n\n /**\n * Remove the player from memory to free up resources.\n */\n remove(): void;\n}\n\n/**\n * Represents a single audio sample containing waveform data from all audio channels.\n *\n * Audio samples are provided in real-time when audio sampling is enabled on an `AudioPlayer`.\n * Each sample contains the raw PCM audio data for all channels (mono has 1 channel, stereo has 2).\n * This data can be used for audio visualization, analysis, or processing.\n */\nexport type AudioSample = {\n /** Array of audio channels, each containing PCM frame data. Stereo audio will have 2 channels (left/right). */\n channels: AudioSampleChannel[];\n /** Timestamp of this sample relative to the audio track's timeline, in seconds. */\n timestamp: number;\n};\n\n/**\n * Represents audio data for a single channel (for example, left or right in stereo audio).\n *\n * Contains the raw PCM (Pulse Code Modulation) audio frames for this channel.\n * Frame values are normalized between -1.0 and 1.0, where 0 represents silence.\n */\nexport type AudioSampleChannel = {\n /** Array of PCM audio frame values, each between -1.0 and 1.0. */\n frames: number[];\n};\n\n/**\n * Event types that an `AudioPlayer` can emit.\n *\n * These events allow you to listen for changes in playback state and receive real-time audio data.\n * Use `player.addListener()` to subscribe to these events.\n */\nexport type AudioEvents = {\n /** Fired when the player's status changes (play/pause/seek/load and so on.). */\n playbackStatusUpdate(status: AudioStatus): void;\n /** Fired when audio sampling is enabled and new sample data is available. */\n audioSampleUpdate(data: AudioSample): void;\n};\n\nexport declare class AudioRecorder extends SharedObject {\n /**\n * Initializes a new audio recorder instance with the given source.\n * @hidden\n */\n constructor(options: Partial);\n\n /**\n * Unique identifier for the recorder object.\n */\n id: string;\n\n /**\n * The current length of the recording, in seconds.\n */\n currentTime: number;\n\n /**\n * Boolean value indicating whether the recording is in progress.\n */\n isRecording: boolean;\n\n /**\n * The uri of the recording.\n */\n uri: string | null;\n\n /**\n * Starts the recording.\n * @param options Optional recording configuration options.\n */\n record(options?: RecordingStartOptions): void;\n\n /**\n * Stop the recording.\n */\n stop(): Promise;\n\n /**\n * Pause the recording.\n */\n pause(): void;\n\n /**\n * Returns a list of available recording inputs. This method can only be called if the `Recording` has been prepared.\n * @return A `Promise` that is fulfilled with an array of `RecordingInput` objects.\n */\n getAvailableInputs(): RecordingInput[];\n\n /**\n * Returns the currently-selected recording input. This method can only be called if the `Recording` has been prepared.\n * @return A `Promise` that is fulfilled with a `RecordingInput` object.\n */\n getCurrentInput(): Promise;\n\n /**\n * Sets the current recording input.\n * @param inputUid The uid of a `RecordingInput`.\n * @return A `Promise` that is resolved if successful or rejected if not.\n */\n setInput(inputUid: string): void;\n\n /**\n * Status of the current recording.\n */\n getStatus(): RecorderState;\n\n /**\n * Starts the recording at the given time.\n * @param seconds The time in seconds to start recording at.\n * @deprecated Use `record({ atTime: seconds })` instead.\n */\n startRecordingAtTime(seconds: number): void;\n\n /**\n * Prepares the recording for recording.\n */\n prepareToRecordAsync(options?: Partial): Promise;\n\n /**\n * Stops the recording once the specified time has elapsed.\n * @param seconds The time in seconds to stop recording at.\n * @deprecated Use `record({ forDuration: seconds })` instead.\n */\n recordForDuration(seconds: number): void;\n}\n\n/**\n * Event types that an `AudioRecorder` can emit.\n *\n * These events are used internally by `expo-audio` hooks to provide real-time status updates.\n * Use `useAudioRecorderState()` or the `statusListener` parameter in `useAudioRecorder()` instead of subscribing directly.\n */\nexport type RecordingEvents = {\n /** Fired when the recorder's status changes (start/stop/pause/error, and so on). */\n recordingStatusUpdate: (status: RecordingStatus) => void;\n};\n\n/**\n * Event types that an `AudioPlaylist` can emit.\n *\n * These events allow you to listen for changes in playlist playback state.\n * Use `playlist.addListener()` to subscribe to these events.\n */\nexport type AudioPlaylistEvents = {\n /** Fired when the playlist's status changes (play/pause/seek/load/track change). */\n playlistStatusUpdate(status: AudioPlaylistStatus): void;\n /** Fired when the current track changes (next/previous/skip). */\n trackChanged(data: { previousIndex: number; currentIndex: number }): void;\n};\n\nexport declare class AudioPlaylist extends SharedObject {\n /**\n * Initializes a new audio playlist instance.\n * @hidden\n */\n constructor(sources: AudioSource[], updateInterval: number, loop: AudioPlaylistLoopMode);\n\n /**\n * Unique identifier for the playlist instance.\n */\n id: string;\n\n /**\n * Index of the currently playing track in the playlist.\n */\n readonly currentIndex: number;\n\n /**\n * Total number of tracks in the playlist.\n */\n readonly trackCount: number;\n\n /**\n * The audio sources currently in the playlist.\n */\n readonly sources: AudioSourceInfo[];\n\n /**\n * Boolean value indicating whether the playlist is currently playing.\n */\n playing: boolean;\n\n /**\n * Boolean value indicating whether the playlist is currently muted.\n */\n muted: boolean;\n\n /**\n * Boolean value indicating whether the current track has finished loading.\n */\n isLoaded: boolean;\n\n /**\n * Boolean value indicating whether the playlist is buffering.\n */\n isBuffering: boolean;\n\n /**\n * Current playback position in seconds.\n */\n currentTime: number;\n\n /**\n * Duration of the current track in seconds.\n */\n duration: number;\n\n /**\n * Current volume (0.0 to 1.0).\n */\n volume: number;\n\n /**\n * Current playback rate (1.0 = normal speed).\n */\n playbackRate: number;\n\n /**\n * Current loop mode.\n */\n loop: AudioPlaylistLoopMode;\n\n /**\n * The current status of the audio playlist.\n * @hidden\n */\n currentStatus: AudioPlaylistStatus;\n\n /**\n * Start playing the current track in the playlist.\n */\n play(): void;\n\n /**\n * Pause playback.\n */\n pause(): void;\n\n /**\n * Skip to the next track in the playlist.\n * If at the end of the playlist and loop mode is 'all', wraps to the first track.\n * If loop mode is 'none' and at the end, does nothing.\n */\n next(): void;\n\n /**\n * Skip to the previous track in the playlist.\n * If at the beginning of the playlist and loop mode is 'all', wraps to the last track.\n * If loop mode is 'none' and at the beginning, does nothing.\n */\n previous(): void;\n\n /**\n * Skip to a specific track in the playlist by index.\n * @param index The index of the track to skip to.\n */\n skipTo(index: number): void;\n\n /**\n * Seeks the playback to a specific position in seconds.\n * @param seconds The position to seek to.\n */\n seekTo(seconds: number): Promise;\n\n /**\n * Add a track to the end of the playlist.\n * @param source The audio source to add.\n */\n add(source: AudioSource): void;\n\n /**\n * Insert a track at a specific position in the playlist.\n * @param source The audio source to insert.\n * @param index The position to insert at.\n */\n insert(source: AudioSource, index: number): void;\n\n /**\n * Remove a track from the playlist by index.\n * @param index The index of the track to remove.\n */\n remove(index: number): void;\n\n /**\n * Clear all tracks from the playlist.\n */\n clear(): void;\n\n /**\n * Destroy the playlist and free up resources.\n */\n destroy(): void;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"AudioModule.types.js","sourceRoot":"","sources":["../src/AudioModule.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["import type { PermissionResponse } from 'expo';\nimport { NativeModule, SharedObject } from 'expo-modules-core';\n\nimport type {\n AudioMetadata,\n AudioMode,\n AudioPlaylistLoopMode,\n AudioPlaylistStatus,\n AudioSource,\n AudioSourceInfo,\n AudioStatus,\n PitchCorrectionQuality,\n RecorderState,\n RecordingInput,\n RecordingOptions,\n RecordingStartOptions,\n RecordingStatus,\n} from './Audio.types';\nimport type { AudioLockScreenOptions } from './AudioConstants';\nimport type { AudioStream } from './AudioStream.types';\n\n/**\n * @hidden\n */\nexport declare class NativeAudioModule extends NativeModule {\n setIsAudioActiveAsync(active: boolean): Promise;\n setAudioModeAsync(category: Partial): Promise;\n requestRecordingPermissionsAsync(): Promise;\n requestNotificationPermissionsAsync(): Promise;\n getRecordingPermissionsAsync(): Promise;\n preload(source: AudioSource, preferredForwardBufferDuration: number): Promise;\n clearPreloadedSource(source: AudioSource): Promise;\n clearAllPreloadedSources(): Promise;\n getPreloadedSources(): Promise;\n\n readonly AudioPlayer: typeof AudioPlayer;\n readonly AudioRecorder: typeof AudioRecorder;\n readonly AudioPlaylist: typeof AudioPlaylist;\n readonly AudioStream: typeof AudioStream;\n}\n\nexport declare class AudioPlayer extends SharedObject {\n /**\n * Initializes a new audio player instance with the given source.\n * @hidden\n */\n constructor(\n source: AudioSource,\n updateInterval: number,\n keepAudioSessionActive: boolean,\n preferredForwardBufferDuration: number\n );\n\n /**\n * Unique identifier for the player object.\n */\n id: string;\n\n /**\n * Boolean value indicating whether the player is currently playing.\n */\n playing: boolean;\n\n /**\n * Boolean value indicating whether the player is currently muted.\n */\n muted: boolean;\n\n /**\n * Boolean value indicating whether the player is currently looping.\n */\n loop: boolean;\n\n /**\n * Boolean value indicating whether the player is currently paused.\n */\n paused: boolean;\n\n /**\n * Boolean value indicating whether the player is finished loading.\n */\n isLoaded: boolean;\n\n /**\n * Boolean value indicating whether audio sampling is supported on the platform.\n */\n isAudioSamplingSupported: boolean;\n\n /**\n * Boolean value indicating whether the player is buffering.\n */\n isBuffering: boolean;\n\n /**\n * The current position through the audio item in seconds.\n */\n currentTime: number;\n\n /**\n * The total duration of the audio in seconds.\n */\n duration: number;\n\n /**\n * The current volume of the audio.\n *\n * **Range:** `0.0` to `1.0`. For example, `0.0` is completely silent (0%), `0.5` is half volume (50%), and `1.0` is full volume (100%).\n *\n *\n * @example\n * ```tsx\n * import { useAudioPlayer } from 'expo-audio';\n *\n * export default function App() {\n * const player = useAudioPlayer(source);\n *\n * // Mute the audio\n * player.volume = 0.0;\n *\n * // Set volume to 50%\n * player.volume = 0.5;\n *\n * // Set to full volume\n * player.volume = 1.0;\n * }\n * ```\n */\n volume: number;\n\n /**\n * The current playback rate of the audio. It accepts different values depending on the platform:\n * - **Android**: `0.1` to `2.0`\n * - **iOS**: `0.0` to `2.0`\n * - **Web**: Follows browser implementation\n *\n * @example\n * ```tsx\n * import { useAudioPlayer } from 'expo-audio';\n *\n * export default function App() {\n * const player = useAudioPlayer(source);\n *\n * // Normal playback speed\n * player.playbackRate = 1.0;\n *\n * // Slow motion (half speed)\n * player.playbackRate = 0.5;\n *\n * // Fast playback (1.5x speed)\n * player.playbackRate = 1.5;\n *\n * // Maximum speed on mobile\n * player.playbackRate = 2.0;\n * }\n * ```\n */\n playbackRate: number;\n\n /**\n * A boolean describing if we are correcting the pitch for a changed rate.\n */\n shouldCorrectPitch: boolean;\n\n /**\n * The current status of the audio player.\n * @hidden\n */\n currentStatus: AudioStatus;\n\n /**\n * Start playing audio.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current audio source with a new one.\n */\n replace(source: AudioSource): void;\n\n /**\n * Seeks the playback by the given number of seconds.\n * @param seconds The number of seconds to seek by.\n * @param toleranceMillisBefore The tolerance allowed before the requested seek time, in milliseconds. iOS only.\n * @param toleranceMillisAfter The tolerance allowed after the requested seek time, in milliseconds. iOS only.\n */\n seekTo(\n seconds: number,\n toleranceMillisBefore?: number,\n toleranceMillisAfter?: number\n ): Promise;\n\n /**\n * Sets the current playback rate of the audio.\n *\n * @param rate The playback rate of the audio. See [`playbackRate`](#playbackrate) property for detailed range information.\n * @param pitchCorrectionQuality The quality of the pitch correction.\n */\n setPlaybackRate(rate: number, pitchCorrectionQuality?: PitchCorrectionQuality): void;\n\n /**\n *\n * @hidden\n */\n setAudioSamplingEnabled(enabled: boolean): void;\n\n /**\n * Sets or removes this audio player as the active player for lock screen controls.\n * Only one player can control the lock screen at a time.\n *\n * > **Note:** For lock screen controls to work correctly, [`interruptionMode`](#interruptionmode) must be set to `doNotMix` using [`setAudioModeAsync`](#audiosetaudiomodeasyncmode).\n * > Without this, the OS might not associate lock screen controls with your player.\n *\n * @param active Whether this player should be active for lock screen controls.\n * @param metadata Optional metadata to display on the lock screen (title, artist, album, artwork).\n * @param options Optional configuration to configure the lock screen controls.\n */\n setActiveForLockScreen(\n active: boolean,\n metadata?: AudioMetadata,\n options?: AudioLockScreenOptions\n ): void;\n\n /**\n * Updates the metadata displayed on the lock screen for this player.\n * This method only has an effect if this player is currently active for lock screen controls.\n * @param metadata The metadata to display (title, artist, album, artwork).\n */\n updateLockScreenMetadata(metadata: AudioMetadata): void;\n\n /**\n * Removes this player from lock screen controls if it's currently active.\n * This will clear the lock screen's now playing info.\n */\n clearLockScreenControls(): void;\n\n /**\n * Remove the player from memory to free up resources.\n */\n remove(): void;\n}\n\n/**\n * Represents a single audio sample containing waveform data from all audio channels.\n *\n * Audio samples are provided in real-time when audio sampling is enabled on an `AudioPlayer`.\n * Each sample contains the raw PCM audio data for all channels (mono has 1 channel, stereo has 2).\n * This data can be used for audio visualization, analysis, or processing.\n */\nexport type AudioSample = {\n /** Array of audio channels, each containing PCM frame data. Stereo audio will have 2 channels (left/right). */\n channels: AudioSampleChannel[];\n /** Timestamp of this sample relative to the audio track's timeline, in seconds. */\n timestamp: number;\n};\n\n/**\n * Represents audio data for a single channel (for example, left or right in stereo audio).\n *\n * Contains the raw PCM (Pulse Code Modulation) audio frames for this channel.\n * Frame values are normalized between -1.0 and 1.0, where 0 represents silence.\n */\nexport type AudioSampleChannel = {\n /** Array of PCM audio frame values, each between -1.0 and 1.0. */\n frames: number[];\n};\n\n/**\n * Event types that an `AudioPlayer` can emit.\n *\n * These events allow you to listen for changes in playback state and receive real-time audio data.\n * Use `player.addListener()` to subscribe to these events.\n */\nexport type AudioEvents = {\n /** Fired when the player's status changes (play/pause/seek/load and so on.). */\n playbackStatusUpdate(status: AudioStatus): void;\n /** Fired when audio sampling is enabled and new sample data is available. */\n audioSampleUpdate(data: AudioSample): void;\n};\n\nexport declare class AudioRecorder extends SharedObject {\n /**\n * Initializes a new audio recorder instance with the given source.\n * @hidden\n */\n constructor(options: Partial);\n\n /**\n * Unique identifier for the recorder object.\n */\n id: string;\n\n /**\n * The current length of the recording, in seconds.\n */\n currentTime: number;\n\n /**\n * Boolean value indicating whether the recording is in progress.\n */\n isRecording: boolean;\n\n /**\n * The uri of the recording.\n */\n uri: string | null;\n\n /**\n * Starts the recording.\n * @param options Optional recording configuration options.\n */\n record(options?: RecordingStartOptions): void;\n\n /**\n * Stop the recording.\n */\n stop(): Promise;\n\n /**\n * Pause the recording.\n */\n pause(): void;\n\n /**\n * Returns a list of available recording inputs. This method can only be called if the `Recording` has been prepared.\n * @return A `Promise` that is fulfilled with an array of `RecordingInput` objects.\n */\n getAvailableInputs(): RecordingInput[];\n\n /**\n * Returns the currently-selected recording input. This method can only be called if the `Recording` has been prepared.\n * @return A `Promise` that is fulfilled with a `RecordingInput` object.\n */\n getCurrentInput(): Promise;\n\n /**\n * Sets the current recording input.\n * @param inputUid The uid of a `RecordingInput`.\n * @return A `Promise` that is resolved if successful or rejected if not.\n */\n setInput(inputUid: string): void;\n\n /**\n * Status of the current recording.\n */\n getStatus(): RecorderState;\n\n /**\n * Starts the recording at the given time.\n * @param seconds The time in seconds to start recording at.\n * @deprecated Use `record({ atTime: seconds })` instead.\n */\n startRecordingAtTime(seconds: number): void;\n\n /**\n * Prepares the recording for recording.\n */\n prepareToRecordAsync(options?: Partial): Promise;\n\n /**\n * Stops the recording once the specified time has elapsed.\n * @param seconds The time in seconds to stop recording at.\n * @deprecated Use `record({ forDuration: seconds })` instead.\n */\n recordForDuration(seconds: number): void;\n}\n\n/**\n * Event types that an `AudioRecorder` can emit.\n *\n * These events are used internally by `expo-audio` hooks to provide real-time status updates.\n * Use `useAudioRecorderState()` or the `statusListener` parameter in `useAudioRecorder()` instead of subscribing directly.\n */\nexport type RecordingEvents = {\n /** Fired when the recorder's status changes (start/stop/pause/error, and so on). */\n recordingStatusUpdate: (status: RecordingStatus) => void;\n};\n\n/**\n * Event types that an `AudioPlaylist` can emit.\n *\n * These events allow you to listen for changes in playlist playback state.\n * Use `playlist.addListener()` to subscribe to these events.\n */\nexport type AudioPlaylistEvents = {\n /** Fired when the playlist's status changes (play/pause/seek/load/track change). */\n playlistStatusUpdate(status: AudioPlaylistStatus): void;\n /** Fired when the current track changes (next/previous/skip). */\n trackChanged(data: { previousIndex: number; currentIndex: number }): void;\n};\n\nexport declare class AudioPlaylist extends SharedObject {\n /**\n * Initializes a new audio playlist instance.\n * @hidden\n */\n constructor(sources: AudioSource[], updateInterval: number, loop: AudioPlaylistLoopMode);\n\n /**\n * Unique identifier for the playlist instance.\n */\n id: string;\n\n /**\n * Index of the currently playing track in the playlist.\n */\n readonly currentIndex: number;\n\n /**\n * Total number of tracks in the playlist.\n */\n readonly trackCount: number;\n\n /**\n * The audio sources currently in the playlist.\n */\n readonly sources: AudioSourceInfo[];\n\n /**\n * Boolean value indicating whether the playlist is currently playing.\n */\n playing: boolean;\n\n /**\n * Boolean value indicating whether the playlist is currently muted.\n */\n muted: boolean;\n\n /**\n * Boolean value indicating whether the current track has finished loading.\n */\n isLoaded: boolean;\n\n /**\n * Boolean value indicating whether the playlist is buffering.\n */\n isBuffering: boolean;\n\n /**\n * Current playback position in seconds.\n */\n currentTime: number;\n\n /**\n * Duration of the current track in seconds.\n */\n duration: number;\n\n /**\n * Current volume (0.0 to 1.0).\n */\n volume: number;\n\n /**\n * Current playback rate (1.0 = normal speed).\n */\n playbackRate: number;\n\n /**\n * Current loop mode.\n */\n loop: AudioPlaylistLoopMode;\n\n /**\n * The current status of the audio playlist.\n * @hidden\n */\n currentStatus: AudioPlaylistStatus;\n\n /**\n * Start playing the current track in the playlist.\n */\n play(): void;\n\n /**\n * Pause playback.\n */\n pause(): void;\n\n /**\n * Skip to the next track in the playlist.\n * If at the end of the playlist and loop mode is 'all', wraps to the first track.\n * If loop mode is 'none' and at the end, does nothing.\n */\n next(): void;\n\n /**\n * Skip to the previous track in the playlist.\n * If at the beginning of the playlist and loop mode is 'all', wraps to the last track.\n * If loop mode is 'none' and at the beginning, does nothing.\n */\n previous(): void;\n\n /**\n * Skip to a specific track in the playlist by index.\n * @param index The index of the track to skip to.\n */\n skipTo(index: number): void;\n\n /**\n * Seeks the playback to a specific position in seconds.\n * @param seconds The position to seek to.\n */\n seekTo(seconds: number): Promise;\n\n /**\n * Add a track to the end of the playlist.\n * @param source The audio source to add.\n */\n add(source: AudioSource): void;\n\n /**\n * Insert a track at a specific position in the playlist.\n * @param source The audio source to insert.\n * @param index The position to insert at.\n */\n insert(source: AudioSource, index: number): void;\n\n /**\n * Remove a track from the playlist by index.\n * @param index The index of the track to remove.\n */\n remove(index: number): void;\n\n /**\n * Clear all tracks from the playlist.\n */\n clear(): void;\n\n /**\n * Destroy the playlist and free up resources.\n */\n destroy(): void;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-audio/build/AudioModule.web.d.ts b/packages/expo-audio/build/AudioModule.web.d.ts
index 168d4e49a88f2c..0cc7fbc6775d8a 100644
--- a/packages/expo-audio/build/AudioModule.web.d.ts
+++ b/packages/expo-audio/build/AudioModule.web.d.ts
@@ -1,4 +1,4 @@
-import type { PermissionResponse } from 'expo-modules-core';
+import { type PermissionResponse } from 'expo';
import type { AudioMode, AudioSource } from './Audio.types';
export { AudioPlayerWeb } from './AudioPlayer.web';
export { AudioPlaylistWeb } from './AudioPlaylist.web';
diff --git a/packages/expo-audio/build/AudioModule.web.d.ts.map b/packages/expo-audio/build/AudioModule.web.d.ts.map
index a10d93b6930672..a95680c68c461c 100644
--- a/packages/expo-audio/build/AudioModule.web.d.ts.map
+++ b/packages/expo-audio/build/AudioModule.web.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"AudioModule.web.d.ts","sourceRoot":"","sources":["../src/AudioModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAI5D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,IAAI,aAAa,SAAO,CAAC;AAuBhC,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,SAAS,iBAAI;AAC3D,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,OAAO,iBAS1D;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAarE;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAEjD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAS9D;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAoBhF;AAED,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAkBpF"}
\ No newline at end of file
+{"version":3,"file":"AudioModule.web.d.ts","sourceRoot":"","sources":["../src/AudioModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAoB,MAAM,MAAM,CAAC;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAI5D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,IAAI,aAAa,SAAO,CAAC;AAuBhC,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,SAAS,iBAAI;AAC3D,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,OAAO,iBAS1D;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAarE;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAEjD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAS9D;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAoBhF;AAED,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAkBpF"}
\ No newline at end of file
diff --git a/packages/expo-audio/build/AudioModule.web.js b/packages/expo-audio/build/AudioModule.web.js
index d8252d46f57867..01c49937d85e3a 100644
--- a/packages/expo-audio/build/AudioModule.web.js
+++ b/packages/expo-audio/build/AudioModule.web.js
@@ -1,4 +1,4 @@
-import { PermissionStatus } from 'expo-modules-core';
+import { PermissionStatus } from 'expo';
import { activePlayers } from './AudioPlayer.web';
import { getUserMedia, getSourceUri, preloadCache } from './AudioUtils.web';
export { AudioPlayerWeb } from './AudioPlayer.web';
diff --git a/packages/expo-audio/build/AudioModule.web.js.map b/packages/expo-audio/build/AudioModule.web.js.map
index f38dcf9ef399b1..74bb8e8b8c9965 100644
--- a/packages/expo-audio/build/AudioModule.web.js.map
+++ b/packages/expo-audio/build/AudioModule.web.js.map
@@ -1 +1 @@
-{"version":3,"file":"AudioModule.web.js","sourceRoot":"","sources":["../src/AudioModule.web.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PermissionResponse } from 'expo-modules-core';\nimport { PermissionStatus } from 'expo-modules-core';\n\nimport type { AudioMode, AudioSource } from './Audio.types';\nimport { activePlayers } from './AudioPlayer.web';\nimport { getUserMedia, getSourceUri, preloadCache } from './AudioUtils.web';\n\nexport { AudioPlayerWeb } from './AudioPlayer.web';\nexport { AudioPlaylistWeb } from './AudioPlaylist.web';\nexport { AudioRecorderWeb } from './AudioRecorder.web';\n\nexport let isAudioActive = true;\n\nasync function getPermissionWithQueryAsync(\n name: PermissionNameWithAdditionalValues\n): Promise {\n if (!navigator || !navigator.permissions || !navigator.permissions.query) return null;\n\n try {\n const { state } = await navigator.permissions.query({ name });\n switch (state) {\n case 'granted':\n return PermissionStatus.GRANTED;\n case 'denied':\n return PermissionStatus.DENIED;\n default:\n return PermissionStatus.UNDETERMINED;\n }\n } catch {\n // Firefox - TypeError: 'microphone' (value of 'name' member of PermissionDescriptor) is not a valid value for enumeration PermissionName.\n return PermissionStatus.UNDETERMINED;\n }\n}\n\nexport async function setAudioModeAsync(mode: AudioMode) {}\nexport async function setIsAudioActiveAsync(active: boolean) {\n isAudioActive = active;\n if (!active) {\n for (const player of activePlayers) {\n if (player.playing) {\n player.pause();\n }\n }\n }\n}\n\nexport async function preloadAsync(source: AudioSource): Promise {\n const uri = getSourceUri(source);\n if (!uri || preloadCache.has(uri)) return;\n\n const headers =\n source && typeof source === 'object' && !Array.isArray(source) ? source.headers : undefined;\n\n const response = await fetch(uri, headers ? { headers } : undefined);\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n const audio = new Audio(blobUrl);\n audio.preload = 'auto';\n preloadCache.set(uri, { blobUrl, audio });\n}\n\nexport function preload(source: AudioSource): void {\n preloadAsync(source).catch(() => {});\n}\n\nexport function clearPreloadedSource(source: AudioSource): void {\n const uri = getSourceUri(source);\n if (!uri) return;\n\n const cached = preloadCache.get(uri);\n if (cached) {\n URL.revokeObjectURL(cached.blobUrl);\n preloadCache.delete(uri);\n }\n}\n\nexport function clearAllPreloadedSources(): void {\n for (const cached of preloadCache.values()) {\n URL.revokeObjectURL(cached.blobUrl);\n }\n preloadCache.clear();\n}\n\nexport function getPreloadedSources(): string[] {\n return Array.from(preloadCache.keys());\n}\n\nexport async function getRecordingPermissionsAsync(): Promise {\n const maybeStatus = await getPermissionWithQueryAsync('microphone');\n switch (maybeStatus) {\n case PermissionStatus.GRANTED:\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n case PermissionStatus.DENIED:\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n default:\n return await requestRecordingPermissionsAsync();\n }\n}\n\nexport async function requestRecordingPermissionsAsync(): Promise {\n try {\n const stream = await getUserMedia({ audio: true });\n stream.getTracks().forEach((track) => track.stop());\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n } catch {\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n }\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"AudioModule.web.js","sourceRoot":"","sources":["../src/AudioModule.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { type PermissionResponse, PermissionStatus } from 'expo';\n\nimport type { AudioMode, AudioSource } from './Audio.types';\nimport { activePlayers } from './AudioPlayer.web';\nimport { getUserMedia, getSourceUri, preloadCache } from './AudioUtils.web';\n\nexport { AudioPlayerWeb } from './AudioPlayer.web';\nexport { AudioPlaylistWeb } from './AudioPlaylist.web';\nexport { AudioRecorderWeb } from './AudioRecorder.web';\n\nexport let isAudioActive = true;\n\nasync function getPermissionWithQueryAsync(\n name: PermissionNameWithAdditionalValues\n): Promise {\n if (!navigator || !navigator.permissions || !navigator.permissions.query) return null;\n\n try {\n const { state } = await navigator.permissions.query({ name });\n switch (state) {\n case 'granted':\n return PermissionStatus.GRANTED;\n case 'denied':\n return PermissionStatus.DENIED;\n default:\n return PermissionStatus.UNDETERMINED;\n }\n } catch {\n // Firefox - TypeError: 'microphone' (value of 'name' member of PermissionDescriptor) is not a valid value for enumeration PermissionName.\n return PermissionStatus.UNDETERMINED;\n }\n}\n\nexport async function setAudioModeAsync(mode: AudioMode) {}\nexport async function setIsAudioActiveAsync(active: boolean) {\n isAudioActive = active;\n if (!active) {\n for (const player of activePlayers) {\n if (player.playing) {\n player.pause();\n }\n }\n }\n}\n\nexport async function preloadAsync(source: AudioSource): Promise {\n const uri = getSourceUri(source);\n if (!uri || preloadCache.has(uri)) return;\n\n const headers =\n source && typeof source === 'object' && !Array.isArray(source) ? source.headers : undefined;\n\n const response = await fetch(uri, headers ? { headers } : undefined);\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n const audio = new Audio(blobUrl);\n audio.preload = 'auto';\n preloadCache.set(uri, { blobUrl, audio });\n}\n\nexport function preload(source: AudioSource): void {\n preloadAsync(source).catch(() => {});\n}\n\nexport function clearPreloadedSource(source: AudioSource): void {\n const uri = getSourceUri(source);\n if (!uri) return;\n\n const cached = preloadCache.get(uri);\n if (cached) {\n URL.revokeObjectURL(cached.blobUrl);\n preloadCache.delete(uri);\n }\n}\n\nexport function clearAllPreloadedSources(): void {\n for (const cached of preloadCache.values()) {\n URL.revokeObjectURL(cached.blobUrl);\n }\n preloadCache.clear();\n}\n\nexport function getPreloadedSources(): string[] {\n return Array.from(preloadCache.keys());\n}\n\nexport async function getRecordingPermissionsAsync(): Promise {\n const maybeStatus = await getPermissionWithQueryAsync('microphone');\n switch (maybeStatus) {\n case PermissionStatus.GRANTED:\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n case PermissionStatus.DENIED:\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n default:\n return await requestRecordingPermissionsAsync();\n }\n}\n\nexport async function requestRecordingPermissionsAsync(): Promise {\n try {\n const stream = await getUserMedia({ audio: true });\n stream.getTracks().forEach((track) => track.stop());\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n } catch {\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n }\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-audio/build/ExpoAudio.d.ts b/packages/expo-audio/build/ExpoAudio.d.ts
index d2f2198aba8d94..0e753738f8e429 100644
--- a/packages/expo-audio/build/ExpoAudio.d.ts
+++ b/packages/expo-audio/build/ExpoAudio.d.ts
@@ -1,4 +1,4 @@
-import type { PermissionResponse } from 'expo-modules-core';
+import { type PermissionResponse } from 'expo';
import type { AudioMode, AudioPlayerOptions, AudioPlaylistOptions, AudioPlaylistStatus, AudioSource, AudioStatus, PreloadOptions, RecordingOptions, RecordingStatus } from './Audio.types';
import AudioModule from './AudioModule';
import type { AudioPlayer, AudioPlaylist, AudioRecorder, AudioSample } from './AudioModule.types';
diff --git a/packages/expo-audio/build/ExpoAudio.d.ts.map b/packages/expo-audio/build/ExpoAudio.d.ts.map
index 78633a9a03d407..850833183cd006 100644
--- a/packages/expo-audio/build/ExpoAudio.d.ts.map
+++ b/packages/expo-audio/build/ExpoAudio.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"ExpoAudio.d.ts","sourceRoot":"","sources":["../src/ExpoAudio.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"ExpoAudio.d.ts","sourceRoot":"","sources":["../src/ExpoAudio.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-audio/build/ExpoAudio.js.map b/packages/expo-audio/build/ExpoAudio.js.map
index 39aac77cb32722..22ff33034b1512 100644
--- a/packages/expo-audio/build/ExpoAudio.js.map
+++ b/packages/expo-audio/build/ExpoAudio.js.map
@@ -1 +1 @@
-{"version":3,"file":"ExpoAudio.js","sourceRoot":"","sources":["../src/ExpoAudio.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEvent } from 'expo';\nimport type { PermissionResponse } from 'expo-modules-core';\nimport { useReleasingSharedObject } from 'expo-modules-core';\nimport { useEffect, useMemo } from 'react';\nimport { Platform } from 'react-native';\n\nimport type {\n AudioMode,\n AudioPlayerOptions,\n AudioPlaylistOptions,\n AudioPlaylistStatus,\n AudioSource,\n AudioStatus,\n PitchCorrectionQuality,\n PreloadOptions,\n RecordingOptions,\n RecordingStatus,\n} from './Audio.types';\nimport {\n AUDIO_SAMPLE_UPDATE,\n PLAYBACK_STATUS_UPDATE,\n PLAYLIST_STATUS_UPDATE,\n RECORDING_STATUS_UPDATE,\n} from './AudioEventKeys';\nimport AudioModule from './AudioModule';\nimport type { AudioPlayer, AudioPlaylist, AudioRecorder, AudioSample } from './AudioModule.types';\nimport { createRecordingOptions } from './utils/options';\nimport { resolveSource, resolveSources, resolveSourceWithDownload } from './utils/resolveSource';\n\n// TODO: Temporary solution until we develop a way of overriding prototypes that won't break the lazy loading of the module.\nconst replace = AudioModule.AudioPlayer.prototype.replace;\nAudioModule.AudioPlayer.prototype.replace = function (source: AudioSource) {\n return replace.call(this, resolveSource(source));\n};\n\nconst setPlaybackRate = AudioModule.AudioPlayer.prototype.setPlaybackRate;\nAudioModule.AudioPlayer.prototype.setPlaybackRate = function (\n rate: number,\n pitchCorrectionQuality?: PitchCorrectionQuality\n) {\n if (Platform.OS === 'android') {\n return setPlaybackRate.call(this, rate);\n } else {\n return setPlaybackRate.call(this, rate, pitchCorrectionQuality);\n }\n};\n\n// Audio recording prototypes should not be shimmed on tvOS, where they do not exist\nif (!Platform.isTV || Platform.OS !== 'ios') {\n const prepareToRecordAsync = AudioModule.AudioRecorder.prototype.prepareToRecordAsync;\n AudioModule.AudioRecorder.prototype.prepareToRecordAsync = function (options?: RecordingOptions) {\n const processedOptions = options ? createRecordingOptions(options) : undefined;\n return prepareToRecordAsync.call(this, processedOptions);\n };\n}\n\n/**\n * Creates an `AudioPlayer` instance that automatically releases when the component unmounts.\n *\n * This hook manages the player's lifecycle and ensures it's properly disposed when no longer needed.\n * The player will start loading the audio source immediately upon creation.\n *\n * @param source The audio source to load. Can be a local asset via `require()`, a remote URL, or null for no initial source.\n * @param options Audio player configuration options.\n * @returns An `AudioPlayer` instance that's automatically managed by the component lifecycle.\n *\n * @example\n * ```tsx\n * import { useAudioPlayer } from 'expo-audio';\n *\n * function MyComponent() {\n * const player = useAudioPlayer(require('./sound.mp3'));\n *\n * return (\n *