@sentry/react-native
Version:
Official Sentry SDK for react-native
342 lines • 14.8 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { debug, getClient, getGlobalScope, getIntegrationsToSetup, getIsolationScope, initAndBind, makeDsn, stackParserFromStackParserOptions, withScope as coreWithScope, } from '@sentry/core';
import { defaultStackParser, makeFetchTransport, Profiler } from '@sentry/react';
import * as React from 'react';
import { ReactNativeClient } from './client';
import { FeedbackFormProvider } from './feedback/FeedbackFormProvider';
import { getDevServer } from './integrations/debugsymbolicatorutils';
import { getDefaultIntegrations } from './integrations/default';
import { shouldEnableNativeNagger } from './options';
import { enableSyncToNative } from './scopeSync';
import { TouchEventBoundary } from './touchevents';
import { ReactNativeProfiler } from './tracing';
import { _appLoaded, _extendAppStart, _finishExtendedAppStart, _getExtendedAppStartSpan, } from './tracing/integrations/appStart';
import { useEncodePolyfill } from './transports/encodePolyfill';
import { DEFAULT_BUFFER_SIZE, makeNativeTransportFactory } from './transports/native';
import { getDefaultEnvironment, isExpoGo, isRunningInMetroDevServer, isWeb } from './utils/environment';
import { registerFeatureMarker } from './utils/featureMarkers';
import { getDefaultRelease } from './utils/release';
import { safeFactory, safeTracesSampler } from './utils/safe';
import { checkSentryJsSdkVersionMismatch } from './utils/sdkVersionCheck';
import { RN_GLOBAL_OBJ } from './utils/worldwide';
import { NATIVE } from './wrapper';
const CAPTURE_APP_START_ERRORS_INTEGRATION_NAME = 'CaptureAppStartErrors';
const DEFAULT_OPTIONS = {
enableNativeCrashHandling: true,
enableNativeNagger: true,
autoInitializeNativeSdk: true,
enableAutoPerformanceTracing: true,
enableWatchdogTerminationTracking: true,
patchGlobalPromise: true,
sendClientReports: true,
maxQueueSize: DEFAULT_BUFFER_SIZE,
attachStacktrace: true,
enableCaptureFailedRequests: false,
enableNdk: true,
enableAppStartTracking: true,
enableNativeFramesTracking: true,
enableStallTracking: true,
enableUserInteractionTracing: false,
propagateTraceparent: false,
};
/**
* Inits the SDK and returns the final options.
*/
export function init(passedOptions) {
var _a, _b, _c, _d, _e, _f;
if (isRunningInMetroDevServer()) {
return;
}
const userOptions = Object.assign(Object.assign({}, RN_GLOBAL_OBJ.__SENTRY_OPTIONS__), passedOptions);
const maxQueueSize = (_c = (_a = userOptions.maxQueueSize) !== null && _a !== void 0 ? _a : (_b = userOptions.transportOptions) === null || _b === void 0 ? void 0 : _b.bufferSize) !== null && _c !== void 0 ? _c : DEFAULT_OPTIONS.maxQueueSize;
const enableNative = userOptions.enableNative === undefined || userOptions.enableNative ? NATIVE.isNativeAvailable() : false;
useEncodePolyfill();
if (enableNative) {
enableSyncToNative(getGlobalScope());
enableSyncToNative(getIsolationScope());
}
const getURLFromDSN = (dsn) => {
if (!dsn) {
return undefined;
}
const dsnComponents = makeDsn(dsn);
if (!dsnComponents) {
debug.error('Failed to extract url from DSN: ', dsn);
return undefined;
}
const port = dsnComponents.port ? `:${dsnComponents.port}` : '';
return `${dsnComponents.protocol}://${dsnComponents.host}${port}`;
};
const userBeforeBreadcrumb = safeFactory(userOptions.beforeBreadcrumb, {
loggerMessage: 'The beforeBreadcrumb threw an error',
});
// Exclude Dev Server and Sentry Dsn request from Breadcrumbs
const devServerUrl = (_d = getDevServer()) === null || _d === void 0 ? void 0 : _d.url;
const dsn = getURLFromDSN(userOptions.dsn);
const defaultBeforeBreadcrumb = (breadcrumb, _hint) => {
var _a;
const type = breadcrumb.type || '';
const url = typeof ((_a = breadcrumb.data) === null || _a === void 0 ? void 0 : _a.url) === 'string' ? breadcrumb.data.url : '';
if (type === 'http' && ((devServerUrl && url.startsWith(devServerUrl)) || (dsn && url.startsWith(dsn)))) {
return null;
}
return breadcrumb;
};
const chainedBeforeBreadcrumb = (breadcrumb, hint) => {
let modifiedBreadcrumb = breadcrumb;
if (userBeforeBreadcrumb) {
const result = userBeforeBreadcrumb(breadcrumb, hint);
if (result === null) {
return null;
}
modifiedBreadcrumb = result;
}
return defaultBeforeBreadcrumb(modifiedBreadcrumb, hint);
};
const options = Object.assign(Object.assign(Object.assign({}, DEFAULT_OPTIONS), userOptions), { release: (_e = userOptions.release) !== null && _e !== void 0 ? _e : getDefaultRelease(), enableNative, enableNativeNagger: shouldEnableNativeNagger(userOptions.enableNativeNagger),
// If custom transport factory fails the SDK won't initialize
transport: userOptions.transport ||
makeNativeTransportFactory({
enableNative,
}) ||
makeFetchTransport, transportOptions: Object.assign(Object.assign(Object.assign({}, DEFAULT_OPTIONS.transportOptions), ((_f = userOptions.transportOptions) !== null && _f !== void 0 ? _f : {})), { bufferSize: maxQueueSize }), maxQueueSize, integrations: [], stackParser: stackParserFromStackParserOptions(userOptions.stackParser || defaultStackParser), beforeBreadcrumb: chainedBeforeBreadcrumb, initialScope: safeFactory(userOptions.initialScope, { loggerMessage: 'The initialScope threw an error' }) });
if (!('autoInitializeNativeSdk' in userOptions) && RN_GLOBAL_OBJ.__SENTRY_OPTIONS__ && !__DEV__) {
// Options file is present in a release build, native SDK is expected to be initialized
// before JS from the native app entry point (e.g. AppDelegate, MainApplication).
// In dev builds, we always re-initialize from JS to set up the native log bridge
// and provide runtime values (devServerUrl, defaultSidecarUrl, etc.).
// oxlint-disable-next-line eslint(no-console)
console.info('[Sentry] Using options file. Native SDK is expected to be initialized before JS, skipping automatic native initialization from JS.');
options.autoInitializeNativeSdk = false;
}
if ('tracesSampler' in options) {
options.tracesSampler = safeTracesSampler(options.tracesSampler);
}
if (!('environment' in options)) {
options.environment = getDefaultEnvironment();
}
const defaultIntegrations = userOptions.defaultIntegrations === undefined ? getDefaultIntegrations(options) : userOptions.defaultIntegrations;
options.integrations = getIntegrationsToSetup({
integrations: safeFactory(userOptions.integrations, { loggerMessage: 'The integrations threw an error' }),
defaultIntegrations,
});
initAndBind(ReactNativeClient, options);
if (__DEV__) {
checkSentryJsSdkVersionMismatch();
}
if (isExpoGo()) {
debug.log('Offline caching, native errors features are not available in Expo Go.');
debug.log('Use EAS Build / Native Release Build to test these features.');
}
if (RN_GLOBAL_OBJ.__SENTRY_OPTIONS__) {
debug.log('Sentry JS initialized with options from the options file.');
// Adoption marker for the "capture app-start errors" feature: shipping
// `sentry.options.json` is what opts users in (the Metro serializer bundles
// it into JS as `__SENTRY_OPTIONS__`, and native reads it before JS runs).
registerFeatureMarker(CAPTURE_APP_START_ERRORS_INTEGRATION_NAME);
}
}
/**
* Inits the Sentry React Native SDK with automatic instrumentation and wrapped features.
*/
export function wrap(RootComponent, options) {
var _a;
const profilerProps = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.profilerProps), { name: (_a = RootComponent.displayName) !== null && _a !== void 0 ? _a : 'Root', updateProps: {} });
const ProfilerComponent = isWeb() ? Profiler : ReactNativeProfiler;
const RootApp = appProps => {
var _a;
return (React.createElement(TouchEventBoundary, Object.assign({}, ((_a = options === null || options === void 0 ? void 0 : options.touchEventBoundaryProps) !== null && _a !== void 0 ? _a : {})),
React.createElement(ProfilerComponent, Object.assign({}, profilerProps),
React.createElement(FeedbackFormProvider, null,
React.createElement(RootComponent, Object.assign({}, appProps))))));
};
return RootApp;
}
/**
* If native client is available it will trigger a native crash.
* Use this only for testing purposes.
*/
export function nativeCrash() {
NATIVE.nativeCrash();
}
/**
* Signals that the application has finished loading and is ready for user interaction.
*
* Call this when your app is truly ready — after async initialization, data loading,
* splash screen dismissal, auth session restore, etc. This marks the end of the app start span,
* giving you a more accurate measurement of perceived startup time.
*
* If not called, the SDK falls back to the root component mount time (via `Sentry.wrap()`)
* or JS bundle execution start.
*
* @experimental This API is subject to change in future versions.
*
* @example
* ```ts
* await loadRemoteConfig();
* await restoreSession();
* SplashScreen.hide();
* Sentry.appLoaded();
* ```
*/
export function appLoaded() {
// oxlint-disable-next-line typescript-eslint(no-floating-promises)
_appLoaded();
}
/**
* Extends the app start window so work done after initialization (remote config, session restore,
* splash screen dismissal, etc.) is included in the app start measurement. Call
* {@link finishExtendedAppStart} when the app is ready, or attach child spans via
* {@link getExtendedAppStartSpan} to break the extended work down.
*
* Requires standalone app start tracing (`_experiments.enableStandaloneAppStartTracing`). No-ops if
* the app start transaction was already created, if extend was already called, or if called before
* `Sentry.init()`.
*
* @experimental This API is subject to change in future versions.
*
* @example
* ```ts
* Sentry.extendAppStart();
* await initializeRemoteConfig();
* Sentry.finishExtendedAppStart();
* ```
*/
export function extendAppStart() {
_extendAppStart();
}
/**
* Returns the extended app start span for attaching child spans, or a no-op span when there is no
* active extension. Only meaningful between {@link extendAppStart} and {@link finishExtendedAppStart}.
*
* @experimental This API is subject to change in future versions.
*
* @example
* ```ts
* Sentry.extendAppStart();
* const parentSpan = Sentry.getExtendedAppStartSpan();
* const child = Sentry.startInactiveSpan({ parentSpan, op: 'app.init', name: 'fetch remote config' });
* await loadRemoteConfig();
* child.end();
* Sentry.finishExtendedAppStart();
* ```
*/
export function getExtendedAppStartSpan() {
return _getExtendedAppStartSpan();
}
/**
* Finishes the app start extension started with {@link extendAppStart}, finalizing the app start
* transaction (its duration is trimmed to the last child span). No-ops if there is no active
* extension.
*
* Returns a promise that resolves once the app start transaction has been captured. `await` it
* before {@link flush} (e.g. before a code-push/expo update) to make sure the app start data is
* queued.
*
* @experimental This API is subject to change in future versions.
*/
export function finishExtendedAppStart() {
return _finishExtendedAppStart();
}
/**
* Flushes all pending events in the queue to disk.
* Use this before applying any realtime updates such as code-push or expo updates.
*/
export function flush() {
return __awaiter(this, void 0, void 0, function* () {
try {
const client = getClient();
if (client) {
const result = yield client.flush();
return result;
}
}
catch (_) { }
debug.error('Failed to flush the event queue.');
return false;
});
}
/**
* Closes the SDK, stops sending events.
*/
export function close() {
return __awaiter(this, void 0, void 0, function* () {
try {
const client = getClient();
if (client) {
yield client.close();
}
}
catch (e) {
debug.error('Failed to close the SDK');
}
});
}
/**
* Creates a new scope with and executes the given operation within.
* The scope is automatically removed once the operation
* finishes or throws.
*
* This is essentially a convenience function for:
*
* pushScope();
* callback();
* popScope();
*
* @param callback that will be enclosed into push/popScope.
*/
export function withScope(callback) {
const safeCallback = (scope) => {
try {
return callback(scope);
}
catch (e) {
debug.error('Error while running withScope callback', e);
return undefined;
}
};
return coreWithScope(safeCallback);
}
/**
* Returns if the app crashed in the last run.
*/
export function crashedLastRun() {
return __awaiter(this, void 0, void 0, function* () {
return NATIVE.crashedLastRun();
});
}
/**
* Pauses app hang tracking on iOS.
*
* App hang detection will ignore detected app hangs until
* `resumeAppHangTracking` is called.
*
* Use this when showing system dialogs (e.g., permission prompts)
* that block the main thread but are not real hangs.
*
* No-op on Android and when native is not available.
*
* @platform ios
*/
export function pauseAppHangTracking() {
NATIVE.pauseAppHangTracking();
}
/**
* Resumes app hang tracking on iOS after it was paused.
*
* No-op on Android and when native is not available.
*
* @platform ios
*/
export function resumeAppHangTracking() {
NATIVE.resumeAppHangTracking();
}
//# sourceMappingURL=sdk.js.map