expo-observe 58.0.14 → 58.0.16

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 (63) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/build/index.d.ts +1 -1
  3. package/build/index.js.map +1 -1
  4. package/build/integrations/react-native-reanimated/init.d.ts +13 -0
  5. package/build/integrations/react-native-reanimated/init.js +129 -0
  6. package/build/integrations/react-native-reanimated/init.js.map +1 -0
  7. package/build/integrations/react-native-reanimated/reanimated.d.ts +13 -0
  8. package/build/integrations/react-native-reanimated/reanimated.js +18 -0
  9. package/build/integrations/react-native-reanimated/reanimated.js.map +1 -0
  10. package/build/integrations/react-native-reanimated/stack.d.ts +5 -0
  11. package/build/integrations/react-native-reanimated/stack.js +27 -0
  12. package/build/integrations/react-native-reanimated/stack.js.map +1 -0
  13. package/build/module.js +7 -0
  14. package/build/module.js.map +1 -1
  15. package/build/reportCaughtError.d.ts +4 -3
  16. package/build/reportCaughtError.js +5 -4
  17. package/build/reportCaughtError.js.map +1 -1
  18. package/build/types.d.ts +40 -0
  19. package/build/types.js.map +1 -1
  20. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{58.0.14/expo.modules.observe-58.0.14-sources.jar → 58.0.16/expo.modules.observe-58.0.16-sources.jar} +0 -0
  21. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16-sources.jar.md5 +1 -0
  22. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16-sources.jar.sha1 +1 -0
  23. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16-sources.jar.sha256 +1 -0
  24. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16-sources.jar.sha512 +1 -0
  25. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{58.0.14/expo.modules.observe-58.0.14.aar → 58.0.16/expo.modules.observe-58.0.16.aar} +0 -0
  26. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.aar.md5 +1 -0
  27. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.aar.sha1 +1 -0
  28. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.aar.sha256 +1 -0
  29. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.aar.sha512 +1 -0
  30. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{58.0.14/expo.modules.observe-58.0.14.module → 58.0.16/expo.modules.observe-58.0.16.module} +22 -22
  31. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.module.md5 +1 -0
  32. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.module.sha1 +1 -0
  33. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.module.sha256 +1 -0
  34. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.module.sha512 +1 -0
  35. package/local-maven-repo/expo/modules/observe/expo.modules.observe/{58.0.14/expo.modules.observe-58.0.14.pom → 58.0.16/expo.modules.observe-58.0.16.pom} +2 -2
  36. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.pom.md5 +1 -0
  37. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.pom.sha1 +1 -0
  38. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.pom.sha256 +1 -0
  39. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.16/expo.modules.observe-58.0.16.pom.sha512 +1 -0
  40. package/package.json +11 -5
  41. package/src/index.ts +1 -0
  42. package/src/integrations/react-native-reanimated/init.ts +157 -0
  43. package/src/integrations/react-native-reanimated/reanimated.ts +29 -0
  44. package/src/integrations/react-native-reanimated/stack.ts +28 -0
  45. package/src/module.ts +10 -0
  46. package/src/reportCaughtError.ts +8 -4
  47. package/src/types.ts +41 -0
  48. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14-sources.jar.md5 +0 -1
  49. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14-sources.jar.sha1 +0 -1
  50. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14-sources.jar.sha256 +0 -1
  51. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14-sources.jar.sha512 +0 -1
  52. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.aar.md5 +0 -1
  53. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.aar.sha1 +0 -1
  54. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.aar.sha256 +0 -1
  55. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.aar.sha512 +0 -1
  56. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.module.md5 +0 -1
  57. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.module.sha1 +0 -1
  58. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.module.sha256 +0 -1
  59. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.module.sha512 +0 -1
  60. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.pom.md5 +0 -1
  61. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.pom.sha1 +0 -1
  62. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.pom.sha256 +0 -1
  63. package/local-maven-repo/expo/modules/observe/expo.modules.observe/58.0.14/expo.modules.observe-58.0.14.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 58.0.16
