grafana / grafana/faro-react-native-sdk
config.app name/version/environment dropped: RN SDK's installationId meta wins faro-core's shallow meta merge
- Dominant language
- TypeScript
- Stars
- 9
- Forks
- 4
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 9
Description
## Summary
With `@grafana/faro-react-native` 1.3.1 and default mobile metas, `meta.app.name`, `meta.app.version` and `meta.app.environment` from `config.app` are dropped before the first signal is sent. Only `meta.app.installationId` survives.
## Cause
`faro-core` builds the meta object by reducing the metas list with a shallow `Object.assign`:
```js
// @grafana/faro-core/dist/cjs/metas/initialize.js
const getValue = () => items.reduce((acc, item) => Object.assign(acc, isFunction(item) ? item() : item), {});
```
so a later `{ app: {...} }` replaces an earlier one rather than merging into it.
`makeRNConfig` registers `config.app` via core's `registerInitialMetas`, and then the preloaded mobile meta from `loadMobileMetaForInit` contributes `{ app: { installationId } }` later in the list (`sessionAttributes.ts` around lines 286–317). The second entry wins, and the app identity is gone.
## Reproduction
Against real `@grafana/faro-core` 2.11.0, mirroring the meta order `makeRNConfig` produces:
```ts
const faro = initializeFaro({
app: { name: 'my-app', version: '1', environment: 'staging' },
metas: [{ app: { installationId: 'abc-123' } }],
transports: [], instrumentations: [], isolate: true, preventGlobalExposure: true,
});
faro.metas.value.app // => { installationId: 'abc-123' }
```
Observed live: every payload from a production Android device reaches a self-hosted Alloy `faro.receiver` with `session_attr_*`, `view_name`, `page_url`, `sdk_*` and `browser_mobile` fields, and **no `app_name` / `app_version` / `app_environment`**. A web app using `@grafana/faro-web-sdk` against the same receiver emits all three.
## Impact
Any receiver-side routing that keys on `app.name` (the standard way to separate apps and environments on a shared collector) sees no app identity from RN clients at all. On our receiver every mobile signal lands in a quarantine stream.
## Suggested fix
In `makeRNConfig`, merge `preloadedAppMeta` into the returned `app` object instead of appending it as a separate meta item, so there is only ever one `app` entry in the list:
```ts
return {
app: { ...config.app, ...preloadedAppMeta, ...(installationId && { installationId }) },
metas: [...defaultMetas, ...customMetas], // drop appMetasIfPresent
...
}
```
Alternatively, `faro-core` could deep-merge the `app` key specifically, since it is the one meta every SDK contributes to.
## Workaround we're shipping
After `initializeFaro` resolves:
```ts
faro.metas.add({ app: { ...faro.metas.value.app, ...configApp } });
```
Last meta wins the shallow merge, and `installationId` is preserved by the spread.
## Versions
- `@grafana/faro-react-native` 1.3.1
- `@grafana/faro-core` 2.11.0
- React Native 0.81.5, Expo SDK 54, Hermes
Contributor guide
Research direction
Start in makeRNConfig and inspect sessionAttributes.ts around lines 286–317, where loadMobileMetaForInit contributes the installationId metadata. Reproduce the reported initializeFaro metadata order, then verify the resulting app metadata retains config.app name, version, and environment alongside installationId. Confirm the existing workaround behavior and add or run the relevant SDK tests if available.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, typescript
- Domain
- mobile-dev, observability-sre
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100