@honeybadger-io/react-native 5.2.0 → 6.0.0

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/index.js DELETED
@@ -1,657 +0,0 @@
1
- //
2
- // Honeybadger React Native
3
- //
4
-
5
-
6
- import { Platform, NativeModules, NativeEventEmitter } from 'react-native';
7
-
8
-
9
- const pkg = require('./package.json');
10
- const HoneybadgerNativeModule = NativeModules.HoneybadgerReactNative;
11
-
12
-
13
- let _apiKey = null;
14
- let _initialized = false;
15
- let _context = {};
16
- let _logLevel = 'warning'; // "error", "warning", "debug"
17
- let _previousJSGlobalExceptionHandler = null;
18
- let _revision = '';
19
- let _projectRoot = '';
20
- let _reportErrors = true;
21
-
22
-
23
-
24
- // ----------------------------------------------------------------------------
25
- // Public Interface
26
- // ----------------------------------------------------------------------------
27
- const honeybadger = {
28
-
29
- /**
30
- * Initialize and configure the Honeybadger React Native library.
31
- * @param {string} apiKey - Your Honeybadger API key.
32
- * @param {boolean} [reportErrors=true] reportErrors - Whether to send error reports to Honeybadger (disable for dev environments, etc.)
33
- * @param {string=} revision - The git revision of the current build.
34
- * @param {string=} projectRoot - The path to the project root.
35
- */
36
- configure ( apiKey, reportErrors = true, revision = '', projectRoot = '' )
37
- {
38
- if ( !isValidAPIKey(apiKey) )
39
- {
40
- informUserOfInvalidAPIKey();
41
- return;
42
- }
43
-
44
- _apiKey = apiKey.trim();
45
- _revision = (revision || '').trim();
46
- _projectRoot = (projectRoot || '').trim();
47
- _reportErrors = reportErrors;
48
-
49
- if ( !_initialized )
50
- {
51
- setNativeExceptionHandler();
52
- setJavaScriptErrorHandler();
53
- _initialized = true;
54
- }
55
- },
56
-
57
- /**
58
- * Send any kind of error, exception, object, String, etc. to Honeybadger.
59
- * @param {string|object} err - The error string or object.
60
- * @param {string|object} additionalData - Additional data to include with the error.
61
- */
62
- notify ( err, additionalData )
63
- {
64
- if ( !isValidAPIKey(_apiKey) ) {
65
- informUserOfInvalidAPIKey();
66
- return;
67
- }
68
-
69
- if ( !err || (isString(err) && err.trim().length === 0) || (isObject(err) && err.length === 0) ) {
70
- logError('Honeybadger.notify() - invalid error');
71
- return;
72
- }
73
-
74
- if ( isString(err) ) {
75
- err = {
76
- 'message' : err.trim(),
77
- };
78
- }
79
-
80
- if ( isStringWithValue(additionalData) ) {
81
- additionalData = {
82
- 'additionalData' : additionalData.trim(),
83
- };
84
- }
85
- else if ( !isObject(additionalData) ) {
86
- additionalData = {};
87
- }
88
-
89
- const errName = safeStringFromField(err, 'name', 'Error via notify()');
90
- const errMsg = safeStringFromField(err, 'message', 'Unknown error message');
91
-
92
- let contextForThisError = {};
93
- Object.assign(contextForThisError, _context);
94
- Object.assign(contextForThisError, additionalData);
95
-
96
- let payloadData = {
97
- errorClass : `React Native ${(Platform.OS === 'ios' ? 'iOS' : 'Android')} ${errName}`,
98
- errorMsg : errMsg,
99
- details: {
100
- initialHandler: 'notify',
101
- },
102
- context: contextForThisError,
103
- };
104
-
105
- let backTrace = backTraceFromJavaScriptError(err);
106
-
107
- if ( arrayHasValues(backTrace.framesFromComponentStack) ) {
108
- payloadData.backTrace = backTrace.framesFromComponentStack;
109
- payloadData.details.primaryBackTraceSource = 'ReactNativeComponentStack';
110
- if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
111
- payloadData.details.javaScriptStackTrace = backTrace.framesFromJavaScriptErrorStack;
112
- }
113
- } else if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
114
- payloadData.backTrace = backTrace.framesFromJavaScriptErrorStack;
115
- payloadData.details.primaryBackTraceSource = 'JavaScriptErrorStack';
116
- }
117
-
118
- sendToHoneybadger(buildPayload(payloadData));
119
- },
120
-
121
-
122
- /**
123
- * Include additional data whenever an error or an exception occurs. This can be called as many times as needed. New context data will be merged with any previously-set context data.
124
- * @param {object} context - Additional data to include with all errors and exceptions.
125
- */
126
- setContext ( context )
127
- {
128
- if ( isObject(context) ) {
129
- Object.assign(_context, context);
130
- }
131
- },
132
-
133
-
134
- /**
135
- * Clears/resets any data previously set through setContext().
136
- * @param {object=} context - Optional new context to set.
137
- */
138
- resetContext ( context )
139
- {
140
- _context = (isObject(context) ? context : {});
141
- },
142
-
143
-
144
- /**
145
- * Sets the logging level for the Honeybadger library.
146
- * @param {('debug'|'warning'|'error')} [level=warning] - The logging level.
147
- */
148
- setLogLevel ( level = 'warning' )
149
- {
150
- if ( !level ) {
151
- return;
152
- }
153
-
154
- switch (level.toLowerCase())
155
- {
156
- case 'debug':
157
- case 'warning':
158
- case 'error':
159
- _logLevel = level.toLowerCase();
160
- break;
161
-
162
- default:
163
- logWarning("Honeybadger: Log level should be one of 'debug', 'warning', or 'error'.");
164
- }
165
- }
166
- };
167
-
168
-
169
- export default honeybadger;
170
-
171
-
172
-
173
- // -----------------------------------------------------------------------------
174
- // Internal
175
- // -----------------------------------------------------------------------------
176
-
177
- function informUserOfInvalidAPIKey() {
178
- logError('Please initialize Honeybadger by calling configure() with a valid Honeybadger.io API key.');
179
- }
180
-
181
-
182
- function isValidAPIKey(apiKey) {
183
- return apiKey && apiKey.trim().length > 0;
184
- }
185
-
186
-
187
-
188
- function setJavaScriptErrorHandler()
189
- {
190
- logDebug('Setting up the JavaScript global error handler.');
191
-
192
- _previousJSGlobalExceptionHandler = global.ErrorUtils.getGlobalHandler();
193
-
194
- global.ErrorUtils.setGlobalHandler(function(err, isFatal) {
195
- logDebug('JavaScript global error handler triggered.');
196
-
197
- onJavaScriptError(err, {
198
- initialHandler: 'Global JavaScript Error Handler',
199
- });
200
-
201
- // Allowing the default error handler to process the error after
202
- // we're done with it will show the useful RN red info box in dev.
203
- if ( _previousJSGlobalExceptionHandler && _previousJSGlobalExceptionHandler != this ) {
204
- logDebug('Passing error to previous error handler.');
205
- _previousJSGlobalExceptionHandler(err, isFatal);
206
- }
207
- });
208
- }
209
-
210
-
211
-
212
- function setNativeExceptionHandler() {
213
- if ( !HoneybadgerNativeModule ) {
214
- logError('Honeybadger: The native module was not found. Please review the installation instructions.');
215
- return;
216
- }
217
-
218
- logDebug('Starting HoneyBadger native module.');
219
- HoneybadgerNativeModule.start();
220
-
221
- const nativeEventEmitter = new NativeEventEmitter(HoneybadgerNativeModule);
222
-
223
- logDebug('Listening for native exceptions...');
224
- nativeEventEmitter.addListener('native-exception-event', function(data) {
225
- switch ( Platform.OS ) {
226
- case 'ios': onNativeIOSException(data); break;
227
- case 'android': onNativeAndroidException(data); break;
228
- }
229
- });
230
- }
231
-
232
-
233
-
234
- function sendToHoneybadger(payload) {
235
- if ( !payload || !isValidAPIKey(_apiKey) || !_reportErrors ) {
236
- return;
237
- }
238
-
239
-
240
- logDebug('Sending error report to Honeybadger...');
241
-
242
- const params = {
243
- method: 'POST',
244
- headers: {
245
- 'Content-Type' : 'application/json',
246
- 'Accept' : 'text/json, application/json',
247
- 'X-API-Key' : _apiKey,
248
- 'User-Agent' : buildUserAgent(),
249
- },
250
- body: JSON.stringify(payload)
251
- };
252
-
253
- fetch('https://api.honeybadger.io/v1/notices/js', params).then(response => {
254
- if ( !response.ok ) {
255
- logDebug(`Failed to post error to Honeybadger: ${response.status}`);
256
- logDebug(response);
257
- } else {
258
- logDebug('Successful post to Honeybadger.');
259
- logDebug(response);
260
- }
261
- });
262
- }
263
-
264
-
265
-
266
- function buildUserAgent() {
267
- let reactNativeVersion = `${Platform.constants.reactNativeVersion.major}.${Platform.constants.reactNativeVersion.minor}.${Platform.constants.reactNativeVersion.patch}`;
268
- const nativePlatformName = Platform.constants.systemName || (Platform.OS === 'ios' ? 'iOS' : 'Android');
269
- const nativePlatformVersion = Platform.constants.osVersion || '';
270
- let nativePlatform = `${nativePlatformName} ${nativePlatformVersion}`;
271
- return `${pkg.name} ${pkg.version}; ${reactNativeVersion}; ${nativePlatform}`;
272
- }
273
-
274
-
275
-
276
- function buildPayload ( data ) {
277
- let payload = {
278
- notifier : {
279
- name : pkg.name,
280
- url : pkg.repository.url,
281
- version : pkg.version,
282
- },
283
- error : {
284
- class : data.errorClass || 'React Native Error',
285
- message : data.errorMsg || 'Unknown Error',
286
- backtrace : data.backTrace,
287
- },
288
- request : {
289
- context: data.context,
290
- },
291
- server : {
292
- // eslint-disable-next-line no-undef
293
- environment_name: (__DEV__ ? 'development' : 'production'),
294
- project_root: _projectRoot || '',
295
- revision: _revision || ''
296
- },
297
- };
298
-
299
- if ( data.details && data.details.length > 0 ) {
300
- payload.details = {
301
- 'React Native' : data.details
302
- }
303
- }
304
-
305
- return payload;
306
- }
307
-
308
-
309
-
310
- function framesFromComponentStack(str) {
311
- str = str || '';
312
- let frames = [];
313
- const regex = /^\s*in\s(\S+)(\s\(at\s(\S+):(\S+)\)\s*$)?/gm;
314
- let match;
315
- while ( (match = regex.exec(str)) !== null ) {
316
- if ( match.index === regex.lastIndex) {
317
- regex.lastIndex++;
318
- }
319
- frames.push({
320
- method: match && match.length > 1 ? match[1] : '',
321
- file: match && match.length > 3 ? match[3] : '',
322
- number: match && match.length > 4 ? match[4] : '',
323
- });
324
- }
325
- return frames;
326
- }
327
-
328
-
329
-
330
- function logDebug(data) {
331
- if ( data && _logLevel.toLowerCase() === 'debug' ) {
332
- console.log(data);
333
- }
334
- }
335
-
336
-
337
-
338
- function logWarning(data) {
339
- if ( data && _logLevel.toLowerCase() === 'warning' || _logLevel.toLowerCase() === 'error' ) {
340
- console.warn(data);
341
- }
342
- }
343
-
344
-
345
-
346
- function logError(data) {
347
- if ( data ) {
348
- console.error(data);
349
- }
350
- }
351
-
352
-
353
-
354
- // ----------------------------------------------------------------------------
355
- // JavaScript
356
- // ----------------------------------------------------------------------------
357
-
358
- function onJavaScriptError(err, data) {
359
- if ( !err ) {
360
- return;
361
- }
362
-
363
- let payloadData = {
364
- errorClass: `React Native ${(Platform.OS === 'ios' ? 'iOS' : 'Android')} Error`,
365
- errorMsg: errorMessageFromJavaScriptError(err),
366
- details: {
367
- initialHandler: data.initialHandler || '',
368
- },
369
- context: _context || {},
370
- };
371
-
372
- let backTrace = backTraceFromJavaScriptError(err);
373
-
374
- if ( arrayHasValues(backTrace.framesFromComponentStack) ) {
375
- payloadData.backTrace = backTrace.framesFromComponentStack;
376
- payloadData.details.primaryBackTraceSource = 'ReactNativeComponentStack';
377
- if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
378
- payloadData.details.javaScriptStackTrace = backTrace.framesFromJavaScriptErrorStack;
379
- }
380
- } else if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
381
- payloadData.backTrace = backTrace.framesFromJavaScriptErrorStack;
382
- payloadData.details.primaryBackTraceSource = 'JavaScriptErrorStack';
383
- }
384
-
385
- sendToHoneybadger(buildPayload(payloadData));
386
- }
387
-
388
-
389
-
390
- function errorMessageFromJavaScriptError(err) {
391
- if ( !err ) {
392
- return '';
393
- }
394
-
395
- if ( isStringWithValue(err) ) {
396
- return err.trim();
397
- }
398
- else if ( isObject(err) && err.message && err.message.length > 0 ) {
399
- return err.message;
400
- }
401
-
402
- return '';
403
- }
404
-
405
-
406
-
407
- function backTraceFromJavaScriptError(err) {
408
- return {
409
- framesFromComponentStack: ( isObjectWithField(err, 'componentStack') ? framesFromComponentStack(err.componentStack) : []),
410
- framesFromJavaScriptErrorStack: ( isObjectWithField(err, 'stack') ? framesFromJavaScriptErrorStack(err.stack) : []),
411
- };
412
- }
413
-
414
-
415
-
416
- function framesFromJavaScriptErrorStack(stack) {
417
- let frames = [];
418
- let lines = stack.split('\n');
419
- const javaScriptCoreRe = /^\s*(?:([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i;
420
- for ( let i = 0 ; i < lines.length ; ++i ) {
421
- const line = lines[i];
422
- const parts = javaScriptCoreRe.exec(line);
423
- if ( parts ) {
424
- frames.push({
425
- file: parts[3] || '',
426
- method: parts[1] || '',
427
- number: (parts[4] ? +parts[4] : ''),
428
- column: (parts[5] ? +parts[5] : ''),
429
- });
430
- } else if ( line.indexOf('[native code]') !== -1 ) {
431
- let parts = line.split('@');
432
- if ( parts && parts.length === 2 ) {
433
- frames.push({
434
- file: parts[1],
435
- method: parts[0],
436
- number: '',
437
- column: '',
438
- });
439
- }
440
- }
441
- }
442
- return frames;
443
- }
444
-
445
-
446
-
447
- // ----------------------------------------------------------------------------
448
- // Android
449
- // ----------------------------------------------------------------------------
450
-
451
- function onNativeAndroidException(data)
452
- {
453
- let payloadData = {
454
- errorClass: `React Native Android ${data.type}`,
455
- errorMsg: data.message || '',
456
- context: _context || {},
457
- };
458
-
459
- let backTrace = backTraceFromAndroidException(data);
460
-
461
- if ( arrayHasValues(backTrace) ) {
462
- payloadData.backTrace = backTrace;
463
- }
464
-
465
- sendToHoneybadger(buildPayload(payloadData));
466
- }
467
-
468
-
469
-
470
- function backTraceFromAndroidException(data)
471
- {
472
- if ( !data || !data.stackTrace ) return [];
473
- return data.stackTrace.map ( (frame) => {
474
- let method = ( isStringWithValue(frame.class) && isStringWithValue(frame.method) ) ? (frame.class + '.' + frame.method) : frame.method;
475
- return {
476
- method: method || '',
477
- file: frame.file || '',
478
- number: frame.line || ''
479
- };
480
- });
481
- }
482
-
483
-
484
-
485
- // ----------------------------------------------------------------------------
486
- // iOS
487
- // ----------------------------------------------------------------------------
488
-
489
- function onNativeIOSException(data)
490
- {
491
- let payloadData = {
492
- errorClass: `React Native iOS ${data.type}`,
493
- errorMsg: errorMessageFromIOSException(data),
494
- details: {
495
- errorDomain: data.errorDomain || '',
496
- initialHandler: data.initialHandler || '',
497
- userInfo: data.userInfo || {},
498
- architecture: data.architecture || '',
499
- },
500
- context: _context || {},
501
- };
502
-
503
- let backTrace = backTraceFromIOSException(data);
504
-
505
- if ( arrayHasValues(backTrace.framesFromComponentStack) )
506
- {
507
- payloadData.backTrace = backTrace.framesFromComponentStack;
508
- payloadData.details.primaryBackTraceSource = 'ReactNativeComponentStack';
509
- if ( arrayHasValues(backTrace.framesFromReactNativeIOSStackTrace) ) {
510
- payloadData.details.reactNativeIOSStackTrace = backTrace.framesFromReactNativeIOSStackTrace;
511
- }
512
- if ( arrayHasValues(backTrace.framesFromIOSCallStack) ) {
513
- payloadData.details.iosCallStack = backTrace.framesFromIOSCallStack;
514
- }
515
- }
516
- else if ( arrayHasValues(backTrace.framesFromReactNativeIOSStackTrace) )
517
- {
518
- payloadData.backTrace = backTrace.framesFromReactNativeIOSStackTrace;
519
- payloadData.details.primaryBackTraceSource = 'ReactNativeIOSStackTrace';
520
- if ( arrayHasValues(backTrace.framesFromIOSCallStack) ) {
521
- payloadData.details.iosCallStack = backTrace.framesFromIOSCallStack;
522
- }
523
- }
524
- else if ( arrayHasValues(backTrace.framesFromIOSCallStack) )
525
- {
526
- payloadData.backTrace = backTrace.framesFromIOSCallStack;
527
- payloadData.details.primaryBackTraceSource = 'iOSCallStack';
528
- }
529
-
530
- sendToHoneybadger(buildPayload(payloadData));
531
- }
532
-
533
-
534
-
535
- function errorMessageFromIOSException(data) {
536
- if ( !data ) {
537
- return '';
538
- }
539
-
540
- if ( data.localizedDescription && data.localizedDescription !== '' ) {
541
- const localizedDescription = data.localizedDescription;
542
- const startOfNativeIOSCallStack = localizedDescription.indexOf('callstack: (\n');
543
- if ( startOfNativeIOSCallStack === -1 ) {
544
- const lines = localizedDescription.split('\n');
545
- return lines.length === 0 ? localizedDescription : lines[0].trim();
546
- } else {
547
- return localizedDescription.substr(0, startOfNativeIOSCallStack).trim();
548
- }
549
- }
550
- else if ( (data.name && data.name !== '') || (data.reason && data.reason !== '') ) {
551
- return `${data.name} : ${data.reason}`.trim();
552
- }
553
-
554
- return '';
555
- }
556
-
557
-
558
-
559
- function backTraceFromIOSException(data) {
560
- return {
561
- framesFromComponentStack: framesFromComponentStack(data.localizedDescription),
562
- framesFromReactNativeIOSStackTrace: framesFromReactNativeIOSStackTrace(data),
563
- framesFromIOSCallStack: framesFromIOSCallStack(data),
564
- };
565
- }
566
-
567
-
568
-
569
- function framesFromReactNativeIOSStackTrace(data) {
570
- if ( !data.reactNativeStackTrace ) {
571
- return [];
572
- }
573
- let frames = [];
574
- data.reactNativeStackTrace.forEach( (frame) => {
575
- frames.push({
576
- method: frame.methodName || '',
577
- number: frame.lineNumber || '',
578
- file: frame.file || '',
579
- column: frame.column || '',
580
- });
581
- });
582
- return frames;
583
- }
584
-
585
-
586
-
587
- function framesFromIOSCallStack(data) {
588
- let callStack = [];
589
-
590
- if ( isStringWithValue(data.localizedDescription) ) {
591
- callStack = data.localizedDescription.split('\n').map(item => item.trim());
592
- }
593
- else if ( arrayHasValues(data.callStackSymbols) ) {
594
- callStack = data.callStackSymbols.map(item => item.trim());
595
- }
596
-
597
- let frames = [];
598
- const regex = /\d+\s+(\S+)\s+(\S+)\s(.+)\s\+\s(\d+)(\s+\((\S+):(\S+)\))?/gm;
599
- let match;
600
- callStack.forEach(element => {
601
- while ( (match = regex.exec(element)) !== null ) {
602
- if ( match.index === regex.lastIndex) {
603
- regex.lastIndex++;
604
- }
605
-
606
- let moduleName = match && match.length > 1 ? match[1] : '';
607
- let stackAddress = match && match.length > 2 ? match[2] : '';
608
- let loadAddress = match && match.length > 3 ? match[3] : '';
609
- // let symbolOffset = match && match.length > 4 ? match[4] : '';
610
- let file = match && match.length > 6 ? match[6] : '';
611
- let line = match && match.length > 7 ? match[7] : '';
612
-
613
- frames.push({
614
- file: file || moduleName || '',
615
- line: line || '',
616
- method: loadAddress || '',
617
- stack_address: stackAddress || '',
618
- });
619
- }
620
- });
621
-
622
- return frames;
623
- }
624
-
625
-
626
-
627
- // ----------------------------------------------------------------------------
628
- // Util
629
- // ----------------------------------------------------------------------------
630
-
631
- function isObject(val) {
632
- return val != null && (typeof val === 'object') && !(Array.isArray(val));
633
- }
634
-
635
- function isObjectWithField(possibleObj, field) {
636
- return isObject(possibleObj) && (field in possibleObj);
637
- }
638
-
639
- function isString(val) {
640
- return val != null && typeof val === 'string';
641
- }
642
-
643
- function isStringWithValue(val) {
644
- return isString(val) && val.trim().length > 0;
645
- }
646
-
647
- function arrayHasValues(obj) {
648
- return obj != null && Array.isArray(obj) && obj.length !== 0;
649
- }
650
-
651
- function safeStringFromField(obj, field, defaultValue = '') {
652
- if ( isObjectWithField(obj, field) && isStringWithValue(obj[field]) ) {
653
- return obj[field].trim();
654
- }
655
- return defaultValue;
656
- }
657
-
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "include": ["index.js"],
3
- "compilerOptions": {
4
- // Read .js files
5
- "allowJs": true,
6
- // Generate d.ts files
7
- "declaration": true,
8
- // Only generate d.ts files
9
- "emitDeclarationOnly": true,
10
- // Don't complain about conflicting types in our dependencies
11
- "skipLibCheck": true,
12
- }
13
- }