4
+
5
+ ### Patch Changes
6
+
7
+ - Add a `react-native-reanimated` integration that reports Reanimated errors, and optionally warnings, to EAS Observe. ([#50576](https://github.com/expo/expo/pull/50576) by [@douglowder](https://github.com/douglowder))
8
+ - Updated dependencies. ([#51720](https://github.com/expo/expo/pull/51720))
9
+ - expo-app-metrics@58.0.14
10
+
11
+ ## 58.0.15
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies. ([#51609](https://github.com/expo/expo/pull/51609))
16
+ - expo-app-metrics@58.0.13
17
+
3
18
  ## 58.0.14
4
19
 
5
20
  ### Patch Changes
package/build/index.d.ts CHANGED
@@ -12,5 +12,5 @@ export { ObserveRoot } from './ObserveRoot';
12
12
  export { Observe };
13
13
  /** @deprecated Import the named `Observe` export instead. */
14
14
  export default Observe;
15
- export type { ObserveAttribute, ObserveAttributes, ObserveConfig, ObserveIntegrationsConfig, ObserveModule, ObserveModuleEvents, ObserveNavigationIntegrationConfig, ObserveNetworkTracesConfig, } from './types';
15
+ export type { ObserveAttribute, ObserveAttributes, ObserveConfig, ObserveIntegrationsConfig, ObserveModule, ObserveModuleEvents, ObserveNavigationIntegrationConfig, ObserveNetworkTracesConfig, ObserveReanimatedIntegrationConfig, } from './types';
16
16
  export { useObserve } from './useObserve';
@@ -1 +1 @@
1
- {"version":3,"sources":["index.ts"],"sourcesContent":[null],"names":["Observe","default","AppMetrics","ObserveErrorBoundary","ObserveInteractiveMarker","ObserveRoot","setBundleDefaults","environment","process","env","NODE_ENV","isJsDev","__DEV__","useObserve"],"mappings":"AAAA,OAAOA,aAAa,WAAW;AAE/B;;CAEC,GACD,SAASC,WAAWC,UAAU,QAAQ,mBAAmB;AAWzD,SAASC,oBAAoB,QAAQ,yBAAyB;AAK9D,SAASC,wBAAwB,QAAQ,6BAA6B;AAEtE,SAASC,WAAW,QAAQ,gBAAgB;AAE5CL,QAAQM,iBAAiB,CAAC;IACxBC,aAAaC,QAAQC,GAAG,CAACC,QAAQ,IAAI;IACrCC,SAAS,CAAC,CAACC;AACb;AAEA,SAASZ,OAAO,GAAG;AAEnB,2DAA2D,GAC3D,eAAeA,QAAQ;AAYvB,SAASa,UAAU,QAAQ,eAAe"}
1
+ {"version":3,"sources":["index.ts"],"sourcesContent":[null],"names":["Observe","default","AppMetrics","ObserveErrorBoundary","ObserveInteractiveMarker","ObserveRoot","setBundleDefaults","environment","process","env","NODE_ENV","isJsDev","__DEV__","useObserve"],"mappings":"AAAA,OAAOA,aAAa,WAAW;AAE/B;;CAEC,GACD,SAASC,WAAWC,UAAU,QAAQ,mBAAmB;AAWzD,SAASC,oBAAoB,QAAQ,yBAAyB;AAK9D,SAASC,wBAAwB,QAAQ,6BAA6B;AAEtE,SAASC,WAAW,QAAQ,gBAAgB;AAE5CL,QAAQM,iBAAiB,CAAC;IACxBC,aAAaC,QAAQC,GAAG,CAACC,QAAQ,IAAI;IACrCC,SAAS,CAAC,CAACC;AACb;AAEA,SAASZ,OAAO,GAAG;AAEnB,2DAA2D,GAC3D,eAAeA,QAAQ;AAavB,SAASa,UAAU,QAAQ,eAAe"}
@@ -0,0 +1,13 @@
1
+ import type { ObserveIntegrationsConfig } from '../../types';
2
+ /**
3
+ * Configures Reanimated's logger with the integration's `level` and `strict` settings and a
4
+ * callback that reports Reanimated errors to Observe as `reanimated.error` errors. When
5
+ * `reportWarnings` is set, the callback also reports Reanimated warnings as `reanimated.warning`
6
+ * events.
7
+ */
8
+ export declare function initReanimatedIntegration(config: NonNullable<ObserveIntegrationsConfig['react-native-reanimated']>): void;
9
+ /**
10
+ * Stops reporting Reanimated errors and warnings to Observe. Reanimated's logger keeps its
11
+ * configuration.
12
+ */
13
+ export declare function disableReanimatedIntegration(): void;
@@ -0,0 +1,129 @@
1
+ import AppMetrics from 'expo-app-metrics';
2
+ import { reportCaughtError } from '../../reportCaughtError';
3
+ import { loadReanimated } from './reanimated';
4
+ import { removeFirstFrame } from './stack';
5
+ // Value of Reanimated's `ReanimatedLogLevel.error`.
6
+ const LOG_LEVEL_ERROR = 2;
7
+ // `configureReanimatedLogger` accepts an `onLog` callback since Reanimated 4.7.0.
8
+ const MIN_MAJOR = 4;
9
+ const MIN_MINOR = 7;
10
+ const REANIMATED_ERROR_NAME = 'reanimated.error';
11
+ const REANIMATED_WARNING_EVENT = 'reanimated.warning';
12
+ // A UI-runtime log can repeat every frame, so each distinct message is reported at most once per
13
+ // window. The cap bounds the reports in a window when messages embed varying values, and the
14
+ // window lets new messages be reported again after a surge. Errors and warnings have separate
15
+ // windows, so a screen with many distinct warnings cannot stop errors from being reported.
16
+ const REPORT_WINDOW_MS = 15 * 60_000;
17
+ const MAX_DISTINCT_MESSAGES_PER_WINDOW = 20;
18
+ // Attribute values are sent on every dispatch, and strict-mode messages append a docs
19
+ // reference, so the copied message is capped. Messages are also deduplicated by this capped
20
+ // text, so two messages that differ only after the cap do not send the same attribute twice.
21
+ const MAX_MESSAGE_LENGTH = 500;
22
+ // Maps the capped text of each message reported in the current window to the time it was
23
+ // reported. A `Map` keeps insertion order, so the oldest report is always first. The time comes
24
+ // from the monotonic `performance.now()`, so a change to the device clock cannot stop expiry.
25
+ const reportedErrors = new Map();
26
+ const reportedWarnings = new Map();
27
+ // The callback stays registered in Reanimated after the integration is turned off, because
28
+ // configuring the logger again would reset the app's `level` and `strict`. It checks this flag.
29
+ let isEnabled = false;
30
+ let shouldReportWarnings = false;
31
+ /**
32
+ * A Reanimated error reported to Observe. The stack is set explicitly so it points at the
33
+ * callback's caller rather than at this constructor.
34
+ */ class ReanimatedError extends Error {
35
+ constructor(message, stack){
36
+ super(message);
37
+ this.name = REANIMATED_ERROR_NAME;
38
+ if (stack !== undefined) {
39
+ this.stack = stack;
40
+ }
41
+ }
42
+ }
43
+ function supportsOnLog(version) {
44
+ const match = /^(\d+)\.(\d+)/.exec(version);
45
+ if (!match) {
46
+ return false;
47
+ }
48
+ const major = Number(match[1]);
49
+ const minor = Number(match[2]);
50
+ return major > MIN_MAJOR || major === MIN_MAJOR && minor >= MIN_MINOR;
51
+ }
52
+ function removeExpiredReports(reported, now) {
53
+ for (const [message, reportedAt] of reported){
54
+ if (now - reportedAt < REPORT_WINDOW_MS) {
55
+ return;
56
+ }
57
+ reported.delete(message);
58
+ }
59
+ }
60
+ function shouldReport(reported, cappedMessage) {
61
+ const now = performance.now();
62
+ removeExpiredReports(reported, now);
63
+ if (reported.has(cappedMessage) || reported.size >= MAX_DISTINCT_MESSAGES_PER_WINDOW) {
64
+ return false;
65
+ }
66
+ reported.set(cappedMessage, now);
67
+ return true;
68
+ }
69
+ // Reanimated already prints every log to the console, so this only reports to Observe. The name
70
+ // is specific to Observe, so `removeFirstFrame` cannot remove a frame of an app function.
71
+ function __expo_observe_reportReanimatedLog({ level, message }) {
72
+ if (!isEnabled) {
73
+ return;
74
+ }
75
+ const isError = level === LOG_LEVEL_ERROR;
76
+ if (!isError && !shouldReportWarnings) {
77
+ return;
78
+ }
79
+ const cappedMessage = message.slice(0, MAX_MESSAGE_LENGTH);
80
+ if (!shouldReport(isError ? reportedErrors : reportedWarnings, cappedMessage)) {
81
+ return;
82
+ }
83
+ if (isError) {
84
+ // For a log on the React Native runtime, Reanimated calls `onLog` synchronously, so this
85
+ // stack includes the frames that led to the log. A log on the UI runtime is delivered later,
86
+ // so its stack has no frames left after `removeFirstFrame`.
87
+ const stack = removeFirstFrame(new Error(message).stack, __expo_observe_reportReanimatedLog.name);
88
+ reportCaughtError(new ReanimatedError(message, stack), 'integration');
89
+ return;
90
+ }
91
+ AppMetrics.logEvent(REANIMATED_WARNING_EVENT, {
92
+ displayName: 'Reanimated warning',
93
+ body: message,
94
+ severity: 'warn',
95
+ // `eas observe:events` and `observe:session` show `attributes` but not `body`, so the
96
+ // message is copied here to stay queryable.
97
+ attributes: {
98
+ message: cappedMessage
99
+ }
100
+ });
101
+ }
102
+ /**
103
+ * Configures Reanimated's logger with the integration's `level` and `strict` settings and a
104
+ * callback that reports Reanimated errors to Observe as `reanimated.error` errors. When
105
+ * `reportWarnings` is set, the callback also reports Reanimated warnings as `reanimated.warning`
106
+ * events.
107
+ */ export function initReanimatedIntegration(config) {
108
+ const reanimated = loadReanimated();
109
+ if (!reanimated) {
110
+ console.warn("[expo-observe] `integrations: { 'react-native-reanimated': ... }` was set, but " + '`react-native-reanimated` is not installed. The integration will not initialize.');
111
+ return;
112
+ }
113
+ if (!supportsOnLog(reanimated.version)) {
114
+ console.warn(`[expo-observe] The 'react-native-reanimated' integration requires Reanimated 4.7.0 or later, ` + `but Reanimated ${reanimated.version} is installed. Reanimated errors and warnings will not be ` + 'reported to EAS Observe. Upgrade react-native-reanimated to 4.7.0 or later.');
115
+ return;
116
+ }
117
+ const { reportWarnings = false, ...loggerConfig } = typeof config === 'object' ? config : {};
118
+ reanimated.configureReanimatedLogger(loggerConfig, __expo_observe_reportReanimatedLog);
119
+ shouldReportWarnings = reportWarnings;
120
+ isEnabled = true;
121
+ }
122
+ /**
123
+ * Stops reporting Reanimated errors and warnings to Observe. Reanimated's logger keeps its
124
+ * configuration.
125
+ */ export function disableReanimatedIntegration() {
126
+ isEnabled = false;
127
+ }
128
+
129
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["integrations/react-native-reanimated/init.ts"],"sourcesContent":[null],"names":["AppMetrics","reportCaughtError","loadReanimated","removeFirstFrame","LOG_LEVEL_ERROR","MIN_MAJOR","MIN_MINOR","REANIMATED_ERROR_NAME","REANIMATED_WARNING_EVENT","REPORT_WINDOW_MS","MAX_DISTINCT_MESSAGES_PER_WINDOW","MAX_MESSAGE_LENGTH","reportedErrors","Map","reportedWarnings","isEnabled","shouldReportWarnings","ReanimatedError","Error","message","stack","name","undefined","supportsOnLog","version","match","exec","major","Number","minor","removeExpiredReports","reported","now","reportedAt","delete","shouldReport","cappedMessage","performance","has","size","set","__expo_observe_reportReanimatedLog","level","isError","slice","logEvent","displayName","body","severity","attributes","initReanimatedIntegration","config","reanimated","console","warn","reportWarnings","loggerConfig","configureReanimatedLogger","disableReanimatedIntegration"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type ReanimatedLogData = {
2
+ level: number;
3
+ message: string;
4
+ };
5
+ export type ReanimatedLoggerConfig = {
6
+ level?: number;
7
+ strict?: boolean;
8
+ };
9
+ export interface LoadedReanimated {
10
+ version: string;
11
+ configureReanimatedLogger(config: ReanimatedLoggerConfig, onLog?: (data: ReanimatedLogData) => void): void;
12
+ }
13
+ export declare function loadReanimated(): LoadedReanimated | null;
@@ -0,0 +1,18 @@
1
+ // react-native-reanimated is an optional peerDependency of expo-observe. It is required lazily,
2
+ // only when the integration is enabled, so an app that doesn't enable it never loads Reanimated
3
+ // through expo-observe. This lives in its own file because a `require` wrapped in try/catch
4
+ // doesn't work with Metro's `inlineRequires` when it shares a file with other imports (see
5
+ // expo-gl's `GLWorkletContextManager`).
6
+ export function loadReanimated() {
7
+ try {
8
+ const { reanimatedVersion, configureReanimatedLogger } = require('react-native-reanimated');
9
+ return {
10
+ version: reanimatedVersion,
11
+ configureReanimatedLogger
12
+ };
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ //# sourceMappingURL=reanimated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["integrations/react-native-reanimated/reanimated.ts"],"sourcesContent":[null],"names":["loadReanimated","reanimatedVersion","configureReanimatedLogger","require","version"],"mappings":"AAAA,gGAAgG;AAChG,gGAAgG;AAChG,4FAA4F;AAC5F,2FAA2F;AAC3F,wCAAwC;AAcxC,OAAO,SAASA;IACd,IAAI;QACF,MAAM,EAAEC,iBAAiB,EAAEC,yBAAyB,EAAE,GAAGC,QAAQ;QAIjE,OAAO;YAAEC,SAASH;YAAmBC;QAA0B;IACjE,EAAE,OAAM;QACN,OAAO;IACT;AACF"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Removes the first frame of the stack when it is a frame of `functionName`. The message can
3
+ * contain newlines, so the first frame is the first line that is a stack frame.
4
+ */
5
+ export declare function removeFirstFrame(stack: string | undefined, functionName: string): string | undefined;
@@ -0,0 +1,27 @@
1
+ const STACK_FRAME_PATTERN = /^\s*at /;
2
+ function escapeRegExp(text) {
3
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
4
+ }
5
+ /**
6
+ * Removes the first frame of the stack when it is a frame of `functionName`. The message can
7
+ * contain newlines, so the first frame is the first line that is a stack frame.
8
+ */ export function removeFirstFrame(stack, functionName) {
9
+ if (stack === undefined) {
10
+ return undefined;
11
+ }
12
+ const lines = stack.split('\n');
13
+ const firstFrameIndex = lines.findIndex((line)=>STACK_FRAME_PATTERN.test(line));
14
+ if (firstFrameIndex === -1) {
15
+ return stack;
16
+ }
17
+ const functionFramePattern = new RegExp(`^\\s*at ${escapeRegExp(functionName)} `);
18
+ if (!functionFramePattern.test(lines[firstFrameIndex] ?? '')) {
19
+ return stack;
20
+ }
21
+ return [
22
+ ...lines.slice(0, firstFrameIndex),
23
+ ...lines.slice(firstFrameIndex + 1)
24
+ ].join('\n');
25
+ }
26
+
27
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["integrations/react-native-reanimated/stack.ts"],"sourcesContent":[null],"names":["STACK_FRAME_PATTERN","escapeRegExp","text","replace","removeFirstFrame","stack","functionName","undefined","lines","split","firstFrameIndex","findIndex","line","test","functionFramePattern","RegExp","slice","join"],"mappings":"AAAA,MAAMA,sBAAsB;AAE5B,SAASC,aAAaC,IAAY;IAChC,OAAOA,KAAKC,OAAO,CAAC,uBAAuB;AAC7C;AAEA;;;CAGC,GACD,OAAO,SAASC,iBACdC,KAAyB,EACzBC,YAAoB;IAEpB,IAAID,UAAUE,WAAW;QACvB,OAAOA;IACT;IACA,MAAMC,QAAQH,MAAMI,KAAK,CAAC;IAC1B,MAAMC,kBAAkBF,MAAMG,SAAS,CAAC,CAACC,OAASZ,oBAAoBa,IAAI,CAACD;IAC3E,IAAIF,oBAAoB,CAAC,GAAG;QAC1B,OAAOL;IACT;IACA,MAAMS,uBAAuB,IAAIC,OAAO,CAAC,QAAQ,EAAEd,aAAaK,cAAc,CAAC,CAAC;IAChF,IAAI,CAACQ,qBAAqBD,IAAI,CAACL,KAAK,CAACE,gBAAgB,IAAI,KAAK;QAC5D,OAAOL;IACT;IACA,OAAO;WAAIG,MAAMQ,KAAK,CAAC,GAAGN;WAAqBF,MAAMQ,KAAK,CAACN,kBAAkB;KAAG,CAACO,IAAI,CAAC;AACxF"}
package/build/module.js CHANGED
@@ -2,6 +2,7 @@ import { requireNativeModule } from 'expo';
2
2
  import AppMetrics, { setErrorHandlerEnabled } from 'expo-app-metrics';
3
3
  import { initRouterIntegration } from './integrations/expo-router/init';
4
4
  import { isRouterInstalled } from './integrations/expo-router/router';
5
+ import { disableReanimatedIntegration, initReanimatedIntegration } from './integrations/react-native-reanimated/init';
5
6
  import { initReactNavigationIntegration } from './integrations/react-navigation/init';
6
7
  import { isReactNavigationInstalled } from './integrations/react-navigation/reactNavigation';
7
8
  import { reportCaughtError } from './reportCaughtError';
@@ -64,6 +65,12 @@ const Observe = new Proxy(native, {
64
65
  } else if (shouldInitReactNavigationIntegration) {
65
66
  initReactNavigationIntegration(config.integrations?.['react-navigation']);
66
67
  }
68
+ const reanimatedConfig = config.integrations?.['react-native-reanimated'];
69
+ if (reanimatedConfig) {
70
+ initReanimatedIntegration(reanimatedConfig);
71
+ } else {
72
+ disableReanimatedIntegration();
73
+ }
67
74
  return target.configure(config);
68
75
  };
69
76
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["module.ts"],"sourcesContent":[null],"names":["requireNativeModule","AppMetrics","setErrorHandlerEnabled","initRouterIntegration","isRouterInstalled","initReactNavigationIntegration","isReactNavigationInstalled","reportCaughtError","networkTracesConfigFromOption","networkTraces","option","enabled","__DEV__","malformed","filter","hosts","host","test","length","console","warn","join","native","Observe","Proxy","get","target","prop","receiver","config","errorHandlingEnabled","setNetworkTracesConfig","routerEnabled","integrations","reactNavigationEnabled","shouldInitRouterIntegration","shouldInitReactNavigationIntegration","configure","error","name","callback","registerIntegrationImpl","Object","keys","includes","Reflect","getIntegrations","subscription","addListener","remove"],"mappings":"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"}
1
+ {"version":3,"sources":["module.ts"],"sourcesContent":[null],"names":["requireNativeModule","AppMetrics","setErrorHandlerEnabled","initRouterIntegration","isRouterInstalled","disableReanimatedIntegration","initReanimatedIntegration","initReactNavigationIntegration","isReactNavigationInstalled","reportCaughtError","networkTracesConfigFromOption","networkTraces","option","enabled","__DEV__","malformed","filter","hosts","host","test","length","console","warn","join","native","Observe","Proxy","get","target","prop","receiver","config","errorHandlingEnabled","setNetworkTracesConfig","routerEnabled","integrations","reactNavigationEnabled","shouldInitRouterIntegration","shouldInitReactNavigationIntegration","reanimatedConfig","configure","error","name","callback","registerIntegrationImpl","Object","keys","includes","Reflect","getIntegrations","subscription","addListener","remove"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
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.
2
+ * Reports a caught value as a non-fatal error through the AppMetrics module, shared by the native
3
+ * and web `Observe.reportError` implementations and by integrations. `source` is `reportedByUser`
4
+ * for an error the app reports, and `integration` for an error an integration reports for a library.
4
5
  *
5
6
  * Never throws: it's called from a `catch` block, so a failure here (a pathological thrown value, or
6
7
  * a native call that rejects the payload) must not turn a handled error into an unhandled one.
7
8
  */
8
- export declare function reportCaughtError(error: unknown): void;
9
+ export declare function reportCaughtError(error: unknown, source?: 'reportedByUser' | 'integration'): void;
@@ -22,15 +22,16 @@ import AppMetrics from 'expo-app-metrics';
22
22
  };
23
23
  }
24
24
  /**
25
- * Reports a caught value as a non-fatal `reportedByUser`-source error through the AppMetrics module,
26
- * shared by the native and web `Observe.reportError` implementations.
25
+ * Reports a caught value as a non-fatal error through the AppMetrics module, shared by the native
26
+ * and web `Observe.reportError` implementations and by integrations. `source` is `reportedByUser`
27
+ * for an error the app reports, and `integration` for an error an integration reports for a library.
27
28
  *
28
29
  * Never throws: it's called from a `catch` block, so a failure here (a pathological thrown value, or
29
30
  * a native call that rejects the payload) must not turn a handled error into an unhandled one.
30
- */ export function reportCaughtError(error) {
31
+ */ export function reportCaughtError(error, source = 'reportedByUser') {
31
32
  try {
32
33
  AppMetrics.reportError({
33
- source: 'reportedByUser',
34
+ source,
34
35
  ...normalizeReportedError(error),
35
36
  isFatal: false
36
37
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["reportCaughtError.ts"],"sourcesContent":[null],"names":["AppMetrics","stringOrUndefined","value","undefined","normalizeReportedError","error","Error","type","name","message","String","stacktrace","stack","reportCaughtError","reportError","source","isFatal","reportingError","__DEV__","console","warn"],"mappings":"AAAA,OAAOA,gBAAgB,mBAAmB;AAa1C,wGAAwG,GACxG,SAASC,kBAAkBC,KAAc;IACvC,OAAO,OAAOA,UAAU,WAAWA,QAAQC;AAC7C;AAEA;;;CAGC,GACD,SAASC,uBAAuBC,KAAc;IAC5C,iGAAiG;IACjG,mGAAmG;IACnG,yCAAyC;IACzC,IAAIA,iBAAiBC,OAAO;QAC1B,OAAO;YACLC,MAAMN,kBAAkBI,MAAMG,IAAI;YAClCC,SAASR,kBAAkBI,MAAMI,OAAO,KAAKC,OAAOL;YACpDM,YAAYV,kBAAkBI,MAAMO,KAAK;QAC3C;IACF;IACA,kGAAkG;IAClG,OAAO;QAAEH,SAASC,OAAOL;IAAO;AAClC;AAEA;;;;;;CAMC,GACD,OAAO,SAASQ,kBAAkBR,KAAc;IAC9C,IAAI;QACFL,WAAWc,WAAW,CAAC;YACrBC,QAAQ;YACR,GAAGX,uBAAuBC,MAAM;YAChCW,SAAS;QACX;IACF,EAAE,OAAOC,gBAAgB;QACvB,IAAIC,SAAS;YACXC,QAAQC,IAAI,CAAC,4DAA4DH;QAC3E;IACF;AACF"}
1
+ {"version":3,"sources":["reportCaughtError.ts"],"sourcesContent":[null],"names":["AppMetrics","stringOrUndefined","value","undefined","normalizeReportedError","error","Error","type","name","message","String","stacktrace","stack","reportCaughtError","source","reportError","isFatal","reportingError","__DEV__","console","warn"],"mappings":"AAAA,OAAOA,gBAAgB,mBAAmB;AAa1C,wGAAwG,GACxG,SAASC,kBAAkBC,KAAc;IACvC,OAAO,OAAOA,UAAU,WAAWA,QAAQC;AAC7C;AAEA;;;CAGC,GACD,SAASC,uBAAuBC,KAAc;IAC5C,iGAAiG;IACjG,mGAAmG;IACnG,yCAAyC;IACzC,IAAIA,iBAAiBC,OAAO;QAC1B,OAAO;YACLC,MAAMN,kBAAkBI,MAAMG,IAAI;YAClCC,SAASR,kBAAkBI,MAAMI,OAAO,KAAKC,OAAOL;YACpDM,YAAYV,kBAAkBI,MAAMO,KAAK;QAC3C;IACF;IACA,kGAAkG;IAClG,OAAO;QAAEH,SAASC,OAAOL;IAAO;AAClC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASQ,kBACdR,KAAc,EACdS,SAA2C,gBAAgB;IAE3D,IAAI;QACFd,WAAWe,WAAW,CAAC;YACrBD;YACA,GAAGV,uBAAuBC,MAAM;YAChCW,SAAS;QACX;IACF,EAAE,OAAOC,gBAAgB;QACvB,IAAIC,SAAS;YACXC,QAAQC,IAAI,CAAC,4DAA4DH;QAC3E;IACF;AACF"}
package/build/types.d.ts CHANGED
@@ -139,7 +139,47 @@ export interface ObserveIntegrationsConfig {
139
139
  * @default false
140
140
  */
141
141
  'react-navigation'?: boolean | ObserveNavigationIntegrationConfig;
142
+ /**
143
+ * Enables the `react-native-reanimated` integration, which reports Reanimated errors to Observe
144
+ * as errors named `reanimated.error`. To also report Reanimated warnings as events named
145
+ * `reanimated.warning`, set `reportWarnings`. Reanimated still prints errors and warnings to the
146
+ * console. The integration reports each distinct message at most once every 15 minutes. It
147
+ * reports at most 20 distinct errors and 20 distinct warnings in any 15-minute period.
148
+ *
149
+ * Requires `react-native-reanimated` 4.7.0 or later to be installed.
150
+ *
151
+ * The integration configures Reanimated's logger with `configureReanimatedLogger`. To set the
152
+ * logger's `level` and `strict` options, pass an object here. Don't call
153
+ * `configureReanimatedLogger` in your app. A later call removes the integration's callback, and
154
+ * the integration replaces the `level` and `strict` options of an earlier call.
155
+ *
156
+ * @default false
157
+ */
158
+ 'react-native-reanimated'?: boolean | ObserveReanimatedIntegrationConfig;
142
159
  }
160
+ export type ObserveReanimatedIntegrationConfig = {
161
+ /**
162
+ * The minimum log level that Reanimated prints and reports. Pass a `ReanimatedLogLevel` value
163
+ * from `react-native-reanimated`: `ReanimatedLogLevel.warn` (`1`) or `ReanimatedLogLevel.error`
164
+ * (`2`).
165
+ *
166
+ * @default ReanimatedLogLevel.warn
167
+ */
168
+ level?: 1 | 2;
169
+ /**
170
+ * Whether Reanimated also prints and reports its strict-mode warnings.
171
+ *
172
+ * @default true
173
+ */
174
+ strict?: boolean;
175
+ /**
176
+ * Whether the integration also reports Reanimated warnings to Observe, as events named
177
+ * `reanimated.warning`. Reanimated prints warnings to the console either way.
178
+ *
179
+ * @default false
180
+ */
181
+ reportWarnings?: boolean;
182
+ };
143
183
  /**
144
184
  * Events emitted by the native `ExpoObserve` module.
145
185
  */
@@ -1 +1 @@
1
- {"version":3,"sources":["types.ts"],"sourcesContent":[null],"names":[],"mappings":"AAqKA,WAyJC"}
1
+ {"version":3,"sources":["types.ts"],"sourcesContent":[null],"names":[],"mappings":"AA8MA,WAyJC"}
@@ -0,0 +1 @@
1
+ c76770935713ad519193541fcb769a2fcd68f23a401d6c698843d84fbd1f7da0
@@ -0,0 +1 @@
1
+ 997be2ee63594820e4184fbce65b61a4beba2716682ee30161b4fa92db554a7e2c8a100a6ff1c88db10d484d0f72c35a49b258b22d703329cae6d67e2d69d037
@@ -0,0 +1 @@
1
+ c37c74fe309480e5f5278110bcdcd8c8ed1cdda889aa88a6a4ea03b8e78b3bd7
@@ -0,0 +1 @@
1
+ 2c1a46d58d9c82b8c127e42c234cade56a9ba2d7aafec09204ce6cf173461dcde7c88d698dcf39b7912c069519ce3848c7df5a78278ca5e2c26c1c77e3520d54
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "expo.modules.observe",
5
5
  "module": "expo.modules.observe",
6
- "version": "58.0.14",
6
+ "version": "58.0.16",
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-58.0.14.aar",
28
- "url": "expo.modules.observe-58.0.14.aar",
29
- "size": 302595,
30
- "sha512": "011e6ad1efa46ed83a7c1f27b8e240a49e2875d3c712d61dce2269207330b9cf94fc0d8a4a3295b31d450cca9a773bc22f94f3d5ece0f5ec57399e902ea878ae",
31
- "sha256": "d0286094f7cdc9325087139447ec5ec6c4563901d7229252a4460fc64379a0f1",
32
- "sha1": "95adad7bab839e049d62c8416cc5ee285c82629e",
33
- "md5": "338d8842318510f8caf14b01aefdfe6a"
27
+ "name": "expo.modules.observe-58.0.16.aar",
28
+ "url": "expo.modules.observe-58.0.16.aar",
29
+ "size": 302597,
30
+ "sha512": "2c1a46d58d9c82b8c127e42c234cade56a9ba2d7aafec09204ce6cf173461dcde7c88d698dcf39b7912c069519ce3848c7df5a78278ca5e2c26c1c77e3520d54",
31
+ "sha256": "c37c74fe309480e5f5278110bcdcd8c8ed1cdda889aa88a6a4ea03b8e78b3bd7",
32
+ "sha1": "04943132c7c93348101691ce33046bede7b17c4d",
33
+ "md5": "cca677ea690b4997313e35cef5f73f5d"
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": "58.0.12"
64
+ "requires": "58.0.14"
65
65
  }
66
66
  },
67
67
  {
@@ -120,13 +120,13 @@
120
120
  ],
121
121
  "files": [
122
122
  {
123
- "name": "expo.modules.observe-58.0.14.aar",
124
- "url": "expo.modules.observe-58.0.14.aar",
125
- "size": 302595,
126
- "sha512": "011e6ad1efa46ed83a7c1f27b8e240a49e2875d3c712d61dce2269207330b9cf94fc0d8a4a3295b31d450cca9a773bc22f94f3d5ece0f5ec57399e902ea878ae",
127
- "sha256": "d0286094f7cdc9325087139447ec5ec6c4563901d7229252a4460fc64379a0f1",
128
- "sha1": "95adad7bab839e049d62c8416cc5ee285c82629e",
129
- "md5": "338d8842318510f8caf14b01aefdfe6a"
123
+ "name": "expo.modules.observe-58.0.16.aar",
124
+ "url": "expo.modules.observe-58.0.16.aar",
125
+ "size": 302597,
126
+ "sha512": "2c1a46d58d9c82b8c127e42c234cade56a9ba2d7aafec09204ce6cf173461dcde7c88d698dcf39b7912c069519ce3848c7df5a78278ca5e2c26c1c77e3520d54",
127
+ "sha256": "c37c74fe309480e5f5278110bcdcd8c8ed1cdda889aa88a6a4ea03b8e78b3bd7",
128
+ "sha1": "04943132c7c93348101691ce33046bede7b17c4d",
129
+ "md5": "cca677ea690b4997313e35cef5f73f5d"
130
130
  }
131
131
  ]
132
132
  },
@@ -140,13 +140,13 @@
140
140
  },
141
141
  "files": [
142
142
  {
143
- "name": "expo.modules.observe-58.0.14-sources.jar",
144
- "url": "expo.modules.observe-58.0.14-sources.jar",
143
+ "name": "expo.modules.observe-58.0.16-sources.jar",
144
+ "url": "expo.modules.observe-58.0.16-sources.jar",
145
145
  "size": 27883,
146
- "sha512": "dc0f3f96ba7e53fe360f9d84663b73f314427d709b5923e49a8d95088bf66280660405b777ed342e2e14f969619abfc2b2aa4116e0ac0567b6931cba3b7a05c4",
147
- "sha256": "2fe94a3000018f70581d3ab1c396164525960de1ecf98c266d1e24689d915150",
148
- "sha1": "268e21bc0d85402b0ccc18c7d7d267271c08d60e",
149
- "md5": "57d1852cd6856c787f39ccf7dc67fe22"
146
+ "sha512": "997be2ee63594820e4184fbce65b61a4beba2716682ee30161b4fa92db554a7e2c8a100a6ff1c88db10d484d0f72c35a49b258b22d703329cae6d67e2d69d037",
147
+ "sha256": "c76770935713ad519193541fcb769a2fcd68f23a401d6c698843d84fbd1f7da0",
148
+ "sha1": "27be8bc32a8afbb84df92ec0966eeeed1b206829",
149
+ "md5": "6635ff468ff08ed50dde81ad0ab24bc1"
150
150
  }
151
151
  ]
152
152
  }
@@ -0,0 +1 @@
1
+ 0cbea66a354c468fbfd8c6b7e27a396655687fcc010f76d68d437f4b8d2b0f05
@@ -0,0 +1 @@
1
+ 929b60dbf425200c3dd9e14d8f86ea53a9367d4adab82207f010b8fa306afb65e528e9654a5ebd2c6810031a530d86ab784dc3b369c94180ea93d291fe6fa9c3
@@ -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>58.0.14</version>
12
+ <version>58.0.16</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>58.0.12</version>
55
+ <version>58.0.14</version>
56
56
  <scope>runtime</scope>
57
57
  </dependency>
58
58
  <dependency>
@@ -0,0 +1 @@
1
+ a1eab751d5bb4b299af12ad881d4f942a16765461052b1bc12595b6a622153c6
@@ -0,0 +1 @@
1
+ 06fcee23ec778c6ea48b9face7250f15854c047c580b37fcef3a2f8c65f1c442b5a1fb4faab2c6f729edd94d01c7ec5899d4cb9de52f2a0100e27b37aac0f8d5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-observe",
3
- "version": "58.0.14",
3
+ "version": "58.0.16",
4
4
  "description": "Expo module that dispatches collected app metrics to EAS Observe",
5
5
  "keywords": [
6
6
  "EAS",
@@ -42,22 +42,25 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "expo-app-metrics": "~58.0.12",
45
+ "expo-app-metrics": "~58.0.14",
46
46
  "expo-eas-client": "~58.0.2"
47
47
  },
48
48
  "devDependencies": {
49
- "@expo/internal-scripts": "58.0.2",
49
+ "@expo/internal-scripts": "58.0.4",
50
50
  "@react-navigation/native": "^7.1.33",
51
51
  "@testing-library/react-native": "^14.0.1",
52
52
  "@types/react": "~19.3.0",
53
- "expo": "58.0.10"
53
+ "expo": "58.0.17",
54
+ "react-native-reanimated": "4.7.0",
55
+ "react-native-worklets": "0.13.0"
54
56
  },
55
57
  "peerDependencies": {
56
58
  "@react-navigation/native": "^7.0.0",
57
59
  "expo": "*",
58
60
  "expo-router": "*",
59
61
  "react": "*",
60
- "react-native": "*"
62
+ "react-native": "*",
63
+ "react-native-reanimated": ">=4.7.0"
61
64
  },
62
65
  "peerDependenciesMeta": {
63
66
  "expo-router": {
@@ -65,6 +68,9 @@
65
68
  },
66
69
  "@react-navigation/native": {
67
70
  "optional": true
71
+ },
72
+ "react-native-reanimated": {
73
+ "optional": true
68
74
  }
69
75
  },
70
76
  "title": "Expo Observe",
package/src/index.ts CHANGED
@@ -42,5 +42,6 @@ export type {
42
42
  ObserveModuleEvents,
43
43
  ObserveNavigationIntegrationConfig,
44
44
  ObserveNetworkTracesConfig,
45
+ ObserveReanimatedIntegrationConfig,
45
46
  } from './types';
46
47
  export { useObserve } from './useObserve';
@@ -0,0 +1,157 @@
1
+ import AppMetrics from 'expo-app-metrics';
2
+
3
+ import { reportCaughtError } from '../../reportCaughtError';
4
+ import type { ObserveIntegrationsConfig } from '../../types';
5
+ import { loadReanimated, type ReanimatedLogData } from './reanimated';
6
+ import { removeFirstFrame } from './stack';
7
+
8
+ // Value of Reanimated's `ReanimatedLogLevel.error`.
9
+ const LOG_LEVEL_ERROR = 2;
10
+
11
+ // `configureReanimatedLogger` accepts an `onLog` callback since Reanimated 4.7.0.
12
+ const MIN_MAJOR = 4;
13
+ const MIN_MINOR = 7;
14
+
15
+ const REANIMATED_ERROR_NAME = 'reanimated.error';
16
+ const REANIMATED_WARNING_EVENT = 'reanimated.warning';
17
+
18
+ // A UI-runtime log can repeat every frame, so each distinct message is reported at most once per
19
+ // window. The cap bounds the reports in a window when messages embed varying values, and the
20
+ // window lets new messages be reported again after a surge. Errors and warnings have separate
21
+ // windows, so a screen with many distinct warnings cannot stop errors from being reported.
22
+ const REPORT_WINDOW_MS = 15 * 60_000;
23
+ const MAX_DISTINCT_MESSAGES_PER_WINDOW = 20;
24
+ // Attribute values are sent on every dispatch, and strict-mode messages append a docs
25
+ // reference, so the copied message is capped. Messages are also deduplicated by this capped
26
+ // text, so two messages that differ only after the cap do not send the same attribute twice.
27
+ const MAX_MESSAGE_LENGTH = 500;
28
+
29
+ // Maps the capped text of each message reported in the current window to the time it was
30
+ // reported. A `Map` keeps insertion order, so the oldest report is always first. The time comes
31
+ // from the monotonic `performance.now()`, so a change to the device clock cannot stop expiry.
32
+ const reportedErrors = new Map<string, number>();
33
+ const reportedWarnings = new Map<string, number>();
34
+
35
+ // The callback stays registered in Reanimated after the integration is turned off, because
36
+ // configuring the logger again would reset the app's `level` and `strict`. It checks this flag.
37
+ let isEnabled = false;
38
+ let shouldReportWarnings = false;
39
+
40
+ /**
41
+ * A Reanimated error reported to Observe. The stack is set explicitly so it points at the
42
+ * callback's caller rather than at this constructor.
43
+ */
44
+ class ReanimatedError extends Error {
45
+ constructor(message: string, stack: string | undefined) {
46
+ super(message);
47
+ this.name = REANIMATED_ERROR_NAME;
48
+ if (stack !== undefined) {
49
+ this.stack = stack;
50
+ }
51
+ }
52
+ }
53
+
54
+ function supportsOnLog(version: string): boolean {
55
+ const match = /^(\d+)\.(\d+)/.exec(version);
56
+ if (!match) {
57
+ return false;
58
+ }
59
+ const major = Number(match[1]);
60
+ const minor = Number(match[2]);
61
+ return major > MIN_MAJOR || (major === MIN_MAJOR && minor >= MIN_MINOR);
62
+ }
63
+
64
+ function removeExpiredReports(reported: Map<string, number>, now: number): void {
65
+ for (const [message, reportedAt] of reported) {
66
+ if (now - reportedAt < REPORT_WINDOW_MS) {
67
+ return;
68
+ }
69
+ reported.delete(message);
70
+ }
71
+ }
72
+
73
+ function shouldReport(reported: Map<string, number>, cappedMessage: string): boolean {
74
+ const now = performance.now();
75
+ removeExpiredReports(reported, now);
76
+ if (reported.has(cappedMessage) || reported.size >= MAX_DISTINCT_MESSAGES_PER_WINDOW) {
77
+ return false;
78
+ }
79
+ reported.set(cappedMessage, now);
80
+ return true;
81
+ }
82
+
83
+ // Reanimated already prints every log to the console, so this only reports to Observe. The name
84
+ // is specific to Observe, so `removeFirstFrame` cannot remove a frame of an app function.
85
+ function __expo_observe_reportReanimatedLog({ level, message }: ReanimatedLogData): void {
86
+ if (!isEnabled) {
87
+ return;
88
+ }
89
+ const isError = level === LOG_LEVEL_ERROR;
90
+ if (!isError && !shouldReportWarnings) {
91
+ return;
92
+ }
93
+ const cappedMessage = message.slice(0, MAX_MESSAGE_LENGTH);
94
+ if (!shouldReport(isError ? reportedErrors : reportedWarnings, cappedMessage)) {
95
+ return;
96
+ }
97
+ if (isError) {
98
+ // For a log on the React Native runtime, Reanimated calls `onLog` synchronously, so this
99
+ // stack includes the frames that led to the log. A log on the UI runtime is delivered later,
100
+ // so its stack has no frames left after `removeFirstFrame`.
101
+ const stack = removeFirstFrame(
102
+ new Error(message).stack,
103
+ __expo_observe_reportReanimatedLog.name
104
+ );
105
+ reportCaughtError(new ReanimatedError(message, stack), 'integration');
106
+ return;
107
+ }
108
+ AppMetrics.logEvent(REANIMATED_WARNING_EVENT, {
109
+ displayName: 'Reanimated warning',
110
+ body: message,
111
+ severity: 'warn',
112
+ // `eas observe:events` and `observe:session` show `attributes` but not `body`, so the
113
+ // message is copied here to stay queryable.
114
+ attributes: { message: cappedMessage },
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Configures Reanimated's logger with the integration's `level` and `strict` settings and a
120
+ * callback that reports Reanimated errors to Observe as `reanimated.error` errors. When
121
+ * `reportWarnings` is set, the callback also reports Reanimated warnings as `reanimated.warning`
122
+ * events.
123
+ */
124
+ export function initReanimatedIntegration(
125
+ config: NonNullable<ObserveIntegrationsConfig['react-native-reanimated']>
126
+ ): void {
127
+ const reanimated = loadReanimated();
128
+ if (!reanimated) {
129
+ console.warn(
130
+ "[expo-observe] `integrations: { 'react-native-reanimated': ... }` was set, but " +
131
+ '`react-native-reanimated` is not installed. The integration will not initialize.'
132
+ );
133
+ return;
134
+ }
135
+
136
+ if (!supportsOnLog(reanimated.version)) {
137
+ console.warn(
138
+ `[expo-observe] The 'react-native-reanimated' integration requires Reanimated 4.7.0 or later, ` +
139
+ `but Reanimated ${reanimated.version} is installed. Reanimated errors and warnings will not be ` +
140
+ 'reported to EAS Observe. Upgrade react-native-reanimated to 4.7.0 or later.'
141
+ );
142
+ return;
143
+ }
144
+
145
+ const { reportWarnings = false, ...loggerConfig } = typeof config === 'object' ? config : {};
146
+ reanimated.configureReanimatedLogger(loggerConfig, __expo_observe_reportReanimatedLog);
147
+ shouldReportWarnings = reportWarnings;
148
+ isEnabled = true;
149
+ }
150
+
151
+ /**
152
+ * Stops reporting Reanimated errors and warnings to Observe. Reanimated's logger keeps its
153
+ * configuration.
154
+ */
155
+ export function disableReanimatedIntegration(): void {
156
+ isEnabled = false;
157
+ }
@@ -0,0 +1,29 @@
1
+ // react-native-reanimated is an optional peerDependency of expo-observe. It is required lazily,
2
+ // only when the integration is enabled, so an app that doesn't enable it never loads Reanimated
3
+ // through expo-observe. This lives in its own file because a `require` wrapped in try/catch
4
+ // doesn't work with Metro's `inlineRequires` when it shares a file with other imports (see
5
+ // expo-gl's `GLWorkletContextManager`).
6
+
7
+ export type ReanimatedLogData = { level: number; message: string };
8
+
9
+ export type ReanimatedLoggerConfig = { level?: number; strict?: boolean };
10
+
11
+ export interface LoadedReanimated {
12
+ version: string;
13
+ configureReanimatedLogger(
14
+ config: ReanimatedLoggerConfig,
15
+ onLog?: (data: ReanimatedLogData) => void
16
+ ): void;
17
+ }
18
+
19
+ export function loadReanimated(): LoadedReanimated | null {
20
+ try {
21
+ const { reanimatedVersion, configureReanimatedLogger } = require('react-native-reanimated') as {
22
+ reanimatedVersion: string;
23
+ configureReanimatedLogger: LoadedReanimated['configureReanimatedLogger'];
24
+ };
25
+ return { version: reanimatedVersion, configureReanimatedLogger };
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
@@ -0,0 +1,28 @@
1
+ const STACK_FRAME_PATTERN = /^\s*at /;
2
+
3
+ function escapeRegExp(text: string): string {
4
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
5
+ }
6
+
7
+ /**
8
+ * Removes the first frame of the stack when it is a frame of `functionName`. The message can
9
+ * contain newlines, so the first frame is the first line that is a stack frame.
10
+ */
11
+ export function removeFirstFrame(
12
+ stack: string | undefined,
13
+ functionName: string
14
+ ): string | undefined {
15
+ if (stack === undefined) {
16
+ return undefined;
17
+ }
18
+ const lines = stack.split('\n');
19
+ const firstFrameIndex = lines.findIndex((line) => STACK_FRAME_PATTERN.test(line));
20
+ if (firstFrameIndex === -1) {
21
+ return stack;
22
+ }
23
+ const functionFramePattern = new RegExp(`^\\s*at ${escapeRegExp(functionName)} `);
24
+ if (!functionFramePattern.test(lines[firstFrameIndex] ?? '')) {
25
+ return stack;
26
+ }
27
+ return [...lines.slice(0, firstFrameIndex), ...lines.slice(firstFrameIndex + 1)].join('\n');
28
+ }
package/src/module.ts CHANGED
@@ -3,6 +3,10 @@ import AppMetrics, { setErrorHandlerEnabled } from 'expo-app-metrics';
3
3
 
4
4
  import { initRouterIntegration } from './integrations/expo-router/init';
5
5
  import { isRouterInstalled } from './integrations/expo-router/router';
6
+ import {
7
+ disableReanimatedIntegration,
8
+ initReanimatedIntegration,
9
+ } from './integrations/react-native-reanimated/init';
6
10
  import { initReactNavigationIntegration } from './integrations/react-navigation/init';
7
11
  import { isReactNavigationInstalled } from './integrations/react-navigation/reactNavigation';
8
12
  import { reportCaughtError } from './reportCaughtError';
@@ -80,6 +84,12 @@ const Observe: ObserveModule = new Proxy(native, {
80
84
  } else if (shouldInitReactNavigationIntegration) {
81
85
  initReactNavigationIntegration(config.integrations?.['react-navigation']);
82
86
  }
87
+ const reanimatedConfig = config.integrations?.['react-native-reanimated'];
88
+ if (reanimatedConfig) {
89
+ initReanimatedIntegration(reanimatedConfig);
90
+ } else {
91
+ disableReanimatedIntegration();
92
+ }
83
93
  return target.configure(config);
84
94
  };
85
95
  }
@@ -36,16 +36,20 @@ function normalizeReportedError(error: unknown): NormalizedReportedError {
36
36
  }
37
37
 
38
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.
39
+ * Reports a caught value as a non-fatal error through the AppMetrics module, shared by the native
40
+ * and web `Observe.reportError` implementations and by integrations. `source` is `reportedByUser`
41
+ * for an error the app reports, and `integration` for an error an integration reports for a library.
41
42
  *
42
43
  * Never throws: it's called from a `catch` block, so a failure here (a pathological thrown value, or
43
44
  * a native call that rejects the payload) must not turn a handled error into an unhandled one.
44
45
  */
45
- export function reportCaughtError(error: unknown): void {
46
+ export function reportCaughtError(
47
+ error: unknown,
48
+ source: 'reportedByUser' | 'integration' = 'reportedByUser'
49
+ ): void {
46
50
  try {
47
51
  AppMetrics.reportError({
48
- source: 'reportedByUser',
52
+ source,
49
53
  ...normalizeReportedError(error),
50
54
  isFatal: false,
51
55
  });
package/src/types.ts CHANGED
@@ -150,8 +150,49 @@ export interface ObserveIntegrationsConfig {
150
150
  * @default false
151
151
  */
152
152
  'react-navigation'?: boolean | ObserveNavigationIntegrationConfig;
153
+ /**
154
+ * Enables the `react-native-reanimated` integration, which reports Reanimated errors to Observe
155
+ * as errors named `reanimated.error`. To also report Reanimated warnings as events named
156
+ * `reanimated.warning`, set `reportWarnings`. Reanimated still prints errors and warnings to the
157
+ * console. The integration reports each distinct message at most once every 15 minutes. It
158
+ * reports at most 20 distinct errors and 20 distinct warnings in any 15-minute period.
159
+ *
160
+ * Requires `react-native-reanimated` 4.7.0 or later to be installed.
161
+ *
162
+ * The integration configures Reanimated's logger with `configureReanimatedLogger`. To set the
163
+ * logger's `level` and `strict` options, pass an object here. Don't call
164
+ * `configureReanimatedLogger` in your app. A later call removes the integration's callback, and
165
+ * the integration replaces the `level` and `strict` options of an earlier call.
166
+ *
167
+ * @default false
168
+ */
169
+ 'react-native-reanimated'?: boolean | ObserveReanimatedIntegrationConfig;
153
170
  }
154
171
 
172
+ export type ObserveReanimatedIntegrationConfig = {
173
+ /**
174
+ * The minimum log level that Reanimated prints and reports. Pass a `ReanimatedLogLevel` value
175
+ * from `react-native-reanimated`: `ReanimatedLogLevel.warn` (`1`) or `ReanimatedLogLevel.error`
176
+ * (`2`).
177
+ *
178
+ * @default ReanimatedLogLevel.warn
179
+ */
180
+ level?: 1 | 2;
181
+ /**
182
+ * Whether Reanimated also prints and reports its strict-mode warnings.
183
+ *
184
+ * @default true
185
+ */
186
+ strict?: boolean;
187
+ /**
188
+ * Whether the integration also reports Reanimated warnings to Observe, as events named
189
+ * `reanimated.warning`. Reanimated prints warnings to the console either way.
190
+ *
191
+ * @default false
192
+ */
193
+ reportWarnings?: boolean;
194
+ };
195
+
155
196
  /**
156
197
  * Events emitted by the native `ExpoObserve` module.
157
198
  */
@@ -1 +0,0 @@
1
- 2fe94a3000018f70581d3ab1c396164525960de1ecf98c266d1e24689d915150
@@ -1 +0,0 @@
1
- dc0f3f96ba7e53fe360f9d84663b73f314427d709b5923e49a8d95088bf66280660405b777ed342e2e14f969619abfc2b2aa4116e0ac0567b6931cba3b7a05c4
@@ -1 +0,0 @@
1
- d0286094f7cdc9325087139447ec5ec6c4563901d7229252a4460fc64379a0f1
@@ -1 +0,0 @@
1
- 011e6ad1efa46ed83a7c1f27b8e240a49e2875d3c712d61dce2269207330b9cf94fc0d8a4a3295b31d450cca9a773bc22f94f3d5ece0f5ec57399e902ea878ae
@@ -1 +0,0 @@
1
- e9476a7fc0643a6caf1a8aea50792a3be158a7d69cac46e525832da4e7d07d05
@@ -1 +0,0 @@
1
- e36d1782fd020027e4811bb6a2496d48b5758df93463f0e052de99ddafe602cc29f2bc5e1ddbec20f3031d2bd6ee07e1cdc72eb78b39a58bb42fa2398ef00372
@@ -1 +0,0 @@
1
- 22e1c654c571938460b3fdf9b2a21f101a7c9b0ee1dc6765171080dbae436c52
@@ -1 +0,0 @@
1
- 796a8c93a6920a0899e054ce91d6fc83274f66e8bb30429f04dbf539134f9fdd61e5da440fa9dc0817a7899a5ca5b12b7d5b74d30ef7d356d183a13a623a3946