@logbrew/react-native 0.1.9 → 0.1.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.
- package/README.md +111 -2
- package/android/src/main/java/co/logbrew/reactnative/EventRecordStore.java +611 -0
- package/android/src/main/java/co/logbrew/reactnative/FatalStoreModuleImpl.java +129 -0
- package/android/src/newarch/java/co/logbrew/reactnative/FatalStoreModule.java +26 -0
- package/android/src/oldarch/java/co/logbrew/reactnative/FatalStoreModule.java +26 -0
- 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.cjs +2 -0
- package/index.d.cts +3 -0
- package/index.d.ts +3 -0
- package/index.js +2 -0
- package/index.native.d.ts +47 -2
- package/index.native.js +70 -2
- package/ios/LBRNEventRecordStore.h +29 -0
- package/ios/LBRNEventRecordStore.m +746 -0
- package/ios/LBRNFatalStoreModule.mm +159 -14
- package/ios/LBRNPrivateStorage.h +7 -0
- package/ios/LBRNPrivateStorage.m +38 -0
- package/package.json +7 -2
- package/persistent-delivery.native.js +282 -0
- package/promise-rejections.cjs +128 -4
- package/src/NativeLogBrewFatalStore.ts +9 -0
|
@@ -9,10 +9,15 @@ import com.facebook.react.bridge.ReadableType;
|
|
|
9
9
|
import com.facebook.react.bridge.WritableArray;
|
|
10
10
|
import com.facebook.react.bridge.WritableMap;
|
|
11
11
|
import java.io.File;
|
|
12
|
+
import java.nio.charset.StandardCharsets;
|
|
13
|
+
import java.security.MessageDigest;
|
|
14
|
+
import java.security.NoSuchAlgorithmException;
|
|
12
15
|
import java.util.ArrayList;
|
|
13
16
|
import java.util.Arrays;
|
|
17
|
+
import java.util.HashMap;
|
|
14
18
|
import java.util.HashSet;
|
|
15
19
|
import java.util.List;
|
|
20
|
+
import java.util.Map;
|
|
16
21
|
import java.util.Set;
|
|
17
22
|
|
|
18
23
|
final class FatalStoreModuleImpl {
|
|
@@ -32,9 +37,12 @@ final class FatalStoreModuleImpl {
|
|
|
32
37
|
new HashSet<>(Arrays.asList("filename", "line", "column"));
|
|
33
38
|
|
|
34
39
|
private final FatalRecordStore store;
|
|
40
|
+
private final File eventStoreParent;
|
|
41
|
+
private final Map<String, EventRecordStore> eventStores = new HashMap<>();
|
|
35
42
|
|
|
36
43
|
FatalStoreModuleImpl(ReactApplicationContext context) {
|
|
37
44
|
File root = context.getNoBackupFilesDir();
|
|
45
|
+
eventStoreParent = root;
|
|
38
46
|
store =
|
|
39
47
|
root == null
|
|
40
48
|
? null
|
|
@@ -43,6 +51,66 @@ final class FatalStoreModuleImpl {
|
|
|
43
51
|
new AndroidParentDirectorySync());
|
|
44
52
|
}
|
|
45
53
|
|
|
54
|
+
WritableMap loadEventRecords(String queueKey) {
|
|
55
|
+
try {
|
|
56
|
+
EventRecordStore eventStore = eventStore(queueKey);
|
|
57
|
+
return eventStore == null ? status("storage_error") : eventResultMap(eventStore.load());
|
|
58
|
+
} catch (RuntimeException error) {
|
|
59
|
+
return status("storage_error");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
WritableMap appendEventRecord(String queueKey, String serializedEvent, double eventBytes) {
|
|
64
|
+
try {
|
|
65
|
+
EventRecordStore eventStore = eventStore(queueKey);
|
|
66
|
+
Integer byteCount = integer(eventBytes);
|
|
67
|
+
return eventStore == null || byteCount == null
|
|
68
|
+
? status("storage_error")
|
|
69
|
+
: eventResultMap(eventStore.append(serializedEvent, byteCount));
|
|
70
|
+
} catch (RuntimeException error) {
|
|
71
|
+
return status("storage_error");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
WritableMap acknowledgeEventRecords(String queueKey, double count) {
|
|
76
|
+
try {
|
|
77
|
+
EventRecordStore eventStore = eventStore(queueKey);
|
|
78
|
+
Integer recordCount = integer(count);
|
|
79
|
+
return eventStore == null || recordCount == null
|
|
80
|
+
? status("storage_error")
|
|
81
|
+
: eventResultMap(eventStore.acknowledge(recordCount));
|
|
82
|
+
} catch (RuntimeException error) {
|
|
83
|
+
return status("storage_error");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
WritableMap purgeEventRecords(String queueKey) {
|
|
88
|
+
try {
|
|
89
|
+
EventRecordStore eventStore = eventStore(queueKey);
|
|
90
|
+
return eventStore == null ? status("storage_error") : eventResultMap(eventStore.purge());
|
|
91
|
+
} catch (RuntimeException error) {
|
|
92
|
+
return status("storage_error");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
synchronized WritableMap closeEventStore(String queueKey) {
|
|
97
|
+
String queueHash = queueHash(queueKey);
|
|
98
|
+
if (queueHash == null) {
|
|
99
|
+
return status("storage_error");
|
|
100
|
+
}
|
|
101
|
+
EventRecordStore eventStore = eventStores.get(queueHash);
|
|
102
|
+
if (eventStore == null) {
|
|
103
|
+
return status("closed");
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
return eventResultMap(eventStore.close());
|
|
107
|
+
} catch (RuntimeException error) {
|
|
108
|
+
return status("storage_error");
|
|
109
|
+
} finally {
|
|
110
|
+
eventStores.remove(queueHash);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
46
114
|
WritableMap writeFatalRecord(ReadableMap input) {
|
|
47
115
|
if (store == null) {
|
|
48
116
|
return status("storage_error");
|
|
@@ -227,4 +295,65 @@ final class FatalStoreModuleImpl {
|
|
|
227
295
|
output.putString("status", value);
|
|
228
296
|
return output;
|
|
229
297
|
}
|
|
298
|
+
|
|
299
|
+
private synchronized EventRecordStore eventStore(String queueKey) {
|
|
300
|
+
String queueHash = queueHash(queueKey);
|
|
301
|
+
if (eventStoreParent == null || queueHash == null) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
EventRecordStore existing = eventStores.get(queueHash);
|
|
305
|
+
if (existing != null) {
|
|
306
|
+
return existing;
|
|
307
|
+
}
|
|
308
|
+
EventRecordStore created =
|
|
309
|
+
new EventRecordStore(
|
|
310
|
+
new File(eventStoreParent, "logbrew-events-v1-" + queueHash),
|
|
311
|
+
new AndroidParentDirectorySync());
|
|
312
|
+
eventStores.put(queueHash, created);
|
|
313
|
+
return created;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private static String queueHash(String queueKey) {
|
|
317
|
+
if (queueKey == null || queueKey.trim().isEmpty()) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
byte[] keyBytes = queueKey.getBytes(StandardCharsets.UTF_8);
|
|
321
|
+
if (keyBytes.length == 0 || keyBytes.length > 4096) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
byte[] digest = MessageDigest.getInstance("SHA-256").digest(keyBytes);
|
|
326
|
+
StringBuilder value = new StringBuilder(digest.length * 2);
|
|
327
|
+
for (byte item : digest) {
|
|
328
|
+
value.append(String.format(java.util.Locale.ROOT, "%02x", item & 0xff));
|
|
329
|
+
}
|
|
330
|
+
return value.toString();
|
|
331
|
+
} catch (NoSuchAlgorithmException impossible) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private static Integer integer(double value) {
|
|
337
|
+
return Double.isFinite(value)
|
|
338
|
+
&& value >= 0
|
|
339
|
+
&& value <= Integer.MAX_VALUE
|
|
340
|
+
&& value == Math.rint(value)
|
|
341
|
+
? (int) value
|
|
342
|
+
: null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private static WritableMap eventResultMap(EventRecordStore.Result result) {
|
|
346
|
+
WritableMap output = status(result.status);
|
|
347
|
+
if ("loaded".equals(result.status)) {
|
|
348
|
+
WritableArray records = Arguments.createArray();
|
|
349
|
+
for (EventRecordStore.Record record : result.records) {
|
|
350
|
+
WritableMap value = Arguments.createMap();
|
|
351
|
+
value.putString("serializedEvent", record.serializedEvent);
|
|
352
|
+
value.putInt("eventBytes", record.eventBytes);
|
|
353
|
+
records.pushMap(value);
|
|
354
|
+
}
|
|
355
|
+
output.putArray("records", records);
|
|
356
|
+
}
|
|
357
|
+
return output;
|
|
358
|
+
}
|
|
230
359
|
}
|
|
@@ -36,4 +36,30 @@ final class FatalStoreModule extends NativeLogBrewFatalStoreSpec {
|
|
|
36
36
|
public WritableMap discardFatalRecord() {
|
|
37
37
|
return implementation.discardFatalRecord();
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
public WritableMap loadEventRecords(String queueKey) {
|
|
42
|
+
return implementation.loadEventRecords(queueKey);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Override
|
|
46
|
+
public WritableMap appendEventRecord(
|
|
47
|
+
String queueKey, String serializedEvent, double eventBytes) {
|
|
48
|
+
return implementation.appendEventRecord(queueKey, serializedEvent, eventBytes);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Override
|
|
52
|
+
public WritableMap acknowledgeEventRecords(String queueKey, double count) {
|
|
53
|
+
return implementation.acknowledgeEventRecords(queueKey, count);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Override
|
|
57
|
+
public WritableMap purgeEventRecords(String queueKey) {
|
|
58
|
+
return implementation.purgeEventRecords(queueKey);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Override
|
|
62
|
+
public WritableMap closeEventStore(String queueKey) {
|
|
63
|
+
return implementation.closeEventStore(queueKey);
|
|
64
|
+
}
|
|
39
65
|
}
|
|
@@ -38,4 +38,30 @@ final class FatalStoreModule extends ReactContextBaseJavaModule {
|
|
|
38
38
|
public WritableMap discardFatalRecord() {
|
|
39
39
|
return implementation.discardFatalRecord();
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
43
|
+
public WritableMap loadEventRecords(String queueKey) {
|
|
44
|
+
return implementation.loadEventRecords(queueKey);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
48
|
+
public WritableMap appendEventRecord(
|
|
49
|
+
String queueKey, String serializedEvent, double eventBytes) {
|
|
50
|
+
return implementation.appendEventRecord(queueKey, serializedEvent, eventBytes);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
54
|
+
public WritableMap acknowledgeEventRecords(String queueKey, double count) {
|
|
55
|
+
return implementation.acknowledgeEventRecords(queueKey, count);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
59
|
+
public WritableMap purgeEventRecords(String queueKey) {
|
|
60
|
+
return implementation.purgeEventRecords(queueKey);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
64
|
+
public WritableMap closeEventStore(String queueKey) {
|
|
65
|
+
return implementation.closeEventStore(queueKey);
|
|
66
|
+
}
|
|
41
67
|
}
|
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.cjs
CHANGED
|
@@ -29,6 +29,7 @@ function createLogBrewReactNativeClient({
|
|
|
29
29
|
clientKey,
|
|
30
30
|
deliveryIntervalMs,
|
|
31
31
|
deliveryQueueThreshold,
|
|
32
|
+
eventStore,
|
|
32
33
|
maxBatchBytes,
|
|
33
34
|
maxBatchEvents,
|
|
34
35
|
maxQueueBytes,
|
|
@@ -48,6 +49,7 @@ function createLogBrewReactNativeClient({
|
|
|
48
49
|
automaticDelivery,
|
|
49
50
|
deliveryIntervalMs,
|
|
50
51
|
deliveryQueueThreshold,
|
|
52
|
+
eventStore,
|
|
51
53
|
maxBatchBytes,
|
|
52
54
|
maxBatchEvents,
|
|
53
55
|
maxQueueBytes,
|
package/index.d.cts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
DeliveryHealthSnapshot,
|
|
5
5
|
DroppedEvent,
|
|
6
6
|
EnvironmentAttributes,
|
|
7
|
+
EventStore,
|
|
7
8
|
IssueAttributes,
|
|
8
9
|
LogAttributes,
|
|
9
10
|
LogBrewClient,
|
|
@@ -37,6 +38,8 @@ export type CreateLogBrewReactNativeClientConfig = {
|
|
|
37
38
|
clientKey?: string;
|
|
38
39
|
deliveryIntervalMs?: number;
|
|
39
40
|
deliveryQueueThreshold?: number;
|
|
41
|
+
/** Advanced synchronous persistence seam. React Native apps normally use the native entry's persistentQueue mode. */
|
|
42
|
+
eventStore?: EventStore;
|
|
40
43
|
maxBatchBytes?: number;
|
|
41
44
|
maxBatchEvents?: number;
|
|
42
45
|
maxQueueBytes?: number;
|
package/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
DeliveryHealthSnapshot,
|
|
5
5
|
DroppedEvent,
|
|
6
6
|
EnvironmentAttributes,
|
|
7
|
+
EventStore,
|
|
7
8
|
IssueAttributes,
|
|
8
9
|
LogAttributes,
|
|
9
10
|
LogBrewClient,
|
|
@@ -37,6 +38,8 @@ export type CreateLogBrewReactNativeClientConfig = {
|
|
|
37
38
|
clientKey?: string;
|
|
38
39
|
deliveryIntervalMs?: number;
|
|
39
40
|
deliveryQueueThreshold?: number;
|
|
41
|
+
/** Advanced synchronous persistence seam. React Native apps normally use the native entry's persistentQueue mode. */
|
|
42
|
+
eventStore?: EventStore;
|
|
40
43
|
maxBatchBytes?: number;
|
|
41
44
|
maxBatchEvents?: number;
|
|
42
45
|
maxQueueBytes?: number;
|
package/index.js
CHANGED
|
@@ -29,6 +29,7 @@ export function createLogBrewReactNativeClient({
|
|
|
29
29
|
clientKey,
|
|
30
30
|
deliveryIntervalMs,
|
|
31
31
|
deliveryQueueThreshold,
|
|
32
|
+
eventStore,
|
|
32
33
|
maxBatchBytes,
|
|
33
34
|
maxBatchEvents,
|
|
34
35
|
maxQueueBytes,
|
|
@@ -48,6 +49,7 @@ export function createLogBrewReactNativeClient({
|
|
|
48
49
|
automaticDelivery,
|
|
49
50
|
deliveryIntervalMs,
|
|
50
51
|
deliveryQueueThreshold,
|
|
52
|
+
eventStore,
|
|
51
53
|
maxBatchBytes,
|
|
52
54
|
maxBatchEvents,
|
|
53
55
|
maxQueueBytes,
|
package/index.native.d.ts
CHANGED
|
@@ -1,2 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { EventStore, LogBrewClient } from "@logbrew/sdk";
|
|
2
|
+
import type { CreateLogBrewReactNativeClientConfig } from "./index.js";
|
|
3
|
+
|
|
4
|
+
export * from "./index.js";
|
|
5
|
+
export {
|
|
6
|
+
installLogBrewReactNativeGlobalErrorHandler,
|
|
7
|
+
installLogBrewReactNativePromiseRejectionTracker
|
|
8
|
+
} from "./global-errors";
|
|
9
|
+
|
|
10
|
+
export type ReactNativePersistentQueueMode = "auto" | "required" | "disabled";
|
|
11
|
+
|
|
12
|
+
export type CreateNativeLogBrewReactNativeClientConfig =
|
|
13
|
+
Omit<CreateLogBrewReactNativeClientConfig, "eventStore"> & ({
|
|
14
|
+
/**
|
|
15
|
+
* `auto` uses the linked app-private native queue when available and otherwise uses memory.
|
|
16
|
+
* `required` fails client creation when the native queue is unavailable. `disabled` uses memory.
|
|
17
|
+
*/
|
|
18
|
+
persistentQueue?: ReactNativePersistentQueueMode;
|
|
19
|
+
eventStore?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
/** Advanced app-owned synchronous persistence adapter. Mutually exclusive with persistentQueue. */
|
|
22
|
+
eventStore: EventStore;
|
|
23
|
+
persistentQueue?: never;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export declare function createLogBrewReactNativeClient(
|
|
27
|
+
config: CreateNativeLogBrewReactNativeClientConfig
|
|
28
|
+
): LogBrewClient;
|
|
29
|
+
|
|
30
|
+
export declare function createDefaultLogBrewReactNativeClient(
|
|
31
|
+
config: CreateNativeLogBrewReactNativeClientConfig
|
|
32
|
+
): LogBrewClient;
|
|
33
|
+
|
|
34
|
+
export declare function purgeLogBrewReactNativePersistentQueue(
|
|
35
|
+
config: { apiKey?: string; clientKey?: string }
|
|
36
|
+
): void;
|
|
37
|
+
|
|
38
|
+
declare const defaultExport: Omit<
|
|
39
|
+
typeof import("./index.js").default,
|
|
40
|
+
"createLogBrewReactNativeClient" | "createDefaultLogBrewReactNativeClient"
|
|
41
|
+
> & {
|
|
42
|
+
createLogBrewReactNativeClient: typeof createLogBrewReactNativeClient;
|
|
43
|
+
createDefaultLogBrewReactNativeClient: typeof createDefaultLogBrewReactNativeClient;
|
|
44
|
+
purgeLogBrewReactNativePersistentQueue: typeof purgeLogBrewReactNativePersistentQueue;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default defaultExport;
|