@logbrew/react-native 0.1.9 → 0.1.10
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.
- package/README.md +44 -2
- package/global-errors.cjs +4 -2
- package/global-errors.d.cts +63 -0
- package/global-errors.d.ts +63 -0
- package/global-errors.js +2 -1
- package/global-errors.native.js +63 -2
- package/index.native.d.ts +4 -1
- package/index.native.js +2 -1
- package/package.json +1 -1
- package/promise-rejections.cjs +128 -4
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
React Native helpers for the public LogBrew JavaScript SDK.
|
|
8
8
|
|
|
9
|
-
This package is intentionally thin. It keeps all event validation, retry, flush, and shutdown behavior in `@logbrew/sdk`, while adding mobile-friendly helpers for screen views, app-state changes, product actions, API milestones, handled JavaScript errors, app-owned Promise rejection
|
|
9
|
+
This package is intentionally thin. It keeps all event validation, retry, flush, and shutdown behavior in `@logbrew/sdk`, while adding mobile-friendly helpers for screen views, app-state changes, product actions, API milestones, handled JavaScript errors, opt-in Hermes Promise rejection tracking, app-owned Promise rejection callbacks, provider/hook usage, active W3C trace correlation, explicit W3C trace propagation, opt-in lifecycle spans, opt-in resource fetch spans, opt-in reversible global fetch spans, app-owned native bridge scope sync, and reversible instrumentation setup.
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
@@ -202,7 +202,49 @@ Installation is idempotent for the active React Native `ErrorUtils` object. The
|
|
|
202
202
|
|
|
203
203
|
The React Native conditional export obtains LogBrew's synchronous native fatal store through the supported TurboModule or `NativeModules` seam. Before chaining a fatal report, it writes one bounded record to app-private storage that is excluded from operating-system archives. On a later installation it performs stable-ID at-least-once replay, and acknowledgement happens only after local queue admission is observable through the SDK queue counters. Filtered, dropped, unknown-admission, persistence-failed, and acknowledgement-failed records are retained. A failed acknowledgement is retried without admitting the same ID twice in one JavaScript runtime. Use `fatalHealth()` for frozen bounded counters and status, or `discardPendingFatalRecord()` for an explicit rollback discard. The Node ESM and CommonJS entries never import React Native; non-React-Native callers must inject `fatalStore` explicitly.
|
|
204
204
|
|
|
205
|
-
Automatic events exclude the original error message, raw stack, arbitrary metadata, full URLs, hosts, query strings, local absolute paths, payloads, and native error text. `onDiagnostic` receives only a fixed code. This integration does not claim mathematically exactly-once delivery, backend-visible deduplication, native crash capture,
|
|
205
|
+
Automatic events exclude the original error message, raw stack, arbitrary metadata, full URLs, hosts, query strings, local absolute paths, payloads, and native error text. `onDiagnostic` receives only a fixed code. This integration does not claim mathematically exactly-once delivery, backend-visible deduplication, native crash capture, ANR or hang detection, general offline queueing, or symbolication.
|
|
206
|
+
|
|
207
|
+
### Opt-in Hermes Promise rejection tracking
|
|
208
|
+
|
|
209
|
+
React Native exposes one Promise rejection tracker slot. Claim it explicitly
|
|
210
|
+
when LogBrew is the only tracker owner:
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
import {
|
|
214
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
215
|
+
} from "@logbrew/react-native";
|
|
216
|
+
|
|
217
|
+
const promiseRejectionTracker =
|
|
218
|
+
installLogBrewReactNativePromiseRejectionTracker({
|
|
219
|
+
client,
|
|
220
|
+
takeOwnership: true,
|
|
221
|
+
onDiagnostic({ code }) {
|
|
222
|
+
console.warn(`LogBrew Promise rejection tracker: ${code}`);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
promiseRejectionTracker.health();
|
|
227
|
+
promiseRejectionTracker.rejectionHealth();
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The React Native export discovers the active Hermes runtime and uses its
|
|
231
|
+
native tracker without replacing `globalThis.Promise`. Installation is
|
|
232
|
+
idempotent for that runtime slot. It records fixed-content issues without
|
|
233
|
+
reading the rejection value or emitting the runtime rejection identifier.
|
|
234
|
+
`rejectionHealth()` returns bounded duplicate, eviction, and later-handled
|
|
235
|
+
counters.
|
|
236
|
+
|
|
237
|
+
Do not install this helper while Sentry or another integration owns the same
|
|
238
|
+
tracker slot. Use the app-owned callback composition below when another
|
|
239
|
+
integration must remain the owner. Hermes does not expose a previous-owner
|
|
240
|
+
restoration API. `deactivate()` therefore stops LogBrew capture through its
|
|
241
|
+
installed callbacks but cannot reinstate an earlier tracker. Install
|
|
242
|
+
the replacement owner after deactivation when switching integrations.
|
|
243
|
+
|
|
244
|
+
For JavaScriptCore or another runtime, pass an explicit `tracker` with an
|
|
245
|
+
`enable(options)` function that already controls the Promise implementation
|
|
246
|
+
used by the app. LogBrew does not replace the global Promise or add a hidden
|
|
247
|
+
Promise polyfill.
|
|
206
248
|
|
|
207
249
|
### App-owned Promise rejection reports
|
|
208
250
|
|
package/global-errors.cjs
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const { createReactNativeErrorEvent } = require("./index.cjs");
|
|
4
4
|
const { createFatalController } = require("./fatal-replay.cjs");
|
|
5
5
|
const {
|
|
6
|
-
createLogBrewReactNativePromiseRejectionHandlers
|
|
6
|
+
createLogBrewReactNativePromiseRejectionHandlers,
|
|
7
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
7
8
|
} = require("./promise-rejections.cjs");
|
|
8
9
|
|
|
9
10
|
const AUTOMATIC_ERROR_MESSAGE = "React Native global JavaScript report";
|
|
@@ -412,7 +413,8 @@ function isObjectLike(value) {
|
|
|
412
413
|
|
|
413
414
|
const defaultExport = {
|
|
414
415
|
createLogBrewReactNativePromiseRejectionHandlers,
|
|
415
|
-
installLogBrewReactNativeGlobalErrorHandler
|
|
416
|
+
installLogBrewReactNativeGlobalErrorHandler,
|
|
417
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
416
418
|
};
|
|
417
419
|
|
|
418
420
|
module.exports = { ...defaultExport, default: defaultExport };
|
package/global-errors.d.cts
CHANGED
|
@@ -14,6 +14,9 @@ export type ReactNativePromiseRejectionDiagnosticCode =
|
|
|
14
14
|
| "promise_rejection_handler_unavailable"
|
|
15
15
|
| "promise_rejection_id_unavailable"
|
|
16
16
|
| "promise_rejection_recursive_capture_suppressed"
|
|
17
|
+
| "promise_rejection_tracker_installation_failed"
|
|
18
|
+
| "promise_rejection_tracker_ownership_required"
|
|
19
|
+
| "promise_rejection_tracker_unavailable"
|
|
17
20
|
| "promise_rejection_tracking_evicted";
|
|
18
21
|
|
|
19
22
|
export type ReactNativePromiseRejectionDiagnostic = Readonly<{
|
|
@@ -96,6 +99,54 @@ export type LogBrewReactNativePromiseRejectionHandlers = Readonly<{
|
|
|
96
99
|
onUnhandled(runtimeRejectionId: unknown, rejection?: unknown): void;
|
|
97
100
|
}>;
|
|
98
101
|
|
|
102
|
+
export type ReactNativePromiseRejectionTrackerLike = {
|
|
103
|
+
enable(options: Readonly<{
|
|
104
|
+
allRejections: true;
|
|
105
|
+
onHandled(runtimeRejectionId: unknown): void;
|
|
106
|
+
onUnhandled(runtimeRejectionId: unknown, rejection?: unknown): void;
|
|
107
|
+
}>): void;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type InstallLogBrewReactNativePromiseRejectionTrackerOptions = {
|
|
111
|
+
client: Pick<LogBrewClient, "issue">;
|
|
112
|
+
/**
|
|
113
|
+
* Confirms that LogBrew may claim the runtime's single Promise rejection
|
|
114
|
+
* tracker slot. Do not install another tracker owner at the same time.
|
|
115
|
+
*/
|
|
116
|
+
takeOwnership: true;
|
|
117
|
+
/**
|
|
118
|
+
* Optional tracker seam. The React Native conditional export discovers the
|
|
119
|
+
* active Hermes tracker when this is omitted.
|
|
120
|
+
*/
|
|
121
|
+
tracker?: ReactNativePromiseRejectionTrackerLike;
|
|
122
|
+
maxTrackedRejections?: number;
|
|
123
|
+
onDiagnostic?: (diagnostic: ReactNativePromiseRejectionDiagnostic) => void;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type ReactNativePromiseRejectionTrackerHealth = Readonly<{
|
|
127
|
+
active: boolean;
|
|
128
|
+
available: boolean;
|
|
129
|
+
engine: "custom" | "hermes" | "unavailable";
|
|
130
|
+
lastOutcome:
|
|
131
|
+
| "deactivated"
|
|
132
|
+
| "handler_unavailable"
|
|
133
|
+
| "installation_failed"
|
|
134
|
+
| "installed"
|
|
135
|
+
| "ownership_required"
|
|
136
|
+
| "tracker_unavailable";
|
|
137
|
+
restoration: "deactivate_only";
|
|
138
|
+
}>;
|
|
139
|
+
|
|
140
|
+
export type LogBrewReactNativePromiseRejectionTrackerInstallation = Readonly<{
|
|
141
|
+
/**
|
|
142
|
+
* Stops LogBrew capture through the installed callbacks. Hermes does not
|
|
143
|
+
* expose a previous-owner restoration API, so this cannot restore one.
|
|
144
|
+
*/
|
|
145
|
+
deactivate(): boolean;
|
|
146
|
+
health(): ReactNativePromiseRejectionTrackerHealth;
|
|
147
|
+
rejectionHealth(): ReactNativePromiseRejectionHealth;
|
|
148
|
+
}>;
|
|
149
|
+
|
|
99
150
|
export type ReactNativeFatalRecord = Readonly<{
|
|
100
151
|
corruptRecords: number;
|
|
101
152
|
droppedRecords: number;
|
|
@@ -175,6 +226,17 @@ export declare function createLogBrewReactNativePromiseRejectionHandlers(
|
|
|
175
226
|
options: CreateLogBrewReactNativePromiseRejectionHandlersOptions
|
|
176
227
|
): LogBrewReactNativePromiseRejectionHandlers;
|
|
177
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Install privacy-safe automatic Promise rejection capture.
|
|
231
|
+
*
|
|
232
|
+
* Installation is opt-in because React Native runtimes expose one tracker slot.
|
|
233
|
+
* The React Native conditional export discovers Hermes without replacing the
|
|
234
|
+
* global Promise. Other runtimes can inject a compatible tracker explicitly.
|
|
235
|
+
*/
|
|
236
|
+
export declare function installLogBrewReactNativePromiseRejectionTracker(
|
|
237
|
+
options: InstallLogBrewReactNativePromiseRejectionTrackerOptions
|
|
238
|
+
): LogBrewReactNativePromiseRejectionTrackerInstallation;
|
|
239
|
+
|
|
178
240
|
/**
|
|
179
241
|
* Install reversible automatic capture for React Native global JavaScript errors.
|
|
180
242
|
*
|
|
@@ -190,6 +252,7 @@ export declare function installLogBrewReactNativeGlobalErrorHandler(
|
|
|
190
252
|
declare const logBrewReactNativeGlobalErrors: {
|
|
191
253
|
createLogBrewReactNativePromiseRejectionHandlers: typeof createLogBrewReactNativePromiseRejectionHandlers;
|
|
192
254
|
installLogBrewReactNativeGlobalErrorHandler: typeof installLogBrewReactNativeGlobalErrorHandler;
|
|
255
|
+
installLogBrewReactNativePromiseRejectionTracker: typeof installLogBrewReactNativePromiseRejectionTracker;
|
|
193
256
|
};
|
|
194
257
|
|
|
195
258
|
export default logBrewReactNativeGlobalErrors;
|
package/global-errors.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ export type ReactNativePromiseRejectionDiagnosticCode =
|
|
|
14
14
|
| "promise_rejection_handler_unavailable"
|
|
15
15
|
| "promise_rejection_id_unavailable"
|
|
16
16
|
| "promise_rejection_recursive_capture_suppressed"
|
|
17
|
+
| "promise_rejection_tracker_installation_failed"
|
|
18
|
+
| "promise_rejection_tracker_ownership_required"
|
|
19
|
+
| "promise_rejection_tracker_unavailable"
|
|
17
20
|
| "promise_rejection_tracking_evicted";
|
|
18
21
|
|
|
19
22
|
export type ReactNativePromiseRejectionDiagnostic = Readonly<{
|
|
@@ -96,6 +99,54 @@ export type LogBrewReactNativePromiseRejectionHandlers = Readonly<{
|
|
|
96
99
|
onUnhandled(runtimeRejectionId: unknown, rejection?: unknown): void;
|
|
97
100
|
}>;
|
|
98
101
|
|
|
102
|
+
export type ReactNativePromiseRejectionTrackerLike = {
|
|
103
|
+
enable(options: Readonly<{
|
|
104
|
+
allRejections: true;
|
|
105
|
+
onHandled(runtimeRejectionId: unknown): void;
|
|
106
|
+
onUnhandled(runtimeRejectionId: unknown, rejection?: unknown): void;
|
|
107
|
+
}>): void;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type InstallLogBrewReactNativePromiseRejectionTrackerOptions = {
|
|
111
|
+
client: Pick<LogBrewClient, "issue">;
|
|
112
|
+
/**
|
|
113
|
+
* Confirms that LogBrew may claim the runtime's single Promise rejection
|
|
114
|
+
* tracker slot. Do not install another tracker owner at the same time.
|
|
115
|
+
*/
|
|
116
|
+
takeOwnership: true;
|
|
117
|
+
/**
|
|
118
|
+
* Optional tracker seam. The React Native conditional export discovers the
|
|
119
|
+
* active Hermes tracker when this is omitted.
|
|
120
|
+
*/
|
|
121
|
+
tracker?: ReactNativePromiseRejectionTrackerLike;
|
|
122
|
+
maxTrackedRejections?: number;
|
|
123
|
+
onDiagnostic?: (diagnostic: ReactNativePromiseRejectionDiagnostic) => void;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type ReactNativePromiseRejectionTrackerHealth = Readonly<{
|
|
127
|
+
active: boolean;
|
|
128
|
+
available: boolean;
|
|
129
|
+
engine: "custom" | "hermes" | "unavailable";
|
|
130
|
+
lastOutcome:
|
|
131
|
+
| "deactivated"
|
|
132
|
+
| "handler_unavailable"
|
|
133
|
+
| "installation_failed"
|
|
134
|
+
| "installed"
|
|
135
|
+
| "ownership_required"
|
|
136
|
+
| "tracker_unavailable";
|
|
137
|
+
restoration: "deactivate_only";
|
|
138
|
+
}>;
|
|
139
|
+
|
|
140
|
+
export type LogBrewReactNativePromiseRejectionTrackerInstallation = Readonly<{
|
|
141
|
+
/**
|
|
142
|
+
* Stops LogBrew capture through the installed callbacks. Hermes does not
|
|
143
|
+
* expose a previous-owner restoration API, so this cannot restore one.
|
|
144
|
+
*/
|
|
145
|
+
deactivate(): boolean;
|
|
146
|
+
health(): ReactNativePromiseRejectionTrackerHealth;
|
|
147
|
+
rejectionHealth(): ReactNativePromiseRejectionHealth;
|
|
148
|
+
}>;
|
|
149
|
+
|
|
99
150
|
export type ReactNativeFatalRecord = Readonly<{
|
|
100
151
|
corruptRecords: number;
|
|
101
152
|
droppedRecords: number;
|
|
@@ -175,6 +226,17 @@ export declare function createLogBrewReactNativePromiseRejectionHandlers(
|
|
|
175
226
|
options: CreateLogBrewReactNativePromiseRejectionHandlersOptions
|
|
176
227
|
): LogBrewReactNativePromiseRejectionHandlers;
|
|
177
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Install privacy-safe automatic Promise rejection capture.
|
|
231
|
+
*
|
|
232
|
+
* Installation is opt-in because React Native runtimes expose one tracker slot.
|
|
233
|
+
* The React Native conditional export discovers Hermes without replacing the
|
|
234
|
+
* global Promise. Other runtimes can inject a compatible tracker explicitly.
|
|
235
|
+
*/
|
|
236
|
+
export declare function installLogBrewReactNativePromiseRejectionTracker(
|
|
237
|
+
options: InstallLogBrewReactNativePromiseRejectionTrackerOptions
|
|
238
|
+
): LogBrewReactNativePromiseRejectionTrackerInstallation;
|
|
239
|
+
|
|
178
240
|
/**
|
|
179
241
|
* Install reversible automatic capture for React Native global JavaScript errors.
|
|
180
242
|
*
|
|
@@ -190,6 +252,7 @@ export declare function installLogBrewReactNativeGlobalErrorHandler(
|
|
|
190
252
|
declare const logBrewReactNativeGlobalErrors: {
|
|
191
253
|
createLogBrewReactNativePromiseRejectionHandlers: typeof createLogBrewReactNativePromiseRejectionHandlers;
|
|
192
254
|
installLogBrewReactNativeGlobalErrorHandler: typeof installLogBrewReactNativeGlobalErrorHandler;
|
|
255
|
+
installLogBrewReactNativePromiseRejectionTracker: typeof installLogBrewReactNativePromiseRejectionTracker;
|
|
193
256
|
};
|
|
194
257
|
|
|
195
258
|
export default logBrewReactNativeGlobalErrors;
|
package/global-errors.js
CHANGED
|
@@ -2,7 +2,8 @@ import implementation from "./global-errors.cjs";
|
|
|
2
2
|
|
|
3
3
|
export const {
|
|
4
4
|
createLogBrewReactNativePromiseRejectionHandlers,
|
|
5
|
-
installLogBrewReactNativeGlobalErrorHandler
|
|
5
|
+
installLogBrewReactNativeGlobalErrorHandler,
|
|
6
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
6
7
|
} = implementation;
|
|
7
8
|
|
|
8
9
|
export default implementation;
|
package/global-errors.native.js
CHANGED
|
@@ -2,13 +2,16 @@ import { NativeModules, TurboModuleRegistry } from "react-native";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
createLogBrewReactNativePromiseRejectionHandlers,
|
|
5
|
-
installLogBrewReactNativeGlobalErrorHandler as installPlatformNeutralHandler
|
|
5
|
+
installLogBrewReactNativeGlobalErrorHandler as installPlatformNeutralHandler,
|
|
6
|
+
installLogBrewReactNativePromiseRejectionTracker as installPlatformNeutralTracker
|
|
6
7
|
} from "./global-errors.js";
|
|
7
8
|
|
|
8
9
|
export {
|
|
9
10
|
createLogBrewReactNativePromiseRejectionHandlers
|
|
10
11
|
};
|
|
11
12
|
|
|
13
|
+
const hermesTrackerAdapters = new WeakMap();
|
|
14
|
+
|
|
12
15
|
function defaultFatalStore() {
|
|
13
16
|
try {
|
|
14
17
|
return TurboModuleRegistry?.get?.("LogBrewFatalStore")
|
|
@@ -18,6 +21,40 @@ function defaultFatalStore() {
|
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
function defaultPromiseRejectionTracker() {
|
|
25
|
+
let hermes;
|
|
26
|
+
let enable;
|
|
27
|
+
let hasPromise;
|
|
28
|
+
try {
|
|
29
|
+
hermes = globalThis?.HermesInternal;
|
|
30
|
+
if (hermes === null
|
|
31
|
+
|| (typeof hermes !== "object" && typeof hermes !== "function")) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
enable = hermes.enablePromiseRejectionTracker;
|
|
35
|
+
hasPromise = hermes.hasPromise;
|
|
36
|
+
if (typeof enable !== "function"
|
|
37
|
+
|| typeof hasPromise !== "function"
|
|
38
|
+
|| hasPromise.call(hermes) !== true) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const existing = hermesTrackerAdapters.get(hermes);
|
|
46
|
+
if (existing) {
|
|
47
|
+
return existing;
|
|
48
|
+
}
|
|
49
|
+
const adapter = Object.freeze({
|
|
50
|
+
enable(options) {
|
|
51
|
+
return enable.call(hermes, options);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
hermesTrackerAdapters.set(hermes, adapter);
|
|
55
|
+
return adapter;
|
|
56
|
+
}
|
|
57
|
+
|
|
21
58
|
export function installLogBrewReactNativeGlobalErrorHandler(options = {}) {
|
|
22
59
|
let forwarded;
|
|
23
60
|
let hasInjectedStore = false;
|
|
@@ -37,9 +74,33 @@ export function installLogBrewReactNativeGlobalErrorHandler(options = {}) {
|
|
|
37
74
|
});
|
|
38
75
|
}
|
|
39
76
|
|
|
77
|
+
export function installLogBrewReactNativePromiseRejectionTracker(options = {}) {
|
|
78
|
+
let forwarded;
|
|
79
|
+
let hasInjectedTracker = false;
|
|
80
|
+
try {
|
|
81
|
+
const input = options !== null
|
|
82
|
+
&& (typeof options === "object" || typeof options === "function")
|
|
83
|
+
? options
|
|
84
|
+
: {};
|
|
85
|
+
hasInjectedTracker = Object.prototype.hasOwnProperty.call(input, "tracker");
|
|
86
|
+
forwarded = { ...input };
|
|
87
|
+
} catch {
|
|
88
|
+
forwarded = {};
|
|
89
|
+
}
|
|
90
|
+
const tracker = hasInjectedTracker
|
|
91
|
+
? forwarded.tracker
|
|
92
|
+
: defaultPromiseRejectionTracker();
|
|
93
|
+
return installPlatformNeutralTracker({
|
|
94
|
+
...forwarded,
|
|
95
|
+
tracker,
|
|
96
|
+
trackerKind: hasInjectedTracker ? "custom" : tracker ? "hermes" : undefined
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
40
100
|
const logBrewReactNativeGlobalErrors = {
|
|
41
101
|
createLogBrewReactNativePromiseRejectionHandlers,
|
|
42
|
-
installLogBrewReactNativeGlobalErrorHandler
|
|
102
|
+
installLogBrewReactNativeGlobalErrorHandler,
|
|
103
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
43
104
|
};
|
|
44
105
|
|
|
45
106
|
export default logBrewReactNativeGlobalErrors;
|
package/index.native.d.ts
CHANGED
package/index.native.js
CHANGED
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
getReactNativeContext
|
|
11
11
|
} from "./index.js";
|
|
12
12
|
export {
|
|
13
|
-
installLogBrewReactNativeGlobalErrorHandler
|
|
13
|
+
installLogBrewReactNativeGlobalErrorHandler,
|
|
14
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
14
15
|
} from "./global-errors.native.js";
|
|
15
16
|
|
|
16
17
|
export * from "./index.js";
|
package/package.json
CHANGED
package/promise-rejections.cjs
CHANGED
|
@@ -6,9 +6,17 @@ const AUTOMATIC_MESSAGE = "Unhandled Promise rejection";
|
|
|
6
6
|
const DEFAULT_MAX_TRACKED_REJECTIONS = 128;
|
|
7
7
|
const MAX_TRACKED_REJECTIONS = 1024;
|
|
8
8
|
const MAX_STRING_ID_LENGTH = 128;
|
|
9
|
+
const activeTrackerInstallations = new WeakMap();
|
|
9
10
|
let nextEventSequence = 0;
|
|
10
11
|
|
|
11
12
|
function createLogBrewReactNativePromiseRejectionHandlers(options = {}) {
|
|
13
|
+
return createPromiseRejectionHandlers(
|
|
14
|
+
options,
|
|
15
|
+
"app_owned_promise_rejection_tracker"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createPromiseRejectionHandlers(options, mechanism) {
|
|
12
20
|
const input = isObjectLike(options) ? options : {};
|
|
13
21
|
const client = safeReadProperty(input, "client");
|
|
14
22
|
const issue = safeFunction(client, "issue");
|
|
@@ -92,7 +100,7 @@ function createLogBrewReactNativePromiseRejectionHandlers(options = {}) {
|
|
|
92
100
|
if (key === undefined) {
|
|
93
101
|
emitStateDiagnostic(state, onDiagnostic, "promise_rejection_id_unavailable");
|
|
94
102
|
}
|
|
95
|
-
const event = createEvent();
|
|
103
|
+
const event = createEvent(mechanism);
|
|
96
104
|
issue.call(client, event.id, event.timestamp, event.attributes);
|
|
97
105
|
state.capturedEvents = incrementBounded(state.capturedEvents);
|
|
98
106
|
|
|
@@ -124,7 +132,93 @@ function createLogBrewReactNativePromiseRejectionHandlers(options = {}) {
|
|
|
124
132
|
});
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
function
|
|
135
|
+
function installLogBrewReactNativePromiseRejectionTracker(options = {}) {
|
|
136
|
+
const input = isObjectLike(options) ? options : {};
|
|
137
|
+
const onDiagnostic = safeFunction(input, "onDiagnostic");
|
|
138
|
+
if (safeReadProperty(input, "takeOwnership") !== true) {
|
|
139
|
+
emitDiagnostic(onDiagnostic, "promise_rejection_tracker_ownership_required");
|
|
140
|
+
return inactiveTrackerInstallation("ownership_required");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const tracker = safeReadProperty(input, "tracker");
|
|
144
|
+
const enable = safeFunction(tracker, "enable");
|
|
145
|
+
const engine = safeReadProperty(input, "trackerKind") === "hermes"
|
|
146
|
+
? "hermes"
|
|
147
|
+
: isObjectLike(tracker) ? "custom" : "unavailable";
|
|
148
|
+
if (!isObjectLike(tracker) || !enable) {
|
|
149
|
+
emitDiagnostic(onDiagnostic, "promise_rejection_tracker_unavailable");
|
|
150
|
+
return inactiveTrackerInstallation("tracker_unavailable", engine);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const existing = activeTrackerInstallations.get(tracker);
|
|
154
|
+
if (existing?.health().active) {
|
|
155
|
+
return existing;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const handlers = createPromiseRejectionHandlers(
|
|
159
|
+
input,
|
|
160
|
+
"logbrew_owned_promise_rejection_tracker"
|
|
161
|
+
);
|
|
162
|
+
if (!handlers.health().available) {
|
|
163
|
+
return inactiveTrackerInstallation("handler_unavailable", engine, handlers);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const state = {
|
|
167
|
+
active: true,
|
|
168
|
+
lastOutcome: "installed"
|
|
169
|
+
};
|
|
170
|
+
const trackerOptions = Object.freeze({
|
|
171
|
+
allRejections: true,
|
|
172
|
+
onHandled(runtimeRejectionId) {
|
|
173
|
+
if (state.active) {
|
|
174
|
+
handlers.onHandled(runtimeRejectionId);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
onUnhandled(runtimeRejectionId, rejection) {
|
|
178
|
+
if (state.active) {
|
|
179
|
+
handlers.onUnhandled(runtimeRejectionId, rejection);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const installation = Object.freeze({
|
|
185
|
+
deactivate() {
|
|
186
|
+
if (!state.active) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
state.active = false;
|
|
190
|
+
state.lastOutcome = "deactivated";
|
|
191
|
+
if (activeTrackerInstallations.get(tracker) === installation) {
|
|
192
|
+
activeTrackerInstallations.delete(tracker);
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
},
|
|
196
|
+
health() {
|
|
197
|
+
return trackerHealthSnapshot(state, engine, true);
|
|
198
|
+
},
|
|
199
|
+
rejectionHealth() {
|
|
200
|
+
return handlers.health();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
enable.call(tracker, trackerOptions);
|
|
206
|
+
} catch {
|
|
207
|
+
state.active = false;
|
|
208
|
+
state.lastOutcome = "installation_failed";
|
|
209
|
+
emitDiagnostic(onDiagnostic, "promise_rejection_tracker_installation_failed");
|
|
210
|
+
return inactiveTrackerInstallation(
|
|
211
|
+
"installation_failed",
|
|
212
|
+
engine,
|
|
213
|
+
handlers
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
activeTrackerInstallations.set(tracker, installation);
|
|
218
|
+
return installation;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function createEvent(mechanism) {
|
|
128
222
|
const error = new Error(AUTOMATIC_MESSAGE);
|
|
129
223
|
delete error.stack;
|
|
130
224
|
const event = createReactNativeErrorEvent(error, {
|
|
@@ -142,7 +236,7 @@ function createEvent() {
|
|
|
142
236
|
automatic: true,
|
|
143
237
|
fatal: false,
|
|
144
238
|
handled: false,
|
|
145
|
-
mechanism
|
|
239
|
+
mechanism,
|
|
146
240
|
source: "react-native.promise_rejection"
|
|
147
241
|
}
|
|
148
242
|
}
|
|
@@ -224,6 +318,35 @@ function inactiveHandlers() {
|
|
|
224
318
|
});
|
|
225
319
|
}
|
|
226
320
|
|
|
321
|
+
function inactiveTrackerInstallation(
|
|
322
|
+
lastOutcome,
|
|
323
|
+
engine = "unavailable",
|
|
324
|
+
handlers = inactiveHandlers()
|
|
325
|
+
) {
|
|
326
|
+
const snapshot = Object.freeze({
|
|
327
|
+
active: false,
|
|
328
|
+
available: false,
|
|
329
|
+
engine,
|
|
330
|
+
lastOutcome,
|
|
331
|
+
restoration: "deactivate_only"
|
|
332
|
+
});
|
|
333
|
+
return Object.freeze({
|
|
334
|
+
deactivate: () => false,
|
|
335
|
+
health: () => snapshot,
|
|
336
|
+
rejectionHealth: () => handlers.health()
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function trackerHealthSnapshot(state, engine, available) {
|
|
341
|
+
return Object.freeze({
|
|
342
|
+
active: state.active,
|
|
343
|
+
available,
|
|
344
|
+
engine,
|
|
345
|
+
lastOutcome: state.lastOutcome,
|
|
346
|
+
restoration: "deactivate_only"
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
227
350
|
function healthSnapshot(state, trackedRejections, maxTrackedRejections) {
|
|
228
351
|
return Object.freeze({
|
|
229
352
|
available: true,
|
|
@@ -267,5 +390,6 @@ function isObjectLike(value) {
|
|
|
267
390
|
}
|
|
268
391
|
|
|
269
392
|
module.exports = {
|
|
270
|
-
createLogBrewReactNativePromiseRejectionHandlers
|
|
393
|
+
createLogBrewReactNativePromiseRejectionHandlers,
|
|
394
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
271
395
|
};
|