expo-observe 57.0.9 → 57.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/ObserveErrorBoundary.d.ts +2 -0
  3. package/build/ObserveErrorBoundary.d.ts.map +1 -0
  4. package/build/index.d.ts +2 -0
  5. package/build/index.d.ts.map +1 -1
  6. package/build/integrations/expo-router/storage.d.ts +1 -1
  7. package/build/module.d.ts +2 -1
  8. package/build/module.d.ts.map +1 -1
  9. package/build/module.web.d.ts +2 -0
  10. package/build/module.web.d.ts.map +1 -1
  11. package/build/reportCaughtError.d.ts +9 -0
  12. package/build/reportCaughtError.d.ts.map +1 -0
  13. package/build/types.d.ts +34 -0
  14. package/build/types.d.ts.map +1 -1
  15. package/expo-module.config.json +1 -1
  16. package/ios/OpenTelemetry.swift +2 -4
  17. package/ios/Tests/OpenTelemetryTests.swift +25 -0
  18. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.9/expo.modules.observe-57.0.9-sources.jar → 57.0.11/expo.modules.observe-57.0.11-sources.jar} +0 -0
  19. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11-sources.jar.md5 +1 -0
  20. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11-sources.jar.sha1 +1 -0
  21. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11-sources.jar.sha256 +1 -0
  22. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11-sources.jar.sha512 +1 -0
  23. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.9/expo.modules.observe-57.0.9.aar → 57.0.11/expo.modules.observe-57.0.11.aar} +0 -0
  24. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.aar.md5 +1 -0
  25. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.aar.sha1 +1 -0
  26. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.aar.sha256 +1 -0
  27. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.aar.sha512 +1 -0
  28. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.9/expo.modules.observe-57.0.9.module → 57.0.11/expo.modules.observe-57.0.11.module} +23 -23
  29. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.module.md5 +1 -0
  30. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.module.sha1 +1 -0
  31. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.module.sha256 +1 -0
  32. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.module.sha512 +1 -0
  33. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.9/expo.modules.observe-57.0.9.pom → 57.0.11/expo.modules.observe-57.0.11.pom} +2 -2
  34. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.pom.md5 +1 -0
  35. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.pom.sha1 +1 -0
  36. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.pom.sha256 +1 -0
  37. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.11/expo.modules.observe-57.0.11.pom.sha512 +1 -0
  38. package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml +4 -4
  39. package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.md5 +1 -1
  40. package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha1 +1 -1
  41. package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha256 +1 -1
  42. package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha512 +1 -1
  43. package/package.json +4 -4
  44. package/src/ObserveErrorBoundary.tsx +5 -0
  45. package/src/index.ts +5 -0
  46. package/src/integrations/expo-router/storage.ts +1 -1
  47. package/src/integrations/expo-router/useObserveForRouter.ts +1 -1
  48. package/src/integrations/react-navigation/useObserveForReactNavigation.ts +1 -1
  49. package/src/module.ts +34 -1
  50. package/src/module.web.ts +10 -0
  51. package/src/reportCaughtError.ts +57 -0
  52. package/src/types.ts +38 -0
  53. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9-sources.jar.md5 +0 -1
  54. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9-sources.jar.sha1 +0 -1
  55. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9-sources.jar.sha256 +0 -1
  56. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9-sources.jar.sha512 +0 -1
  57. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.aar.md5 +0 -1
  58. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.aar.sha1 +0 -1
  59. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.aar.sha256 +0 -1
  60. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.aar.sha512 +0 -1
  61. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.module.md5 +0 -1
  62. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.module.sha1 +0 -1
  63. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.module.sha256 +0 -1
  64. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.module.sha512 +0 -1
  65. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.pom.md5 +0 -1
  66. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.pom.sha1 +0 -1
  67. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.pom.sha256 +0 -1
  68. package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.9/expo.modules.observe-57.0.9.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,22 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 57.0.11 — 2026-08-06
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 57.0.10 — 2026-08-04
18
+
19
+ ### 🎉 New features
20
+
21
+ - Add `Observe.registerIntegration` to register an integration ([#48245](https://github.com/expo/expo/pull/48245), [#48268](https://github.com/expo/expo/pull/48268) by [@Ubax](https://github.com/Ubax))
22
+ - Expose `ObserveErrorBoundary`, a React error boundary that records render-phase errors. ([#47341](https://github.com/expo/expo/pull/47341) by [@tsapeta](https://github.com/tsapeta))
23
+ - Add `reportError` to report caught, non-fatal errors from your own `try`/`catch` blocks. ([#47871](https://github.com/expo/expo/pull/47871) by [@tsapeta](https://github.com/tsapeta))
24
+
25
+ ### 🐛 Bug fixes
26
+
27
+ - Fix OTel date conversion ([#48161](https://github.com/expo/expo/pull/48161) by [@Ubax](https://github.com/Ubax))
28
+
13
29
  ## 57.0.9 — 2026-07-29
14
30
 
15
31
  _This version does not introduce any user-facing changes._
@@ -0,0 +1,2 @@
1
+ export { AppMetricsErrorBoundary as ObserveErrorBoundary, type AppMetricsErrorBoundaryProps as ObserveErrorBoundaryProps, type AppMetricsErrorBoundaryFallbackProps as ObserveErrorBoundaryFallbackProps, } from 'expo-app-metrics';
2
+ //# sourceMappingURL=ObserveErrorBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObserveErrorBoundary.d.ts","sourceRoot":"","sources":["../src/ObserveErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,IAAI,oBAAoB,EAC/C,KAAK,4BAA4B,IAAI,yBAAyB,EAC9D,KAAK,oCAAoC,IAAI,iCAAiC,GAC/E,MAAM,kBAAkB,CAAC"}
package/build/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import Observe from './module';
2
2
  export { default as AppMetrics } from 'expo-app-metrics';
3
+ export { ObserveErrorBoundary } from './ObserveErrorBoundary';
4
+ export type { ObserveErrorBoundaryProps, ObserveErrorBoundaryFallbackProps, } from './ObserveErrorBoundary';
3
5
  export { ObserveInteractiveMarker } from './ObserveInteractiveMarker';
4
6
  export type { ObserveInteractiveMarkerProps } from './ObserveInteractiveMarker';
5
7
  export { ObserveRoot } from './ObserveRoot';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO5C,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB,6DAA6D;AAC7D,eAAe,OAAO,CAAC;AAEvB,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,aAAa,EACb,mBAAmB,GACpB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO5C,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB,6DAA6D;AAC7D,eAAe,OAAO,CAAC;AAEvB,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,aAAa,EACb,mBAAmB,GACpB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -14,7 +14,7 @@ export interface RouterIntegrationStorage {
14
14
  pendingActions: PendingAction[];
15
15
  renderedScreensIds: Set<string>;
16
16
  /**
17
- * Wether the app had already recorded the first render of the screen
17
+ * Whether the app had already recorded the first render of the screen
18
18
  */
19
19
  hasRecordedInitialTtr: boolean;
20
20
  /**
package/build/module.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { ObserveModule } from './types';
1
+ import type { ObserveIntegrationsConfig, ObserveModule } from './types';
2
2
  declare const Observe: ObserveModule;
3
+ export declare function registerIntegrationImpl<K extends keyof ObserveIntegrationsConfig>(target: Pick<ObserveModule, 'addListener' | 'getIntegrations'>, name: K, callback: (config: ObserveIntegrationsConfig[K]) => void): void;
3
4
  export default Observe;
4
5
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AAI5D,QAAA,MAAM,OAAO,EAAE,aA+Cb,CAAC;AAEH,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAiB,yBAAyB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIvF,QAAA,MAAM,OAAO,EAAE,aA0Db,CAAC;AAEH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAC/E,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAC9D,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,IAAI,CAeN;AAED,eAAe,OAAO,CAAC"}
@@ -5,7 +5,9 @@ declare class ExpoObserveModule extends NativeModule<ObserveModuleEvents> implem
5
5
  dispatchEvents(): Promise<void>;
6
6
  configure(config: ObserveConfig): void;
7
7
  getIntegrations(): ObserveIntegrationsConfig;
8
+ registerIntegration<K extends keyof ObserveIntegrationsConfig>(name: K, callback: (config: ObserveIntegrationsConfig[K]) => void): void;
8
9
  logEvent(name: string, options?: LogEventOptions): void;
10
+ reportError(error: unknown): void;
9
11
  markFirstRender(): void;
10
12
  markInteractive(attributes?: MetricAttributes): void;
11
13
  setGlobalAttributes(attributes?: ObserveAttributes | null): void;
@@ -1 +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;AACvD,OAAmB,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE3F,OAAO,KAAK,EACV,aAAa,EACb,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,cAAM,iBAAkB,SAAQ,YAAY,CAAC,mBAAmB,CAAE,YAAW,aAAa;IAClF,cAAc;IACpB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IACtC,eAAe,IAAI,yBAAyB;IAG5C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI;IAGvD,eAAe,IAAI,IAAI;IAGvB,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAGpD,mBAAmB,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAGhE,iBAAiB,CAAC,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;CAC7E;;AAED,wBAAmE"}
1
+ {"version":3,"file":"module.web.d.ts","sourceRoot":"","sources":["../src/module.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,MAAM,CAAC;AACvD,OAAmB,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAG3F,OAAO,KAAK,EACV,aAAa,EACb,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,cAAM,iBAAkB,SAAQ,YAAY,CAAC,mBAAmB,CAAE,YAAW,aAAa;IAClF,cAAc;IACpB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IACtC,eAAe,IAAI,yBAAyB;IAG5C,mBAAmB,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAC3D,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,IAAI;IAGP,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI;IAGvD,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAGjC,eAAe,IAAI,IAAI;IAGvB,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAGpD,mBAAmB,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAGhE,iBAAiB,CAAC,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;CAC7E;;AAED,wBAAmE"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Reports a caught value as a non-fatal `reportedByUser`-source error through the AppMetrics module,
3
+ * shared by the native and web `Observe.reportError` implementations.
4
+ *
5
+ * Never throws: it's called from a `catch` block, so a failure here (a pathological thrown value, or
6
+ * a native call that rejects the payload) must not turn a handled error into an unhandled one.
7
+ */
8
+ export declare function reportCaughtError(error: unknown): void;
9
+ //# sourceMappingURL=reportCaughtError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reportCaughtError.d.ts","sourceRoot":"","sources":["../src/reportCaughtError.ts"],"names":[],"mappings":"AAqCA;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAYtD"}
package/build/types.d.ts CHANGED
@@ -121,6 +121,20 @@ export declare class ObserveModule extends NativeModule<ObserveModuleEvents> {
121
121
  * call, or an empty object if `configure` has not run yet.
122
122
  */
123
123
  getIntegrations(): ObserveIntegrationsConfig;
124
+ /**
125
+ * Invokes a callback once when the named integration configuration becomes available.
126
+ *
127
+ * @param name Integration name.
128
+ * @param callback Function called with the integration configuration.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * Observe.registerIntegration('expo-router', config => {
133
+ * console.log(config);
134
+ * });
135
+ * ```
136
+ */
137
+ registerIntegration<K extends keyof ObserveIntegrationsConfig>(name: K, callback: (config: ObserveIntegrationsConfig[K]) => void): void;
124
138
  /**
125
139
  * Records a log event against the current main session. The event is
126
140
  * persisted locally and dispatched on the next `dispatchEvents()` flush.
@@ -131,6 +145,26 @@ export declare class ObserveModule extends NativeModule<ObserveModuleEvents> {
131
145
  * @param options Optional body, attributes, and severity overrides.
132
146
  */
133
147
  logEvent(name: string, options?: LogEventOptions): void;
148
+ /**
149
+ * Reports an error your code caught and handled, recorded as a non-fatal `exception` event. Use it
150
+ * to keep visibility into failures you recover from, which never reach the automatic global handler
151
+ * or an error boundary.
152
+ *
153
+ * The thrown value is normalized: an `Error`'s `name`, `message`, and `stack` are captured; any
154
+ * other value (a string, a plain object) is stringified as the message.
155
+ *
156
+ * @param error The caught value. An `Error` is preferred, but any thrown value is accepted.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * try {
161
+ * await syncCart();
162
+ * } catch (error) {
163
+ * Observe.reportError(error);
164
+ * }
165
+ * ```
166
+ */
167
+ reportError(error: unknown): void;
134
168
  /**
135
169
  * Marks the first render of the app. Used to compute the `cold_ttr` and
136
170
  * `warm_ttr` metrics.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE7F;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAEjD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,YAAY,CAAC,EAAE,yBAAyB,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACxC;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,kCAAkC,CAAC;IAC7D;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,kCAAkC,CAAC;CACnE;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE;QAAE,YAAY,EAAE,yBAAyB,CAAA;KAAE,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IAC1E,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAC/B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IACtC;;;OAGG;IACH,eAAe,IAAI,yBAAyB;IAC5C;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI;IACvD;;;OAGG;IACH,eAAe,IAAI,IAAI;IACvB;;;;;;;OAOG;IACH,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI;IACpD;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAChE;;;;;;OAMG;IACH,iBAAiB,CAAC,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;CAC7E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE7F;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAEjD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,YAAY,CAAC,EAAE,yBAAyB,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACxC;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,kCAAkC,CAAC;IAC7D;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,kCAAkC,CAAC;CACnE;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE;QAAE,YAAY,EAAE,yBAAyB,CAAA;KAAE,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IAC1E,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAC/B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IACtC;;;OAGG;IACH,eAAe,IAAI,yBAAyB;IAC5C;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,CAAC,SAAS,MAAM,yBAAyB,EAC3D,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,IAAI;IACP;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI;IACvD;;;;;;;;;;;;;;;;;;OAkBG;IAEH,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IACjC;;;OAGG;IACH,eAAe,IAAI,IAAI;IACvB;;;;;;;OAOG;IACH,eAAe,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI;IACpD;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAChE;;;;;;OAMG;IACH,iBAAiB,CAAC,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;CAC7E"}
@@ -9,7 +9,7 @@
9
9
  "publication": {
10
10
  "groupId": "expo.modules.observe",
11
11
  "artifactId": "expo.modules.observe",
12
- "version": "57.0.9",
12
+ "version": "57.0.11",
13
13
  "repository": "local-maven-repo"
14
14
  }
15
15
  }
@@ -193,13 +193,11 @@ let metricNameMap = [
193
193
  "navigation/tti": "expo.navigation.tti",
194
194
  ]
195
195
 
196
- nonisolated(unsafe) let formatter = ISO8601DateFormatter()
197
-
198
196
  private func nsFromISOString(_ dateString: String?) -> UInt64 {
199
197
  if let dateString,
200
- let date = formatter.date(from: dateString)
198
+ let date = try? Date.ISO8601FormatStyle().parse(dateString)
201
199
  {
202
- return UInt64(date.timeIntervalSince1970 * 1_000_000_000)
200
+ return UInt64((date.timeIntervalSince1970 * 1_000).rounded()) * 1_000_000
203
201
  }
204
202
  return UInt64(Date().timeIntervalSince1970 * 1_000_000_000)
205
203
  }
@@ -148,6 +148,31 @@ struct OpenTelemetryTests {
148
148
  #expect(otMetric.gauge.dataPoints[0].timeUnixNano == expectedNanos)
149
149
  }
150
150
 
151
+ @Test
152
+ func `toOTMetric converts ISO8601 timestamp with fractional seconds to nanoseconds`() {
153
+ let metric = makeMetric(name: "loadTime", value: 1.0, timestamp: "2026-01-09T12:08:09.123Z")
154
+ let otMetric = metric.toOTMetric()
155
+
156
+ #expect(otMetric.gauge.dataPoints[0].timeUnixNano == 1_767_960_489_123_000_000)
157
+ }
158
+
159
+ @Test
160
+ func `toOTLogRecord converts ISO8601 timestamp with fractional seconds to nanoseconds`() {
161
+ let log = Event.Log(
162
+ name: "auth.login_failed",
163
+ body: nil,
164
+ timestamp: "2026-01-09T12:08:09.123Z",
165
+ severity: .info,
166
+ attributes: nil,
167
+ droppedAttributesCount: 0,
168
+ sessionId: testSessionId
169
+ )
170
+ let otLog = log.toOTLogRecord()
171
+
172
+ #expect(otLog.timeUnixNano == 1_767_960_489_123_000_000)
173
+ #expect(otLog.observedTimeUnixNano == 1_767_960_489_123_000_000)
174
+ }
175
+
151
176
  // MARK: - Event metadata
152
177
 
153
178
  @Test
@@ -0,0 +1 @@
1
+ 4331955b74c12afb9a4c63b3ca052e82be6d7dcc603cb73a40e94d76dbfae49b
@@ -0,0 +1 @@
1
+ 2261a6a48f08b57a5eec8cff7f5566dde1e10bdd66466aeb5569594d1f81718daf0c25869e38456909d5c4ce8800ea6e8bd2bedaf4202053ae2a430778bd15eb
@@ -0,0 +1 @@
1
+ bcf48a4f82c8ac35b51edf9e3062520cf7d5e155d7c1cb7ea69ecd4a291f9c58
@@ -0,0 +1 @@
1
+ ec8d2b7924132d4af4fc42ad57ad06379f843a0311891bdcbd25c9a244fa549fee5ebd21288c55ba54b632b1ab44260071496725c2db23c1475bb194788938dd
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "expo.modules.observe",
5
5
  "module": "expo.modules.observe",
6
- "version": "57.0.9",
6
+ "version": "57.0.11",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.observe-57.0.9.aar",
28
- "url": "expo.modules.observe-57.0.9.aar",
29
- "size": 275829,
30
- "sha512": "7a6902e9d954f361187b1fabd32b9e87f55d5fdf1a5b4bc5786a5f83545788f6f5905c94255b966e49236d296fb6f838d18a38f5a399a0b630e9604d80d7b8e3",
31
- "sha256": "708834f7a493060538fe012ffe57b24cd28c192c06450e241587c429806bcf44",
32
- "sha1": "1bf0478b50bb4eb4397e7e4e62a45dcf44ec3071",
33
- "md5": "1fe9628babcfb5166c1bf79bcecafbe9"
27
+ "name": "expo.modules.observe-57.0.11.aar",
28
+ "url": "expo.modules.observe-57.0.11.aar",
29
+ "size": 275833,
30
+ "sha512": "ec8d2b7924132d4af4fc42ad57ad06379f843a0311891bdcbd25c9a244fa549fee5ebd21288c55ba54b632b1ab44260071496725c2db23c1475bb194788938dd",
31
+ "sha256": "bcf48a4f82c8ac35b51edf9e3062520cf7d5e155d7c1cb7ea69ecd4a291f9c58",
32
+ "sha1": "985bd88b96ed082e0d97880d6dc4126dd85bfaa0",
33
+ "md5": "43f9f26be2213da7c272e2ec5625f730"
34
34
  }
35
35
  ]
36
36
  },
@@ -61,7 +61,7 @@
61
61
  "group": "expo.modules.appmetrics",
62
62
  "module": "expo.modules.appmetrics",
63
63
  "version": {
64
- "requires": "57.0.7"
64
+ "requires": "57.0.9"
65
65
  }
66
66
  },
67
67
  {
@@ -134,13 +134,13 @@
134
134
  ],
135
135
  "files": [
136
136
  {
137
- "name": "expo.modules.observe-57.0.9.aar",
138
- "url": "expo.modules.observe-57.0.9.aar",
139
- "size": 275829,
140
- "sha512": "7a6902e9d954f361187b1fabd32b9e87f55d5fdf1a5b4bc5786a5f83545788f6f5905c94255b966e49236d296fb6f838d18a38f5a399a0b630e9604d80d7b8e3",
141
- "sha256": "708834f7a493060538fe012ffe57b24cd28c192c06450e241587c429806bcf44",
142
- "sha1": "1bf0478b50bb4eb4397e7e4e62a45dcf44ec3071",
143
- "md5": "1fe9628babcfb5166c1bf79bcecafbe9"
137
+ "name": "expo.modules.observe-57.0.11.aar",
138
+ "url": "expo.modules.observe-57.0.11.aar",
139
+ "size": 275833,
140
+ "sha512": "ec8d2b7924132d4af4fc42ad57ad06379f843a0311891bdcbd25c9a244fa549fee5ebd21288c55ba54b632b1ab44260071496725c2db23c1475bb194788938dd",
141
+ "sha256": "bcf48a4f82c8ac35b51edf9e3062520cf7d5e155d7c1cb7ea69ecd4a291f9c58",
142
+ "sha1": "985bd88b96ed082e0d97880d6dc4126dd85bfaa0",
143
+ "md5": "43f9f26be2213da7c272e2ec5625f730"
144
144
  }
145
145
  ]
146
146
  },
@@ -154,13 +154,13 @@
154
154
  },
155
155
  "files": [
156
156
  {
157
- "name": "expo.modules.observe-57.0.9-sources.jar",
158
- "url": "expo.modules.observe-57.0.9-sources.jar",
159
- "size": 25677,
160
- "sha512": "56376d3be3f15954dfb4f0c35e01be556beca3723348fed7bf0adb57af61b8d5919ac7f6cfd6d6814efe5f99ae8bbcd45f2e83a34bd4d25941e9a9496d9449f0",
161
- "sha256": "424e6bdbc0186ae58b9363934bebd490ba9a94f5ff31a932a10e81dd16e30986",
162
- "sha1": "90a35ac786aa7cd93fa6ebfc959c5c9602888a7b",
163
- "md5": "c1bce9b3e125ffacd471f7aa5cfaaa74"
157
+ "name": "expo.modules.observe-57.0.11-sources.jar",
158
+ "url": "expo.modules.observe-57.0.11-sources.jar",
159
+ "size": 25679,
160
+ "sha512": "2261a6a48f08b57a5eec8cff7f5566dde1e10bdd66466aeb5569594d1f81718daf0c25869e38456909d5c4ce8800ea6e8bd2bedaf4202053ae2a430778bd15eb",
161
+ "sha256": "4331955b74c12afb9a4c63b3ca052e82be6d7dcc603cb73a40e94d76dbfae49b",
162
+ "sha1": "92a3d67bb204d131eaa2670635e5374892297f6d",
163
+ "md5": "d8445fd317c2598e603a7228028b95d3"
164
164
  }
165
165
  ]
166
166
  }
@@ -0,0 +1 @@
1
+ 7aeb17867b74444d38c24c9a28259151dbf34406331b2beadf0f548d1bf901af
@@ -0,0 +1 @@
1
+ 5aaa581b643a11f7dadfca1e0f1d0759256da417e6a38c6b104f2f77b174ce8dce9ee9bc16ac5c4f44055a0dff6e374c1c7e58a9c92dd3178941b3d12c2eeae2
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>expo.modules.observe</groupId>
11
11
  <artifactId>expo.modules.observe</artifactId>
12
- <version>57.0.9</version>
12
+ <version>57.0.11</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.observe</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -52,7 +52,7 @@
52
52
  <dependency>
53
53
  <groupId>expo.modules.appmetrics</groupId>
54
54
  <artifactId>expo.modules.appmetrics</artifactId>
55
- <version>57.0.7</version>
55
+ <version>57.0.9</version>
56
56
  <scope>runtime</scope>
57
57
  </dependency>
58
58
  <dependency>
@@ -0,0 +1 @@
1
+ b9b9d45825cecf297adde4b906c087bd2d17300b712780ec6aa3bd235b65221e
@@ -0,0 +1 @@
1
+ 85e6a6e4336747324b3b35a508fd070e1eca2f461cca1772bdf1084e912cde2119aa8b443cff8dff92227ed2c99e60828b4f15dcd98bc93a37df51c9492ba409
@@ -3,11 +3,11 @@
3
3
  <groupId>expo.modules.observe</groupId>
4
4
  <artifactId>expo.modules.observe</artifactId>
5
5
  <versioning>
6
- <latest>57.0.9</latest>
7
- <release>57.0.9</release>
6
+ <latest>57.0.11</latest>
7
+ <release>57.0.11</release>
8
8
  <versions>
9
- <version>57.0.9</version>
9
+ <version>57.0.11</version>
10
10
  </versions>
11
- <lastUpdated>20260729192358</lastUpdated>
11
+ <lastUpdated>20260806111631</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- b146439c74d2e0bb206c77f80014a8af
1
+ edf456b3cd686bf50009496c5ab5a357
@@ -1 +1 @@
1
- 577e45512a91bd8ad92f0a980bc4f9828d081309
1
+ 4c192aed008c71146249041db3dcd79be878b285
@@ -1 +1 @@
1
- d39c5fe255b007e3f0557f8f0c2005b8d8f060befe43c8551f17d28076ef3045
1
+ 991f023d59eb236c3615e5bce92f81d49c8fd6743c7d7fa11cfb66d453db4ecd
@@ -1 +1 @@
1
- 84eaf852945cdad90631e6df0d8de81b86abfae247379b523c6ca5a433bb633fda60b6503fbdc4abb42beaa3244d9062fd1e0b79c4fa1247031ba5af3428b7c9
1
+ 0b5bdad7ddfdb217baf1562f3357e23ac0f5db1826de0673c48ab165b7f8dccfc18d5e25d093d88401d0a5b1704c39ec94337d33821c41a4efec6899ae81b686
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-observe",
3
3
  "title": "Expo Observe",
4
- "version": "57.0.9",
4
+ "version": "57.0.11",
5
5
  "description": "Expo module that dispatches collected app metrics to EAS Observe",
6
6
  "main": "src/index.ts",
7
7
  "types": "build/index.d.ts",
@@ -35,14 +35,14 @@
35
35
  "author": "650 Industries, Inc.",
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "expo-app-metrics": "~57.0.7",
38
+ "expo-app-metrics": "~57.0.9",
39
39
  "expo-eas-client": "~57.0.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@react-navigation/native": "^7.1.33",
43
43
  "@testing-library/react-native": "^13.3.0",
44
44
  "@types/react": "~19.2.0",
45
- "expo": "57.0.9",
45
+ "expo": "57.0.11",
46
46
  "expo-module-scripts": "56.0.3"
47
47
  },
48
48
  "peerDependencies": {
@@ -60,7 +60,7 @@
60
60
  "optional": true
61
61
  }
62
62
  },
63
- "gitHead": "ff6a677893d78b02c3adf63f67ba159b926395b0",
63
+ "gitHead": "e322c877a4d185fb18b27d68945a190cb7f15ce6",
64
64
  "scripts": {
65
65
  "build": "expo-module build",
66
66
  "clean": "expo-module clean",
@@ -0,0 +1,5 @@
1
+ export {
2
+ AppMetricsErrorBoundary as ObserveErrorBoundary,
3
+ type AppMetricsErrorBoundaryProps as ObserveErrorBoundaryProps,
4
+ type AppMetricsErrorBoundaryFallbackProps as ObserveErrorBoundaryFallbackProps,
5
+ } from 'expo-app-metrics';
package/src/index.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import Observe from './module';
2
2
 
3
3
  export { default as AppMetrics } from 'expo-app-metrics';
4
+ export { ObserveErrorBoundary } from './ObserveErrorBoundary';
5
+ export type {
6
+ ObserveErrorBoundaryProps,
7
+ ObserveErrorBoundaryFallbackProps,
8
+ } from './ObserveErrorBoundary';
4
9
  export { ObserveInteractiveMarker } from './ObserveInteractiveMarker';
5
10
  export type { ObserveInteractiveMarkerProps } from './ObserveInteractiveMarker';
6
11
  export { ObserveRoot } from './ObserveRoot';
@@ -16,7 +16,7 @@ export interface RouterIntegrationStorage {
16
16
  pendingActions: PendingAction[];
17
17
  renderedScreensIds: Set<string>;
18
18
  /**
19
- * Wether the app had already recorded the first render of the screen
19
+ * Whether the app had already recorded the first render of the screen
20
20
  */
21
21
  hasRecordedInitialTtr: boolean;
22
22
  /**
@@ -1,13 +1,13 @@
1
1
  import AppMetrics, { type MetricAttributes } from 'expo-app-metrics';
2
2
  import { use, useCallback, useEffect, useRef } from 'react';
3
3
 
4
+ import { useAssertValueDoesNotChange } from '../../useAssertValueDoesNotChange';
4
5
  import { getNavigationMetricParams } from '../navigationConfig';
5
6
  import { ObserveRouterIntegrationContext } from './ObserveRouterIntegrationProvider';
6
7
  import { emitTTI } from './emitTTI';
7
8
  import { getRouterIntegrationConfig, isInitialized } from './init';
8
9
  import { buildRoutePattern } from './routeName';
9
10
  import { optionalRouter } from './router';
10
- import { useAssertValueDoesNotChange } from '../../useAssertValueDoesNotChange';
11
11
 
12
12
  type MarkInteractive = (typeof AppMetrics)['markInteractive'];
13
13
 
@@ -1,6 +1,7 @@
1
1
  import AppMetrics, { type MetricAttributes } from 'expo-app-metrics';
2
2
  import { use, useCallback, useEffect, useRef } from 'react';
3
3
 
4
+ import { useAssertValueDoesNotChange } from '../../useAssertValueDoesNotChange';
4
5
  import { getNavigationRouteParams } from '../navigationConfig';
5
6
  import { ObserveReactNavigationIntegrationContext } from './context';
6
7
  import { emitTTI } from './emitTTI';
@@ -8,7 +9,6 @@ import { getPathname } from './getPathname';
8
9
  import { getReactNavigationIntegrationConfig, isInitialized } from './init';
9
10
  import { optionalReactNavigation } from './reactNavigation';
10
11
  import type { NavigationStateLike } from './types';
11
- import { useAssertValueDoesNotChange } from '../../useAssertValueDoesNotChange';
12
12
 
13
13
  type MarkInteractive = (typeof AppMetrics)['markInteractive'];
14
14
 
package/src/module.ts CHANGED
@@ -5,7 +5,8 @@ import { initRouterIntegration } from './integrations/expo-router/init';
5
5
  import { isRouterInstalled } from './integrations/expo-router/router';
6
6
  import { initReactNavigationIntegration } from './integrations/react-navigation/init';
7
7
  import { isReactNavigationInstalled } from './integrations/react-navigation/reactNavigation';
8
- import type { ObserveConfig, ObserveModule } from './types';
8
+ import { reportCaughtError } from './reportCaughtError';
9
+ import type { ObserveConfig, ObserveIntegrationsConfig, ObserveModule } from './types';
9
10
 
10
11
  const native = requireNativeModule<ObserveModule>('ExpoObserve');
11
12
 
@@ -47,6 +48,17 @@ const Observe: ObserveModule = new Proxy(native, {
47
48
  };
48
49
  }
49
50
 
51
+ if (prop === 'reportError') {
52
+ return (error: unknown) => reportCaughtError(error);
53
+ }
54
+
55
+ if (prop === 'registerIntegration') {
56
+ return <K extends keyof ObserveIntegrationsConfig>(
57
+ name: K,
58
+ callback: (config: ObserveIntegrationsConfig[K]) => void
59
+ ) => registerIntegrationImpl(target, name, callback);
60
+ }
61
+
50
62
  // On Android, the native module is a JSI host object, so `prop in target` (and `hasOwnProperty`) report
51
63
  // `true` for names it doesn't implement — a host object has no `has` hook. `Object.keys(target)`
52
64
  // goes through `getPropertyNames`, which lists the module's actual members, so use it to forward
@@ -58,4 +70,25 @@ const Observe: ObserveModule = new Proxy(native, {
58
70
  },
59
71
  });
60
72
 
73
+ export function registerIntegrationImpl<K extends keyof ObserveIntegrationsConfig>(
74
+ target: Pick<ObserveModule, 'addListener' | 'getIntegrations'>,
75
+ name: K,
76
+ callback: (config: ObserveIntegrationsConfig[K]) => void
77
+ ): void {
78
+ const integrations = target.getIntegrations();
79
+ if (integrations) {
80
+ if (integrations[name]) {
81
+ callback(integrations[name]);
82
+ }
83
+ return;
84
+ }
85
+
86
+ const subscription = target.addListener('configure', ({ integrations }) => {
87
+ subscription.remove();
88
+ if (integrations?.[name]) {
89
+ callback(integrations[name]);
90
+ }
91
+ });
92
+ }
93
+
61
94
  export default Observe;
package/src/module.web.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { NativeModule, registerWebModule } from 'expo';
2
2
  import AppMetrics, { type LogEventOptions, type MetricAttributes } from 'expo-app-metrics';
3
3
 
4
+ import { reportCaughtError } from './reportCaughtError';
4
5
  import type {
5
6
  ObserveConfig,
6
7
  ObserveIntegrationsConfig,
@@ -15,9 +16,18 @@ class ExpoObserveModule extends NativeModule<ObserveModuleEvents> implements Obs
15
16
  getIntegrations(): ObserveIntegrationsConfig {
16
17
  return {};
17
18
  }
19
+ registerIntegration<K extends keyof ObserveIntegrationsConfig>(
20
+ name: K,
21
+ callback: (config: ObserveIntegrationsConfig[K]) => void
22
+ ): void {
23
+ // Web does not provide integration configuration or emit `configure` events.
24
+ }
18
25
  logEvent(name: string, options?: LogEventOptions): void {
19
26
  AppMetrics.logEvent(name, options);
20
27
  }
28
+ reportError(error: unknown): void {
29
+ reportCaughtError(error);
30
+ }
21
31
  markFirstRender(): void {
22
32
  AppMetrics.markFirstRender();
23
33
  }
@@ -0,0 +1,57 @@
1
+ import AppMetrics from 'expo-app-metrics';
2
+
3
+ /**
4
+ * The `reportError` payload shape sent to the native AppMetrics module. The native record types
5
+ * `type`/`stacktrace` as optional strings and `message` as a required string, so a non-string value
6
+ * would fail the record decode and drop the report. Every field is normalized to a string here.
7
+ */
8
+ type NormalizedReportedError = {
9
+ type?: string;
10
+ message: string;
11
+ stacktrace?: string;
12
+ };
13
+
14
+ /** Returns `value` when it's a string, otherwise `undefined`, so non-string fields never reach native. */
15
+ function stringOrUndefined(value: unknown): string | undefined {
16
+ return typeof value === 'string' ? value : undefined;
17
+ }
18
+
19
+ /**
20
+ * Normalizes an arbitrary caught value into the fields the native `reportError` expects, the way the
21
+ * global `ErrorUtils` handler does.
22
+ */
23
+ function normalizeReportedError(error: unknown): NormalizedReportedError {
24
+ // Only a real `Error` contributes name/message/stack, and each only when it's actually a string;
25
+ // an `Error` with non-string fields would otherwise reach the native string fields (e.g. a mutated
26
+ // `message`) and fail the record decode.
27
+ if (error instanceof Error) {
28
+ return {
29
+ type: stringOrUndefined(error.name),
30
+ message: stringOrUndefined(error.message) ?? String(error),
31
+ stacktrace: stringOrUndefined(error.stack),
32
+ };
33
+ }
34
+ // Any non-Error throw (a string, a plain object, a number) becomes its stringification, no stack.
35
+ return { message: String(error) };
36
+ }
37
+
38
+ /**
39
+ * Reports a caught value as a non-fatal `reportedByUser`-source error through the AppMetrics module,
40
+ * shared by the native and web `Observe.reportError` implementations.
41
+ *
42
+ * Never throws: it's called from a `catch` block, so a failure here (a pathological thrown value, or
43
+ * a native call that rejects the payload) must not turn a handled error into an unhandled one.
44
+ */
45
+ export function reportCaughtError(error: unknown): void {
46
+ try {
47
+ AppMetrics.reportError({
48
+ source: 'reportedByUser',
49
+ ...normalizeReportedError(error),
50
+ isFatal: false,
51
+ });
52
+ } catch (reportingError) {
53
+ if (__DEV__) {
54
+ console.warn('[expo-observe] `reportError` failed to record the error:', reportingError);
55
+ }
56
+ }
57
+ }
package/src/types.ts CHANGED
@@ -126,6 +126,23 @@ export declare class ObserveModule extends NativeModule<ObserveModuleEvents> {
126
126
  * call, or an empty object if `configure` has not run yet.
127
127
  */
128
128
  getIntegrations(): ObserveIntegrationsConfig;
129
+ /**
130
+ * Invokes a callback once when the named integration configuration becomes available.
131
+ *
132
+ * @param name Integration name.
133
+ * @param callback Function called with the integration configuration.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * Observe.registerIntegration('expo-router', config => {
138
+ * console.log(config);
139
+ * });
140
+ * ```
141
+ */
142
+ registerIntegration<K extends keyof ObserveIntegrationsConfig>(
143
+ name: K,
144
+ callback: (config: ObserveIntegrationsConfig[K]) => void
145
+ ): void;
129
146
  /**
130
147
  * Records a log event against the current main session. The event is
131
148
  * persisted locally and dispatched on the next `dispatchEvents()` flush.
@@ -136,6 +153,27 @@ export declare class ObserveModule extends NativeModule<ObserveModuleEvents> {
136
153
  * @param options Optional body, attributes, and severity overrides.
137
154
  */
138
155
  logEvent(name: string, options?: LogEventOptions): void;
156
+ /**
157
+ * Reports an error your code caught and handled, recorded as a non-fatal `exception` event. Use it
158
+ * to keep visibility into failures you recover from, which never reach the automatic global handler
159
+ * or an error boundary.
160
+ *
161
+ * The thrown value is normalized: an `Error`'s `name`, `message`, and `stack` are captured; any
162
+ * other value (a string, a plain object) is stringified as the message.
163
+ *
164
+ * @param error The caught value. An `Error` is preferred, but any thrown value is accepted.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * try {
169
+ * await syncCart();
170
+ * } catch (error) {
171
+ * Observe.reportError(error);
172
+ * }
173
+ * ```
174
+ */
175
+ // eslint-disable-next-line handle-callback-err -- `error` is the caught value to report, not a Node callback error.
176
+ reportError(error: unknown): void;
139
177
  /**
140
178
  * Marks the first render of the app. Used to compute the `cold_ttr` and
141
179
  * `warm_ttr` metrics.
@@ -1 +0,0 @@
1
- 424e6bdbc0186ae58b9363934bebd490ba9a94f5ff31a932a10e81dd16e30986
@@ -1 +0,0 @@
1
- 56376d3be3f15954dfb4f0c35e01be556beca3723348fed7bf0adb57af61b8d5919ac7f6cfd6d6814efe5f99ae8bbcd45f2e83a34bd4d25941e9a9496d9449f0
@@ -1 +0,0 @@
1
- 708834f7a493060538fe012ffe57b24cd28c192c06450e241587c429806bcf44
@@ -1 +0,0 @@
1
- 7a6902e9d954f361187b1fabd32b9e87f55d5fdf1a5b4bc5786a5f83545788f6f5905c94255b966e49236d296fb6f838d18a38f5a399a0b630e9604d80d7b8e3
@@ -1 +0,0 @@
1
- 654a84a63b12eca9a3a921f64eeaa806b9d42676b690236c60300809df7541d8
@@ -1 +0,0 @@
1
- ac5ad4223c45c5c21f8fecba00c492c1881ed5bfbcbbb4ec72ae377c34ff12f542f6894790af058847782f2aa64c0c8c3625f37ecad2682182916e79145d72e9
@@ -1 +0,0 @@
1
- 03e79952adbb9a549f184b48f3304d239fdb4ad22c1b957c02e30f3bfd5bd669
@@ -1 +0,0 @@
1
- 69304ce50078d9bccf3b1e7095c38f2cfc27d63ef8eee467c68950847d95e143e9b3c8df3e9993e9eaa7cd69802e13c8191b553f2e397ed27243da611b4251f1