@sentry/react-native 8.3.0 → 8.5.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/RNSentry.podspec +1 -1
- package/android/build.gradle +2 -2
- package/android/libs/replay-stubs.jar +0 -0
- package/android/replay-stubs/build.gradle +1 -1
- package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +56 -4
- package/android/src/main/java/io/sentry/react/RNSentryStart.java +13 -0
- package/android/src/main/java/io/sentry/react/RNSentryVersion.java +1 -1
- package/android/src/newarch/java/io/sentry/react/RNSentryModule.java +10 -0
- package/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +10 -0
- package/dist/js/NativeRNSentry.d.ts +2 -0
- package/dist/js/NativeRNSentry.d.ts.map +1 -1
- package/dist/js/NativeRNSentry.js.map +1 -1
- package/dist/js/feedback/FeedbackWidgetManager.d.ts +3 -1
- package/dist/js/feedback/FeedbackWidgetManager.d.ts.map +1 -1
- package/dist/js/feedback/FeedbackWidgetManager.js +15 -1
- package/dist/js/feedback/FeedbackWidgetManager.js.map +1 -1
- package/dist/js/feedback/FeedbackWidgetProvider.d.ts +3 -2
- package/dist/js/feedback/FeedbackWidgetProvider.d.ts.map +1 -1
- package/dist/js/feedback/FeedbackWidgetProvider.js +12 -4
- package/dist/js/feedback/FeedbackWidgetProvider.js.map +1 -1
- package/dist/js/feedback/ShakeToReportBug.d.ts +27 -0
- package/dist/js/feedback/ShakeToReportBug.d.ts.map +1 -0
- package/dist/js/feedback/ShakeToReportBug.js +83 -0
- package/dist/js/feedback/ShakeToReportBug.js.map +1 -0
- package/dist/js/feedback/integration.d.ts +11 -0
- package/dist/js/feedback/integration.d.ts.map +1 -1
- package/dist/js/feedback/integration.js +7 -1
- package/dist/js/feedback/integration.js.map +1 -1
- package/dist/js/index.d.ts +3 -3
- package/dist/js/index.d.ts.map +1 -1
- package/dist/js/index.js +2 -2
- package/dist/js/index.js.map +1 -1
- package/dist/js/integrations/debugsymbolicator.js +3 -1
- package/dist/js/integrations/debugsymbolicator.js.map +1 -1
- package/dist/js/integrations/default.d.ts.map +1 -1
- package/dist/js/integrations/default.js +2 -1
- package/dist/js/integrations/default.js.map +1 -1
- package/dist/js/integrations/expocontext.d.ts.map +1 -1
- package/dist/js/integrations/expocontext.js +35 -1
- package/dist/js/integrations/expocontext.js.map +1 -1
- package/dist/js/integrations/exports.d.ts +1 -0
- package/dist/js/integrations/exports.d.ts.map +1 -1
- package/dist/js/integrations/exports.js +1 -0
- package/dist/js/integrations/exports.js.map +1 -1
- package/dist/js/integrations/expoupdateslistener.d.ts +38 -0
- package/dist/js/integrations/expoupdateslistener.d.ts.map +1 -0
- package/dist/js/integrations/expoupdateslistener.js +130 -0
- package/dist/js/integrations/expoupdateslistener.js.map +1 -0
- package/dist/js/misc.js +2 -2
- package/dist/js/misc.js.map +1 -1
- package/dist/js/options.d.ts +12 -0
- package/dist/js/options.d.ts.map +1 -1
- package/dist/js/options.js.map +1 -1
- package/dist/js/sdk.d.ts.map +1 -1
- package/dist/js/sdk.js +3 -2
- package/dist/js/sdk.js.map +1 -1
- package/dist/js/tools/metroconfig.d.ts +4 -0
- package/dist/js/tools/metroconfig.d.ts.map +1 -1
- package/dist/js/tools/metroconfig.js +46 -1
- package/dist/js/tools/metroconfig.js.map +1 -1
- package/dist/js/tools/utils.d.ts.map +1 -1
- package/dist/js/tools/utils.js +2 -3
- package/dist/js/tools/utils.js.map +1 -1
- package/dist/js/tracing/expoAsset.d.ts +42 -0
- package/dist/js/tracing/expoAsset.d.ts.map +1 -0
- package/dist/js/tracing/expoAsset.js +60 -0
- package/dist/js/tracing/expoAsset.js.map +1 -0
- package/dist/js/tracing/expoImage.d.ts +61 -0
- package/dist/js/tracing/expoImage.d.ts.map +1 -0
- package/dist/js/tracing/expoImage.js +101 -0
- package/dist/js/tracing/expoImage.js.map +1 -0
- package/dist/js/tracing/index.d.ts +4 -0
- package/dist/js/tracing/index.d.ts.map +1 -1
- package/dist/js/tracing/index.js +2 -0
- package/dist/js/tracing/index.js.map +1 -1
- package/dist/js/tracing/integrations/appStart.d.ts +8 -0
- package/dist/js/tracing/integrations/appStart.d.ts.map +1 -1
- package/dist/js/tracing/integrations/appStart.js +56 -7
- package/dist/js/tracing/integrations/appStart.js.map +1 -1
- package/dist/js/tracing/integrations/nativeFrames.d.ts.map +1 -1
- package/dist/js/tracing/integrations/nativeFrames.js +32 -22
- package/dist/js/tracing/integrations/nativeFrames.js.map +1 -1
- package/dist/js/tracing/integrations/timeToDisplayIntegration.d.ts.map +1 -1
- package/dist/js/tracing/integrations/timeToDisplayIntegration.js +1 -0
- package/dist/js/tracing/integrations/timeToDisplayIntegration.js.map +1 -1
- package/dist/js/tracing/origin.d.ts +2 -0
- package/dist/js/tracing/origin.d.ts.map +1 -1
- package/dist/js/tracing/origin.js +2 -0
- package/dist/js/tracing/origin.js.map +1 -1
- package/dist/js/tracing/reactnativenavigation.js +1 -1
- package/dist/js/tracing/reactnativenavigation.js.map +1 -1
- package/dist/js/tracing/reactnavigation.d.ts.map +1 -1
- package/dist/js/tracing/reactnavigation.js +12 -5
- package/dist/js/tracing/reactnavigation.js.map +1 -1
- package/dist/js/tracing/utils.d.ts +27 -1
- package/dist/js/tracing/utils.d.ts.map +1 -1
- package/dist/js/tracing/utils.js +66 -1
- package/dist/js/tracing/utils.js.map +1 -1
- package/dist/js/utils/ignorerequirecyclelogs.js +1 -1
- package/dist/js/utils/ignorerequirecyclelogs.js.map +1 -1
- package/dist/js/utils/safe.d.ts +1 -1
- package/dist/js/utils/safe.d.ts.map +1 -1
- package/dist/js/utils/safe.js.map +1 -1
- package/dist/js/version.d.ts +1 -1
- package/dist/js/version.js +1 -1
- package/dist/js/version.js.map +1 -1
- package/ios/RNSentry.mm +47 -1
- package/ios/RNSentryEvents.h +1 -0
- package/ios/RNSentryEvents.m +1 -0
- package/ios/RNSentryVersion.m +1 -1
- package/package.json +14 -14
- package/plugin/build/utils.d.ts +1 -0
- package/plugin/build/utils.js +19 -1
- package/plugin/build/withSentry.d.ts +1 -0
- package/plugin/build/withSentry.js +29 -1
- package/plugin/build/withSentryAndroidGradlePlugin.d.ts +1 -1
- package/plugin/build/withSentryAndroidGradlePlugin.js +1 -1
- package/scripts/sentry-xcode.sh +14 -0
- package/sentry.gradle +13 -0
- package/src/js/NativeRNSentry.ts +2 -0
- package/ts3.8/dist/js/NativeRNSentry.d.ts +2 -0
- package/ts3.8/dist/js/feedback/FeedbackWidgetManager.d.ts +3 -1
- package/ts3.8/dist/js/feedback/FeedbackWidgetProvider.d.ts +3 -2
- package/ts3.8/dist/js/feedback/ShakeToReportBug.d.ts +27 -0
- package/ts3.8/dist/js/feedback/integration.d.ts +11 -0
- package/ts3.8/dist/js/index.d.ts +3 -3
- package/ts3.8/dist/js/integrations/exports.d.ts +1 -0
- package/ts3.8/dist/js/integrations/expoupdateslistener.d.ts +38 -0
- package/ts3.8/dist/js/options.d.ts +12 -0
- package/ts3.8/dist/js/tracing/expoAsset.d.ts +42 -0
- package/ts3.8/dist/js/tracing/expoImage.d.ts +61 -0
- package/ts3.8/dist/js/tracing/index.d.ts +4 -0
- package/ts3.8/dist/js/tracing/integrations/appStart.d.ts +8 -0
- package/ts3.8/dist/js/tracing/origin.d.ts +2 -0
- package/ts3.8/dist/js/tracing/utils.d.ts +27 -1
- package/ts3.8/dist/js/utils/safe.d.ts +1 -1
- package/ts3.8/dist/js/version.d.ts +1 -1
package/dist/js/tools/utils.js
CHANGED
|
@@ -88,10 +88,9 @@ const PRELUDE_MODULE_PATH = '__prelude__';
|
|
|
88
88
|
* Prepends the module after default required prelude modules.
|
|
89
89
|
*/
|
|
90
90
|
function prependModule(modules, module) {
|
|
91
|
+
var _a;
|
|
91
92
|
const modifiedPreModules = [...modules];
|
|
92
|
-
if (modifiedPreModules.length > 0 &&
|
|
93
|
-
modifiedPreModules[0] !== undefined &&
|
|
94
|
-
modifiedPreModules[0].path === PRELUDE_MODULE_PATH) {
|
|
93
|
+
if (modifiedPreModules.length > 0 && ((_a = modifiedPreModules[0]) === null || _a === void 0 ? void 0 : _a.path) === PRELUDE_MODULE_PATH) {
|
|
95
94
|
// prelude module must be first as it measures the bundle startup time
|
|
96
95
|
modifiedPreModules.unshift(modules[0]);
|
|
97
96
|
modifiedPreModules[1] = module;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/js/tools/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as crypto from 'crypto';\n// eslint-disable-next-line import/no-extraneous-dependencies\nimport type { MetroConfig, MixedOutput, Module, ReadOnlyGraph, SerializerOptions } from 'metro';\nimport type CountingSet from 'metro/src/lib/CountingSet'; // types are in src but exports are in private\nimport countLines from './vendor/metro/countLines';\n\nexport type MetroCustomSerializer = Required<Required<MetroConfig>['serializer']>['customSerializer'] | undefined;\n\n// Variant of MixedOutput\n// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/DeltaBundler/types.flow.js#L21\nexport type VirtualJSOutput = {\n type: 'js/script/virtual';\n data: {\n code: string;\n lineCount: number;\n map: [];\n };\n};\n\nexport type Bundle = {\n modules: Array<[id: number, code: string]>;\n post: string;\n pre: string;\n};\n\nexport type SentryMetroSerializerOptionsExtras = {\n sentryBundleCallback?: (bundle: Bundle) => Bundle;\n};\n\nexport type SerializedBundle = { code: string; map: string };\n\nexport type MetroSerializerOutput = string | SerializedBundle | Promise<string | SerializedBundle>;\n\nexport type MetroSerializer = (\n entryPoint: string,\n preModules: ReadonlyArray<Module>,\n graph: ReadOnlyGraph,\n options: SerializerOptions & SentryMetroSerializerOptionsExtras,\n) => MetroSerializerOutput;\n\n/**\n * Returns minified Debug ID code snippet.\n */\nexport function createDebugIdSnippet(debugId: string): string {\n return `var _sentryDebugIds,_sentryDebugIdIdentifier;void 0===_sentryDebugIds&&(_sentryDebugIds={});try{var stack=(new Error).stack;stack&&(_sentryDebugIds[stack]=\"${debugId}\",_sentryDebugIdIdentifier=\"sentry-dbid-${debugId}\")}catch(e){}`;\n}\n\n/**\n * Deterministically hashes a string and turns the hash into a uuid.\n *\n * https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/58271f1af2ade6b3e64d393d70376ae53bc5bd2f/packages/bundler-plugin-core/src/utils.ts#L174\n */\nexport function stringToUUID(str: string): string {\n const md5sum = crypto.createHash('md5');\n md5sum.update(str);\n const md5Hash = md5sum.digest('hex');\n\n // Position 16 is fixed to either 8, 9, a, or b in the uuid v4 spec (10xx in binary)\n // RFC 4122 section 4.4\n const v4variant = ['8', '9', 'a', 'b'][md5Hash.substring(16, 17).charCodeAt(0) % 4] as string;\n\n return `${md5Hash.substring(0, 8)}-${md5Hash.substring(8, 12)}-4${md5Hash.substring(\n 13,\n 16,\n )}-${v4variant}${md5Hash.substring(17, 20)}-${md5Hash.substring(20)}`.toLowerCase();\n}\n\n/**\n * Looks for a particular string pattern (`sdbid-[debug ID]`) in the bundle\n * source and extracts the bundle's debug ID from it.\n *\n * The string pattern is injected via the debug ID injection snipped.\n *\n * https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/40f918458ed449d8b3eabaf64d13c08218213f65/packages/bundler-plugin-core/src/debug-id-upload.ts#L293-L294\n */\nexport function determineDebugIdFromBundleSource(code: string): string | undefined {\n const match = code.match(\n /sentry-dbid-([0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12})/,\n );\n return match ? match[1] : undefined;\n}\n\n/**\n * CountingSet was added in Metro 0.72.0 before that NodeJS Set was used.\n *\n * https://github.com/facebook/metro/blob/fc29a1177f883144674cf85a813b58567f69d545/packages/metro/src/lib/CountingSet.js\n */\nfunction resolveSetCreator(): () => CountingSet<string> {\n const CountingSetFromPrivate = safeRequireCountingSetFromPrivate();\n if (CountingSetFromPrivate) {\n return () => new CountingSetFromPrivate.default();\n }\n\n const CountingSetFromSrc = safeRequireCountingSetFromSrc();\n if (CountingSetFromSrc) {\n return () => new CountingSetFromSrc.default();\n }\n\n return () => new Set() as unknown as CountingSet<string>;\n}\n\n/**\n * CountingSet was added in Metro 0.72.0 before that NodeJS Set was used.\n *\n * https://github.com/facebook/metro/blob/fc29a1177f883144674cf85a813b58567f69d545/packages/metro/src/lib/CountingSet.js\n */\nfunction safeRequireCountingSetFromSrc(): { default: new <T>() => CountingSet<T> } | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies\n return require('metro/src/lib/CountingSet');\n } catch (e) {\n return undefined;\n }\n}\n\n/**\n * CountingSet was moved to private in Metro 0.83.0. (all src exports were moved to private)\n *\n * https://github.com/facebook/metro/commit/ae6f42372ed361611b5672705f22081c2022cf28\n */\nfunction safeRequireCountingSetFromPrivate(): { default: new <T>() => CountingSet<T> } | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies\n return require('metro/private/lib/CountingSet');\n } catch (e) {\n return undefined;\n }\n}\n\nexport const createSet = resolveSetCreator();\n\nconst PRELUDE_MODULE_PATH = '__prelude__';\n\n/**\n * Prepends the module after default required prelude modules.\n */\nexport function prependModule(\n modules: readonly Module<MixedOutput>[],\n module: Module<VirtualJSOutput>,\n): Module<MixedOutput>[] {\n const modifiedPreModules = [...modules];\n if (\n modifiedPreModules.length > 0 &&\n modifiedPreModules[0] !== undefined &&\n modifiedPreModules[0].path === PRELUDE_MODULE_PATH\n ) {\n // prelude module must be first as it measures the bundle startup time\n modifiedPreModules.unshift(modules[0] as Module<VirtualJSOutput>);\n modifiedPreModules[1] = module;\n } else {\n modifiedPreModules.unshift(module);\n }\n return modifiedPreModules;\n}\n\n/**\n * Creates a virtual JS module with the given path and code.\n */\nexport function createVirtualJSModule(\n modulePath: string,\n moduleCode: string,\n): Module<VirtualJSOutput> & { setSource: (code: string) => void } {\n let sourceCode = moduleCode;\n\n return {\n setSource: (code: string) => {\n sourceCode = code;\n },\n dependencies: new Map(),\n getSource: () => Buffer.from(sourceCode),\n inverseDependencies: createSet(),\n path: modulePath,\n output: [\n {\n type: 'js/script/virtual',\n data: {\n code: sourceCode,\n lineCount: countLines(sourceCode),\n map: [],\n },\n },\n ],\n };\n}\n\n/**\n * Tries to load Expo config using `@expo/config` package.\n */\nexport function getExpoConfig(projectRoot: string): Partial<{\n name: string;\n version: string;\n}> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies\n const expoConfig = require('@expo/config') as {\n getConfig?: (projectRoot: string) => { exp: Record<string, unknown> };\n };\n if (expoConfig.getConfig) {\n const { exp } = expoConfig.getConfig(projectRoot);\n return {\n name: typeof exp.name === 'string' && exp.name ? exp.name : undefined,\n version: typeof exp.version === 'string' && exp.version ? exp.version : undefined,\n };\n }\n } catch {\n // @expo/config not available, do nothing\n }\n\n return {};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/js/tools/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as crypto from 'crypto';\n// eslint-disable-next-line import/no-extraneous-dependencies\nimport type { MetroConfig, MixedOutput, Module, ReadOnlyGraph, SerializerOptions } from 'metro';\nimport type CountingSet from 'metro/src/lib/CountingSet'; // types are in src but exports are in private\nimport countLines from './vendor/metro/countLines';\n\nexport type MetroCustomSerializer = Required<Required<MetroConfig>['serializer']>['customSerializer'] | undefined;\n\n// Variant of MixedOutput\n// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/DeltaBundler/types.flow.js#L21\nexport type VirtualJSOutput = {\n type: 'js/script/virtual';\n data: {\n code: string;\n lineCount: number;\n map: [];\n };\n};\n\nexport type Bundle = {\n modules: Array<[id: number, code: string]>;\n post: string;\n pre: string;\n};\n\nexport type SentryMetroSerializerOptionsExtras = {\n sentryBundleCallback?: (bundle: Bundle) => Bundle;\n};\n\nexport type SerializedBundle = { code: string; map: string };\n\nexport type MetroSerializerOutput = string | SerializedBundle | Promise<string | SerializedBundle>;\n\nexport type MetroSerializer = (\n entryPoint: string,\n preModules: ReadonlyArray<Module>,\n graph: ReadOnlyGraph,\n options: SerializerOptions & SentryMetroSerializerOptionsExtras,\n) => MetroSerializerOutput;\n\n/**\n * Returns minified Debug ID code snippet.\n */\nexport function createDebugIdSnippet(debugId: string): string {\n return `var _sentryDebugIds,_sentryDebugIdIdentifier;void 0===_sentryDebugIds&&(_sentryDebugIds={});try{var stack=(new Error).stack;stack&&(_sentryDebugIds[stack]=\"${debugId}\",_sentryDebugIdIdentifier=\"sentry-dbid-${debugId}\")}catch(e){}`;\n}\n\n/**\n * Deterministically hashes a string and turns the hash into a uuid.\n *\n * https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/58271f1af2ade6b3e64d393d70376ae53bc5bd2f/packages/bundler-plugin-core/src/utils.ts#L174\n */\nexport function stringToUUID(str: string): string {\n const md5sum = crypto.createHash('md5');\n md5sum.update(str);\n const md5Hash = md5sum.digest('hex');\n\n // Position 16 is fixed to either 8, 9, a, or b in the uuid v4 spec (10xx in binary)\n // RFC 4122 section 4.4\n const v4variant = ['8', '9', 'a', 'b'][md5Hash.substring(16, 17).charCodeAt(0) % 4] as string;\n\n return `${md5Hash.substring(0, 8)}-${md5Hash.substring(8, 12)}-4${md5Hash.substring(\n 13,\n 16,\n )}-${v4variant}${md5Hash.substring(17, 20)}-${md5Hash.substring(20)}`.toLowerCase();\n}\n\n/**\n * Looks for a particular string pattern (`sdbid-[debug ID]`) in the bundle\n * source and extracts the bundle's debug ID from it.\n *\n * The string pattern is injected via the debug ID injection snipped.\n *\n * https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/40f918458ed449d8b3eabaf64d13c08218213f65/packages/bundler-plugin-core/src/debug-id-upload.ts#L293-L294\n */\nexport function determineDebugIdFromBundleSource(code: string): string | undefined {\n const match = code.match(\n /sentry-dbid-([0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12})/,\n );\n return match ? match[1] : undefined;\n}\n\n/**\n * CountingSet was added in Metro 0.72.0 before that NodeJS Set was used.\n *\n * https://github.com/facebook/metro/blob/fc29a1177f883144674cf85a813b58567f69d545/packages/metro/src/lib/CountingSet.js\n */\nfunction resolveSetCreator(): () => CountingSet<string> {\n const CountingSetFromPrivate = safeRequireCountingSetFromPrivate();\n if (CountingSetFromPrivate) {\n return () => new CountingSetFromPrivate.default();\n }\n\n const CountingSetFromSrc = safeRequireCountingSetFromSrc();\n if (CountingSetFromSrc) {\n return () => new CountingSetFromSrc.default();\n }\n\n return () => new Set() as unknown as CountingSet<string>;\n}\n\n/**\n * CountingSet was added in Metro 0.72.0 before that NodeJS Set was used.\n *\n * https://github.com/facebook/metro/blob/fc29a1177f883144674cf85a813b58567f69d545/packages/metro/src/lib/CountingSet.js\n */\nfunction safeRequireCountingSetFromSrc(): { default: new <T>() => CountingSet<T> } | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies\n return require('metro/src/lib/CountingSet');\n } catch (e) {\n return undefined;\n }\n}\n\n/**\n * CountingSet was moved to private in Metro 0.83.0. (all src exports were moved to private)\n *\n * https://github.com/facebook/metro/commit/ae6f42372ed361611b5672705f22081c2022cf28\n */\nfunction safeRequireCountingSetFromPrivate(): { default: new <T>() => CountingSet<T> } | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies\n return require('metro/private/lib/CountingSet');\n } catch (e) {\n return undefined;\n }\n}\n\nexport const createSet = resolveSetCreator();\n\nconst PRELUDE_MODULE_PATH = '__prelude__';\n\n/**\n * Prepends the module after default required prelude modules.\n */\nexport function prependModule(\n modules: readonly Module<MixedOutput>[],\n module: Module<VirtualJSOutput>,\n): Module<MixedOutput>[] {\n const modifiedPreModules = [...modules];\n if (modifiedPreModules.length > 0 && modifiedPreModules[0]?.path === PRELUDE_MODULE_PATH) {\n // prelude module must be first as it measures the bundle startup time\n modifiedPreModules.unshift(modules[0] as Module<VirtualJSOutput>);\n modifiedPreModules[1] = module;\n } else {\n modifiedPreModules.unshift(module);\n }\n return modifiedPreModules;\n}\n\n/**\n * Creates a virtual JS module with the given path and code.\n */\nexport function createVirtualJSModule(\n modulePath: string,\n moduleCode: string,\n): Module<VirtualJSOutput> & { setSource: (code: string) => void } {\n let sourceCode = moduleCode;\n\n return {\n setSource: (code: string) => {\n sourceCode = code;\n },\n dependencies: new Map(),\n getSource: () => Buffer.from(sourceCode),\n inverseDependencies: createSet(),\n path: modulePath,\n output: [\n {\n type: 'js/script/virtual',\n data: {\n code: sourceCode,\n lineCount: countLines(sourceCode),\n map: [],\n },\n },\n ],\n };\n}\n\n/**\n * Tries to load Expo config using `@expo/config` package.\n */\nexport function getExpoConfig(projectRoot: string): Partial<{\n name: string;\n version: string;\n}> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies\n const expoConfig = require('@expo/config') as {\n getConfig?: (projectRoot: string) => { exp: Record<string, unknown> };\n };\n if (expoConfig.getConfig) {\n const { exp } = expoConfig.getConfig(projectRoot);\n return {\n name: typeof exp.name === 'string' && exp.name ? exp.name : undefined,\n version: typeof exp.version === 'string' && exp.version ? exp.version : undefined,\n };\n }\n } catch {\n // @expo/config not available, do nothing\n }\n\n return {};\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal interface for expo-asset's Asset instance.
|
|
3
|
+
* We define this to avoid a hard dependency on expo-asset.
|
|
4
|
+
*/
|
|
5
|
+
export interface ExpoAssetInstance {
|
|
6
|
+
name: string;
|
|
7
|
+
type: string;
|
|
8
|
+
hash: string | null;
|
|
9
|
+
uri: string;
|
|
10
|
+
localUri: string | null;
|
|
11
|
+
width: number | null;
|
|
12
|
+
height: number | null;
|
|
13
|
+
downloaded: boolean;
|
|
14
|
+
downloadAsync(): Promise<ExpoAssetInstance>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Represents the expo-asset `Asset` class with its static methods.
|
|
18
|
+
* We only describe the methods that we instrument.
|
|
19
|
+
*/
|
|
20
|
+
export interface ExpoAsset {
|
|
21
|
+
loadAsync(moduleId: number | number[] | string | string[]): Promise<ExpoAssetInstance[]>;
|
|
22
|
+
fromModule(virtualAssetModule: number | string): ExpoAssetInstance;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Wraps expo-asset's `Asset` class to add automated performance monitoring.
|
|
26
|
+
*
|
|
27
|
+
* This function instruments `Asset.loadAsync` static method
|
|
28
|
+
* to create performance spans that measure how long asset loading takes.
|
|
29
|
+
*
|
|
30
|
+
* @param assetClass - The `Asset` class from `expo-asset`
|
|
31
|
+
* @returns The same class with instrumented static methods
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { Asset } from 'expo-asset';
|
|
36
|
+
* import * as Sentry from '@sentry/react-native';
|
|
37
|
+
*
|
|
38
|
+
* Sentry.wrapExpoAsset(Asset);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function wrapExpoAsset<T extends ExpoAsset>(assetClass: T): T;
|
|
42
|
+
//# sourceMappingURL=expoAsset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoAsset.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/expoAsset.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACzF,UAAU,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,GAAG,iBAAiB,CAAC;CACpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAcnE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { SPAN_ORIGIN_AUTO_RESOURCE_EXPO_ASSET } from './origin';
|
|
2
|
+
import { describeUrl, traceAsyncOperation } from './utils';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps expo-asset's `Asset` class to add automated performance monitoring.
|
|
5
|
+
*
|
|
6
|
+
* This function instruments `Asset.loadAsync` static method
|
|
7
|
+
* to create performance spans that measure how long asset loading takes.
|
|
8
|
+
*
|
|
9
|
+
* @param assetClass - The `Asset` class from `expo-asset`
|
|
10
|
+
* @returns The same class with instrumented static methods
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { Asset } from 'expo-asset';
|
|
15
|
+
* import * as Sentry from '@sentry/react-native';
|
|
16
|
+
*
|
|
17
|
+
* Sentry.wrapExpoAsset(Asset);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function wrapExpoAsset(assetClass) {
|
|
21
|
+
if (!assetClass) {
|
|
22
|
+
return assetClass;
|
|
23
|
+
}
|
|
24
|
+
if (assetClass.__sentryWrapped) {
|
|
25
|
+
return assetClass;
|
|
26
|
+
}
|
|
27
|
+
wrapLoadAsync(assetClass);
|
|
28
|
+
assetClass.__sentryWrapped = true;
|
|
29
|
+
return assetClass;
|
|
30
|
+
}
|
|
31
|
+
function wrapLoadAsync(assetClass) {
|
|
32
|
+
if (!assetClass.loadAsync) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const originalLoadAsync = assetClass.loadAsync.bind(assetClass);
|
|
36
|
+
assetClass.loadAsync = ((moduleId) => {
|
|
37
|
+
const moduleIds = Array.isArray(moduleId) ? moduleId : [moduleId];
|
|
38
|
+
const assetCount = moduleIds.length;
|
|
39
|
+
const description = describeModuleIds(moduleIds);
|
|
40
|
+
return traceAsyncOperation({
|
|
41
|
+
op: 'resource.asset',
|
|
42
|
+
name: `Asset load ${description}`,
|
|
43
|
+
attributes: {
|
|
44
|
+
'sentry.origin': SPAN_ORIGIN_AUTO_RESOURCE_EXPO_ASSET,
|
|
45
|
+
'asset.count': assetCount,
|
|
46
|
+
},
|
|
47
|
+
}, () => originalLoadAsync(moduleId));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function describeModuleIds(moduleIds) {
|
|
51
|
+
if (moduleIds.length === 1) {
|
|
52
|
+
const id = moduleIds[0];
|
|
53
|
+
if (typeof id === 'string') {
|
|
54
|
+
return describeUrl(id);
|
|
55
|
+
}
|
|
56
|
+
return `asset #${id}`;
|
|
57
|
+
}
|
|
58
|
+
return `${moduleIds.length} assets`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=expoAsset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoAsset.js","sourceRoot":"","sources":["../../../src/js/tracing/expoAsset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA2B3D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAAsB,UAAa;IAC9D,IAAI,CAAC,UAAU,EAAE;QACf,OAAO,UAAU,CAAC;KACnB;IAED,IAAK,UAAgD,CAAC,eAAe,EAAE;QACrE,OAAO,UAAU,CAAC;KACnB;IAED,aAAa,CAAC,UAAU,CAAC,CAAC;IAEzB,UAAgD,CAAC,eAAe,GAAG,IAAI,CAAC;IAEzE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,aAAa,CAAsB,UAAa;IACvD,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE;QACzB,OAAO;KACR;IAED,MAAM,iBAAiB,GAAG,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEhE,UAAU,CAAC,SAAS,GAAG,CAAC,CAAC,QAA+C,EAAgC,EAAE;QACxG,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC;QACpC,MAAM,WAAW,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAEjD,OAAO,mBAAmB,CACxB;YACE,EAAE,EAAE,gBAAgB;YACpB,IAAI,EAAE,cAAc,WAAW,EAAE;YACjC,UAAU,EAAE;gBACV,eAAe,EAAE,oCAAoC;gBACrD,aAAa,EAAE,UAAU;aAC1B;SACF,EACD,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAClC,CAAC;IACJ,CAAC,CAAmB,CAAC;AACvB,CAAC;AAED,SAAS,iBAAiB,CAAC,SAA8B;IACvD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;QAC1B,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE;YAC1B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;SACxB;QACD,OAAO,UAAU,EAAE,EAAE,CAAC;KACvB;IACD,OAAO,GAAG,SAAS,CAAC,MAAM,SAAS,CAAC;AACtC,CAAC","sourcesContent":["import { SPAN_ORIGIN_AUTO_RESOURCE_EXPO_ASSET } from './origin';\nimport { describeUrl, traceAsyncOperation } from './utils';\n\n/**\n * Internal interface for expo-asset's Asset instance.\n * We define this to avoid a hard dependency on expo-asset.\n */\nexport interface ExpoAssetInstance {\n name: string;\n type: string;\n hash: string | null;\n uri: string;\n localUri: string | null;\n width: number | null;\n height: number | null;\n downloaded: boolean;\n downloadAsync(): Promise<ExpoAssetInstance>;\n}\n\n/**\n * Represents the expo-asset `Asset` class with its static methods.\n * We only describe the methods that we instrument.\n */\nexport interface ExpoAsset {\n loadAsync(moduleId: number | number[] | string | string[]): Promise<ExpoAssetInstance[]>;\n fromModule(virtualAssetModule: number | string): ExpoAssetInstance;\n}\n\n/**\n * Wraps expo-asset's `Asset` class to add automated performance monitoring.\n *\n * This function instruments `Asset.loadAsync` static method\n * to create performance spans that measure how long asset loading takes.\n *\n * @param assetClass - The `Asset` class from `expo-asset`\n * @returns The same class with instrumented static methods\n *\n * @example\n * ```typescript\n * import { Asset } from 'expo-asset';\n * import * as Sentry from '@sentry/react-native';\n *\n * Sentry.wrapExpoAsset(Asset);\n * ```\n */\nexport function wrapExpoAsset<T extends ExpoAsset>(assetClass: T): T {\n if (!assetClass) {\n return assetClass;\n }\n\n if ((assetClass as T & { __sentryWrapped?: boolean }).__sentryWrapped) {\n return assetClass;\n }\n\n wrapLoadAsync(assetClass);\n\n (assetClass as T & { __sentryWrapped?: boolean }).__sentryWrapped = true;\n\n return assetClass;\n}\n\nfunction wrapLoadAsync<T extends ExpoAsset>(assetClass: T): void {\n if (!assetClass.loadAsync) {\n return;\n }\n\n const originalLoadAsync = assetClass.loadAsync.bind(assetClass);\n\n assetClass.loadAsync = ((moduleId: number | number[] | string | string[]): Promise<ExpoAssetInstance[]> => {\n const moduleIds = Array.isArray(moduleId) ? moduleId : [moduleId];\n const assetCount = moduleIds.length;\n const description = describeModuleIds(moduleIds);\n\n return traceAsyncOperation(\n {\n op: 'resource.asset',\n name: `Asset load ${description}`,\n attributes: {\n 'sentry.origin': SPAN_ORIGIN_AUTO_RESOURCE_EXPO_ASSET,\n 'asset.count': assetCount,\n },\n },\n () => originalLoadAsync(moduleId),\n );\n }) as T['loadAsync'];\n}\n\nfunction describeModuleIds(moduleIds: (number | string)[]): string {\n if (moduleIds.length === 1) {\n const id = moduleIds[0];\n if (typeof id === 'string') {\n return describeUrl(id);\n }\n return `asset #${id}`;\n }\n return `${moduleIds.length} assets`;\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal interface for expo-image's ImageSource.
|
|
3
|
+
* We define this to avoid a hard dependency on expo-image.
|
|
4
|
+
*/
|
|
5
|
+
interface ExpoImageSource {
|
|
6
|
+
uri?: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
width?: number | null;
|
|
9
|
+
height?: number | null;
|
|
10
|
+
cacheKey?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Internal interface for expo-image's ImageLoadOptions.
|
|
14
|
+
* We define this to avoid a hard dependency on expo-image.
|
|
15
|
+
*/
|
|
16
|
+
interface ExpoImageLoadOptions {
|
|
17
|
+
maxWidth?: number;
|
|
18
|
+
maxHeight?: number;
|
|
19
|
+
onError?(error: Error, retry: () => void): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Internal interface for expo-image's ImageRef.
|
|
23
|
+
* We define this to avoid a hard dependency on expo-image.
|
|
24
|
+
*/
|
|
25
|
+
interface ExpoImageRef {
|
|
26
|
+
readonly width: number;
|
|
27
|
+
readonly height: number;
|
|
28
|
+
readonly scale: number;
|
|
29
|
+
readonly mediaType: string | null;
|
|
30
|
+
readonly isAnimated?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Represents the expo-image `Image` class with its static methods.
|
|
34
|
+
* We only describe the methods that we instrument.
|
|
35
|
+
*/
|
|
36
|
+
export interface ExpoImage {
|
|
37
|
+
prefetch(urls: string | string[], cachePolicyOrOptions?: any): Promise<boolean>;
|
|
38
|
+
loadAsync(source: ExpoImageSource | string | number, options?: ExpoImageLoadOptions): Promise<ExpoImageRef>;
|
|
39
|
+
clearMemoryCache?(): Promise<boolean>;
|
|
40
|
+
clearDiskCache?(): Promise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Wraps expo-image's `Image` class to add automated performance monitoring.
|
|
44
|
+
*
|
|
45
|
+
* This function instruments `Image.prefetch` and `Image.loadAsync` static methods
|
|
46
|
+
* to create performance spans that measure how long image prefetching and loading take.
|
|
47
|
+
*
|
|
48
|
+
* @param imageClass - The `Image` class from `expo-image`
|
|
49
|
+
* @returns The same class with instrumented static methods
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { Image } from 'expo-image';
|
|
54
|
+
* import * as Sentry from '@sentry/react-native';
|
|
55
|
+
*
|
|
56
|
+
* Sentry.wrapExpoImage(Image);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function wrapExpoImage<T extends ExpoImage>(imageClass: T): T;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=expoImage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoImage.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/expoImage.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,UAAU,eAAe;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CACjD;AAED;;;GAGG;AACH,UAAU,YAAY;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IAExB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,oBAAoB,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChF,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5G,gBAAgB,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,cAAc,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAenE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { SPAN_STATUS_ERROR, SPAN_STATUS_OK, startInactiveSpan } from '@sentry/core';
|
|
2
|
+
import { SPAN_ORIGIN_AUTO_RESOURCE_EXPO_IMAGE } from './origin';
|
|
3
|
+
import { describeUrl, sanitizeUrl, traceAsyncOperation } from './utils';
|
|
4
|
+
/**
|
|
5
|
+
* Wraps expo-image's `Image` class to add automated performance monitoring.
|
|
6
|
+
*
|
|
7
|
+
* This function instruments `Image.prefetch` and `Image.loadAsync` static methods
|
|
8
|
+
* to create performance spans that measure how long image prefetching and loading take.
|
|
9
|
+
*
|
|
10
|
+
* @param imageClass - The `Image` class from `expo-image`
|
|
11
|
+
* @returns The same class with instrumented static methods
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { Image } from 'expo-image';
|
|
16
|
+
* import * as Sentry from '@sentry/react-native';
|
|
17
|
+
*
|
|
18
|
+
* Sentry.wrapExpoImage(Image);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function wrapExpoImage(imageClass) {
|
|
22
|
+
if (!imageClass) {
|
|
23
|
+
return imageClass;
|
|
24
|
+
}
|
|
25
|
+
if (imageClass.__sentryWrapped) {
|
|
26
|
+
return imageClass;
|
|
27
|
+
}
|
|
28
|
+
wrapPrefetch(imageClass);
|
|
29
|
+
wrapLoadAsync(imageClass);
|
|
30
|
+
imageClass.__sentryWrapped = true;
|
|
31
|
+
return imageClass;
|
|
32
|
+
}
|
|
33
|
+
function wrapPrefetch(imageClass) {
|
|
34
|
+
if (!imageClass.prefetch) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const originalPrefetch = imageClass.prefetch.bind(imageClass);
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
imageClass.prefetch = ((urls, cachePolicyOrOptions) => {
|
|
40
|
+
const urlList = Array.isArray(urls) ? urls : [urls];
|
|
41
|
+
const urlCount = urlList.length;
|
|
42
|
+
const firstUrl = urlList[0] || 'unknown';
|
|
43
|
+
const description = urlCount === 1 ? describeUrl(firstUrl) : `${urlCount} images`;
|
|
44
|
+
const span = startInactiveSpan({
|
|
45
|
+
op: 'resource.image.prefetch',
|
|
46
|
+
name: `Image prefetch ${description}`,
|
|
47
|
+
attributes: Object.assign({ 'sentry.origin': SPAN_ORIGIN_AUTO_RESOURCE_EXPO_IMAGE, 'image.url_count': urlCount }, (urlCount === 1 ? { 'image.url': sanitizeUrl(firstUrl) } : undefined)),
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
return originalPrefetch(urls, cachePolicyOrOptions)
|
|
51
|
+
.then(result => {
|
|
52
|
+
if (result) {
|
|
53
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SPAN_STATUS_OK });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SPAN_STATUS_ERROR, message: 'prefetch_failed' });
|
|
57
|
+
}
|
|
58
|
+
span === null || span === void 0 ? void 0 : span.end();
|
|
59
|
+
return result;
|
|
60
|
+
})
|
|
61
|
+
.catch((error) => {
|
|
62
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SPAN_STATUS_ERROR, message: String(error) });
|
|
63
|
+
span === null || span === void 0 ? void 0 : span.end();
|
|
64
|
+
throw error;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SPAN_STATUS_ERROR, message: String(error) });
|
|
69
|
+
span === null || span === void 0 ? void 0 : span.end();
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function wrapLoadAsync(imageClass) {
|
|
75
|
+
if (!imageClass.loadAsync) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const originalLoadAsync = imageClass.loadAsync.bind(imageClass);
|
|
79
|
+
imageClass.loadAsync = ((source, options) => {
|
|
80
|
+
const description = describeSource(source);
|
|
81
|
+
const imageUrl = typeof source === 'string' ? source : typeof source === 'object' && source.uri ? source.uri : undefined;
|
|
82
|
+
return traceAsyncOperation({
|
|
83
|
+
op: 'resource.image.load',
|
|
84
|
+
name: `Image load ${description}`,
|
|
85
|
+
attributes: Object.assign({ 'sentry.origin': SPAN_ORIGIN_AUTO_RESOURCE_EXPO_IMAGE }, (imageUrl ? { 'image.url': sanitizeUrl(imageUrl) } : undefined)),
|
|
86
|
+
}, () => originalLoadAsync(source, options));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function describeSource(source) {
|
|
90
|
+
if (typeof source === 'number') {
|
|
91
|
+
return `asset #${source}`;
|
|
92
|
+
}
|
|
93
|
+
if (typeof source === 'string') {
|
|
94
|
+
return describeUrl(source);
|
|
95
|
+
}
|
|
96
|
+
if (source.uri) {
|
|
97
|
+
return describeUrl(source.uri);
|
|
98
|
+
}
|
|
99
|
+
return 'unknown source';
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=expoImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoImage.js","sourceRoot":"","sources":["../../../src/js/tracing/expoImage.ts"],"names":[],"mappings":"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","sourcesContent":["import { SPAN_STATUS_ERROR, SPAN_STATUS_OK, startInactiveSpan } from '@sentry/core';\nimport { SPAN_ORIGIN_AUTO_RESOURCE_EXPO_IMAGE } from './origin';\nimport { describeUrl, sanitizeUrl, traceAsyncOperation } from './utils';\n\n/**\n * Internal interface for expo-image's ImageSource.\n * We define this to avoid a hard dependency on expo-image.\n */\ninterface ExpoImageSource {\n uri?: string;\n headers?: Record<string, string>;\n width?: number | null;\n height?: number | null;\n cacheKey?: string;\n}\n\n/**\n * Internal interface for expo-image's ImageLoadOptions.\n * We define this to avoid a hard dependency on expo-image.\n */\ninterface ExpoImageLoadOptions {\n maxWidth?: number;\n maxHeight?: number;\n onError?(error: Error, retry: () => void): void;\n}\n\n/**\n * Internal interface for expo-image's ImageRef.\n * We define this to avoid a hard dependency on expo-image.\n */\ninterface ExpoImageRef {\n readonly width: number;\n readonly height: number;\n readonly scale: number;\n readonly mediaType: string | null;\n readonly isAnimated?: boolean;\n}\n\n/**\n * Represents the expo-image `Image` class with its static methods.\n * We only describe the methods that we instrument.\n */\nexport interface ExpoImage {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n prefetch(urls: string | string[], cachePolicyOrOptions?: any): Promise<boolean>;\n loadAsync(source: ExpoImageSource | string | number, options?: ExpoImageLoadOptions): Promise<ExpoImageRef>;\n clearMemoryCache?(): Promise<boolean>;\n clearDiskCache?(): Promise<boolean>;\n}\n\n/**\n * Wraps expo-image's `Image` class to add automated performance monitoring.\n *\n * This function instruments `Image.prefetch` and `Image.loadAsync` static methods\n * to create performance spans that measure how long image prefetching and loading take.\n *\n * @param imageClass - The `Image` class from `expo-image`\n * @returns The same class with instrumented static methods\n *\n * @example\n * ```typescript\n * import { Image } from 'expo-image';\n * import * as Sentry from '@sentry/react-native';\n *\n * Sentry.wrapExpoImage(Image);\n * ```\n */\nexport function wrapExpoImage<T extends ExpoImage>(imageClass: T): T {\n if (!imageClass) {\n return imageClass;\n }\n\n if ((imageClass as T & { __sentryWrapped?: boolean }).__sentryWrapped) {\n return imageClass;\n }\n\n wrapPrefetch(imageClass);\n wrapLoadAsync(imageClass);\n\n (imageClass as T & { __sentryWrapped?: boolean }).__sentryWrapped = true;\n\n return imageClass;\n}\n\nfunction wrapPrefetch<T extends ExpoImage>(imageClass: T): void {\n if (!imageClass.prefetch) {\n return;\n }\n\n const originalPrefetch = imageClass.prefetch.bind(imageClass);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n imageClass.prefetch = ((urls: string | string[], cachePolicyOrOptions?: any): Promise<boolean> => {\n const urlList = Array.isArray(urls) ? urls : [urls];\n const urlCount = urlList.length;\n const firstUrl = urlList[0] || 'unknown';\n const description = urlCount === 1 ? describeUrl(firstUrl) : `${urlCount} images`;\n\n const span = startInactiveSpan({\n op: 'resource.image.prefetch',\n name: `Image prefetch ${description}`,\n attributes: {\n 'sentry.origin': SPAN_ORIGIN_AUTO_RESOURCE_EXPO_IMAGE,\n 'image.url_count': urlCount,\n ...(urlCount === 1 ? { 'image.url': sanitizeUrl(firstUrl) } : undefined),\n },\n });\n\n try {\n return originalPrefetch(urls, cachePolicyOrOptions)\n .then(result => {\n if (result) {\n span?.setStatus({ code: SPAN_STATUS_OK });\n } else {\n span?.setStatus({ code: SPAN_STATUS_ERROR, message: 'prefetch_failed' });\n }\n span?.end();\n return result;\n })\n .catch((error: unknown) => {\n span?.setStatus({ code: SPAN_STATUS_ERROR, message: String(error) });\n span?.end();\n throw error;\n });\n } catch (error) {\n span?.setStatus({ code: SPAN_STATUS_ERROR, message: String(error) });\n span?.end();\n throw error;\n }\n }) as T['prefetch'];\n}\n\nfunction wrapLoadAsync<T extends ExpoImage>(imageClass: T): void {\n if (!imageClass.loadAsync) {\n return;\n }\n\n const originalLoadAsync = imageClass.loadAsync.bind(imageClass);\n\n imageClass.loadAsync = ((\n source: ExpoImageSource | string | number,\n options?: ExpoImageLoadOptions,\n ): Promise<ExpoImageRef> => {\n const description = describeSource(source);\n\n const imageUrl =\n typeof source === 'string' ? source : typeof source === 'object' && source.uri ? source.uri : undefined;\n\n return traceAsyncOperation(\n {\n op: 'resource.image.load',\n name: `Image load ${description}`,\n attributes: {\n 'sentry.origin': SPAN_ORIGIN_AUTO_RESOURCE_EXPO_IMAGE,\n ...(imageUrl ? { 'image.url': sanitizeUrl(imageUrl) } : undefined),\n },\n },\n () => originalLoadAsync(source, options),\n );\n }) as T['loadAsync'];\n}\n\nfunction describeSource(source: ExpoImageSource | string | number): string {\n if (typeof source === 'number') {\n return `asset #${source}`;\n }\n if (typeof source === 'string') {\n return describeUrl(source);\n }\n if (source.uri) {\n return describeUrl(source.uri);\n }\n return 'unknown source';\n}\n"]}
|
|
@@ -4,6 +4,10 @@ export { reactNavigationIntegration } from './reactnavigation';
|
|
|
4
4
|
export { reactNativeNavigationIntegration } from './reactnativenavigation';
|
|
5
5
|
export { wrapExpoRouter } from './expoRouter';
|
|
6
6
|
export type { ExpoRouter } from './expoRouter';
|
|
7
|
+
export { wrapExpoImage } from './expoImage';
|
|
8
|
+
export type { ExpoImage } from './expoImage';
|
|
9
|
+
export { wrapExpoAsset } from './expoAsset';
|
|
10
|
+
export type { ExpoAsset } from './expoAsset';
|
|
7
11
|
export { startIdleNavigationSpan, startIdleSpan, getDefaultIdleNavigationSpanOptions } from './span';
|
|
8
12
|
export type { ReactNavigationCurrentRoute, ReactNavigationRoute } from './types';
|
|
9
13
|
export { ReactNativeProfiler } from './reactnativeprofiler';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,IAAI,qCAAqC,EACzD,uCAAuC,EACvC,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,mCAAmC,EAAE,MAAM,QAAQ,CAAC;AAErG,YAAY,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,cAAc,OAAO,CAAC;AAEtB,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,IAAI,qCAAqC,EACzD,uCAAuC,EACvC,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,mCAAmC,EAAE,MAAM,QAAQ,CAAC;AAErG,YAAY,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,cAAc,OAAO,CAAC;AAEtB,cAAc,iBAAiB,CAAC"}
|
package/dist/js/tracing/index.js
CHANGED
|
@@ -2,6 +2,8 @@ export { reactNativeTracingIntegration, INTEGRATION_NAME as REACT_NATIVE_TRACING
|
|
|
2
2
|
export { reactNavigationIntegration } from './reactnavigation';
|
|
3
3
|
export { reactNativeNavigationIntegration } from './reactnativenavigation';
|
|
4
4
|
export { wrapExpoRouter } from './expoRouter';
|
|
5
|
+
export { wrapExpoImage } from './expoImage';
|
|
6
|
+
export { wrapExpoAsset } from './expoAsset';
|
|
5
7
|
export { startIdleNavigationSpan, startIdleSpan, getDefaultIdleNavigationSpanOptions } from './span';
|
|
6
8
|
export { ReactNativeProfiler } from './reactnativeprofiler';
|
|
7
9
|
export { sentryTraceGesture } from './gesturetracing';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/js/tracing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,IAAI,qCAAqC,EACzD,uCAAuC,EACvC,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,mCAAmC,EAAE,MAAM,QAAQ,CAAC;AAIrG,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,cAAc,OAAO,CAAC;AAEtB,cAAc,iBAAiB,CAAC","sourcesContent":["export {\n reactNativeTracingIntegration,\n INTEGRATION_NAME as REACT_NATIVE_TRACING_INTEGRATION_NAME,\n getCurrentReactNativeTracingIntegration,\n getReactNativeTracingIntegration,\n} from './reactnativetracing';\nexport type { ReactNativeTracingIntegration } from './reactnativetracing';\n\nexport { reactNavigationIntegration } from './reactnavigation';\nexport { reactNativeNavigationIntegration } from './reactnativenavigation';\n\nexport { wrapExpoRouter } from './expoRouter';\nexport type { ExpoRouter } from './expoRouter';\n\nexport { startIdleNavigationSpan, startIdleSpan, getDefaultIdleNavigationSpanOptions } from './span';\n\nexport type { ReactNavigationCurrentRoute, ReactNavigationRoute } from './types';\n\nexport { ReactNativeProfiler } from './reactnativeprofiler';\n\nexport { sentryTraceGesture } from './gesturetracing';\n\nexport * from './ops';\n\nexport * from './timetodisplay';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/js/tracing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,IAAI,qCAAqC,EACzD,uCAAuC,EACvC,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,mCAAmC,EAAE,MAAM,QAAQ,CAAC;AAIrG,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,cAAc,OAAO,CAAC;AAEtB,cAAc,iBAAiB,CAAC","sourcesContent":["export {\n reactNativeTracingIntegration,\n INTEGRATION_NAME as REACT_NATIVE_TRACING_INTEGRATION_NAME,\n getCurrentReactNativeTracingIntegration,\n getReactNativeTracingIntegration,\n} from './reactnativetracing';\nexport type { ReactNativeTracingIntegration } from './reactnativetracing';\n\nexport { reactNavigationIntegration } from './reactnavigation';\nexport { reactNativeNavigationIntegration } from './reactnativenavigation';\n\nexport { wrapExpoRouter } from './expoRouter';\nexport type { ExpoRouter } from './expoRouter';\n\nexport { wrapExpoImage } from './expoImage';\nexport type { ExpoImage } from './expoImage';\n\nexport { wrapExpoAsset } from './expoAsset';\nexport type { ExpoAsset } from './expoAsset';\n\nexport { startIdleNavigationSpan, startIdleSpan, getDefaultIdleNavigationSpanOptions } from './span';\n\nexport type { ReactNavigationCurrentRoute, ReactNavigationRoute } from './types';\n\nexport { ReactNativeProfiler } from './reactnativeprofiler';\n\nexport { sentryTraceGesture } from './gesturetracing';\n\nexport * from './ops';\n\nexport * from './timetodisplay';\n"]}
|
|
@@ -37,6 +37,14 @@ export declare function _setRootComponentCreationTimestampMs(timestampMs: number
|
|
|
37
37
|
* @private
|
|
38
38
|
*/
|
|
39
39
|
export declare const _setAppStartEndData: (data: AppStartEndData) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Updates only the endFrames on existing appStartEndData.
|
|
42
|
+
* Used after the async fetchNativeFrames completes to attach frame data
|
|
43
|
+
* without triggering the overwrite warning from _setAppStartEndData.
|
|
44
|
+
*
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
export declare const _updateAppStartEndFrames: (endFrames: NativeFramesResponse | null) => void;
|
|
40
48
|
/**
|
|
41
49
|
* For testing purposes only.
|
|
42
50
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appStart.d.ts","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/appStart.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,WAAW,EAAoC,MAAM,cAAc,CAAC;AAiBjG,OAAO,KAAK,EAA0B,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAgBzF,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG;IAC9C,yBAAyB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD,CAAC;AAeF,UAAU,eAAe;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACxC;AAQD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"appStart.d.ts","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/appStart.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,WAAW,EAAoC,MAAM,cAAc,CAAC;AAiBjG,OAAO,KAAK,EAA0B,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAgBzF,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG;IAC9C,yBAAyB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD,CAAC;AAeF,UAAU,eAAe;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACxC;AAQD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BzF;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAK7E;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAG9E;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,SAAU,eAAe,KAAG,IAG3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,cAAe,oBAAoB,GAAG,IAAI,KAAG,IAIjF,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sCAAsC,IAAI,IAAI,CAE7D;AAyBD;;GAEG;AACH,eAAO,MAAM,mBAAmB;IAG9B;;;;;OAKG;;MAEI,mBAmVR,CAAC"}
|
|
@@ -54,20 +54,22 @@ export function _captureAppStart({ isManual }) {
|
|
|
54
54
|
}
|
|
55
55
|
isRecordedAppStartEndTimestampMsManual = isManual;
|
|
56
56
|
const timestampMs = timestampInSeconds() * 1000;
|
|
57
|
-
|
|
57
|
+
// Set end timestamp immediately to avoid race with processEvent
|
|
58
|
+
// Frames data will be updated after the async fetch
|
|
59
|
+
_setAppStartEndData({
|
|
60
|
+
timestampMs,
|
|
61
|
+
endFrames: null,
|
|
62
|
+
});
|
|
58
63
|
if (NATIVE.enableNative) {
|
|
59
64
|
try {
|
|
60
|
-
endFrames = yield NATIVE.fetchNativeFrames();
|
|
65
|
+
const endFrames = yield NATIVE.fetchNativeFrames();
|
|
61
66
|
debug.log('[AppStart] Captured end frames for app start.', endFrames);
|
|
67
|
+
_updateAppStartEndFrames(endFrames);
|
|
62
68
|
}
|
|
63
69
|
catch (error) {
|
|
64
70
|
debug.log('[AppStart] Failed to capture end frames for app start.', error);
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
|
-
_setAppStartEndData({
|
|
68
|
-
timestampMs,
|
|
69
|
-
endFrames,
|
|
70
|
-
});
|
|
71
73
|
yield ((_a = client.getIntegrationByName(INTEGRATION_NAME)) === null || _a === void 0 ? void 0 : _a.captureStandaloneAppStart());
|
|
72
74
|
});
|
|
73
75
|
}
|
|
@@ -99,6 +101,18 @@ export const _setAppStartEndData = (data) => {
|
|
|
99
101
|
appStartEndData && debug.warn('Overwriting already set app start end data.');
|
|
100
102
|
appStartEndData = data;
|
|
101
103
|
};
|
|
104
|
+
/**
|
|
105
|
+
* Updates only the endFrames on existing appStartEndData.
|
|
106
|
+
* Used after the async fetchNativeFrames completes to attach frame data
|
|
107
|
+
* without triggering the overwrite warning from _setAppStartEndData.
|
|
108
|
+
*
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
export const _updateAppStartEndFrames = (endFrames) => {
|
|
112
|
+
if (appStartEndData) {
|
|
113
|
+
appStartEndData.endFrames = endFrames;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
102
116
|
/**
|
|
103
117
|
* For testing purposes only.
|
|
104
118
|
*
|
|
@@ -137,6 +151,7 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
137
151
|
let appStartDataFlushed = false;
|
|
138
152
|
let afterAllSetupCalled = false;
|
|
139
153
|
let firstStartedActiveRootSpanId = undefined;
|
|
154
|
+
let firstStartedActiveRootSpan = undefined;
|
|
140
155
|
const setup = (client) => {
|
|
141
156
|
_client = client;
|
|
142
157
|
const { enableAppStartTracking } = client.getOptions();
|
|
@@ -158,6 +173,7 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
158
173
|
debug.log('[AppStartIntegration] Resetting app start data flushed flag based on runApplication call.');
|
|
159
174
|
appStartDataFlushed = false;
|
|
160
175
|
firstStartedActiveRootSpanId = undefined;
|
|
176
|
+
firstStartedActiveRootSpan = undefined;
|
|
161
177
|
}
|
|
162
178
|
else {
|
|
163
179
|
debug.log('[AppStartIntegration] Waiting for initial app start was flush, before updating based on runApplication call.');
|
|
@@ -177,7 +193,20 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
177
193
|
});
|
|
178
194
|
const recordFirstStartedActiveRootSpanId = (rootSpan) => {
|
|
179
195
|
if (firstStartedActiveRootSpanId) {
|
|
180
|
-
|
|
196
|
+
// Check if the previously locked span was dropped after it ended (e.g., by
|
|
197
|
+
// ignoreEmptyRouteChangeTransactions or ignoreEmptyBackNavigation setting
|
|
198
|
+
// _sampled = false during spanEnd). If so, reset and allow this new span.
|
|
199
|
+
// We check here (at the next spanStart) rather than at spanEnd because
|
|
200
|
+
// the discard listeners run after the app start listener in registration order,
|
|
201
|
+
// so _sampled is not yet false when our own spanEnd listener would fire.
|
|
202
|
+
if (firstStartedActiveRootSpan && !spanIsSampled(firstStartedActiveRootSpan)) {
|
|
203
|
+
debug.log('[AppStart] Previously locked root span was unsampled after ending. Resetting to allow next transaction.');
|
|
204
|
+
resetFirstStartedActiveRootSpanId();
|
|
205
|
+
// Fall through to lock to this new span
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
181
210
|
}
|
|
182
211
|
if (!isRootSpan(rootSpan)) {
|
|
183
212
|
return;
|
|
@@ -185,8 +214,18 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
185
214
|
if (!spanIsSampled(rootSpan)) {
|
|
186
215
|
return;
|
|
187
216
|
}
|
|
217
|
+
firstStartedActiveRootSpan = rootSpan;
|
|
188
218
|
setFirstStartedActiveRootSpanId(rootSpan.spanContext().spanId);
|
|
189
219
|
};
|
|
220
|
+
/**
|
|
221
|
+
* Resets the first started active root span id and span reference to allow
|
|
222
|
+
* the next root span's transaction to attempt app start attachment.
|
|
223
|
+
*/
|
|
224
|
+
const resetFirstStartedActiveRootSpanId = () => {
|
|
225
|
+
debug.log('[AppStart] Resetting first started active root span id to allow retry on next transaction.');
|
|
226
|
+
firstStartedActiveRootSpanId = undefined;
|
|
227
|
+
firstStartedActiveRootSpan = undefined;
|
|
228
|
+
};
|
|
190
229
|
/**
|
|
191
230
|
* For testing purposes only.
|
|
192
231
|
* @private
|
|
@@ -252,6 +291,7 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
252
291
|
return __awaiter(this, void 0, void 0, function* () {
|
|
253
292
|
if (appStartDataFlushed) {
|
|
254
293
|
// App start data is only relevant for the first transaction of the app run
|
|
294
|
+
debug.log('[AppStart] App start data already flushed. Skipping.');
|
|
255
295
|
return;
|
|
256
296
|
}
|
|
257
297
|
if (!((_a = event.contexts) === null || _a === void 0 ? void 0 : _a.trace)) {
|
|
@@ -271,34 +311,42 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
271
311
|
return;
|
|
272
312
|
}
|
|
273
313
|
}
|
|
314
|
+
// All failure paths below set appStartDataFlushed = true to prevent
|
|
315
|
+
// wasteful retries — these conditions won't change within the same app start.
|
|
274
316
|
const appStart = yield NATIVE.fetchNativeAppStart();
|
|
275
317
|
if (!appStart) {
|
|
276
318
|
debug.warn('[AppStart] Failed to retrieve the app start metrics from the native layer.');
|
|
319
|
+
appStartDataFlushed = true;
|
|
277
320
|
return;
|
|
278
321
|
}
|
|
279
322
|
if (appStart.has_fetched) {
|
|
280
323
|
debug.warn('[AppStart] Measured app start metrics were already reported from the native layer.');
|
|
324
|
+
appStartDataFlushed = true;
|
|
281
325
|
return;
|
|
282
326
|
}
|
|
283
327
|
const appStartTimestampMs = appStart.app_start_timestamp_ms;
|
|
284
328
|
if (!appStartTimestampMs) {
|
|
285
329
|
debug.warn('[AppStart] App start timestamp could not be loaded from the native layer.');
|
|
330
|
+
appStartDataFlushed = true;
|
|
286
331
|
return;
|
|
287
332
|
}
|
|
288
333
|
const appStartEndTimestampMs = (appStartEndData === null || appStartEndData === void 0 ? void 0 : appStartEndData.timestampMs) || getBundleStartTimestampMs();
|
|
289
334
|
if (!appStartEndTimestampMs) {
|
|
290
335
|
debug.warn('[AppStart] Javascript failed to record app start end. `_setAppStartEndData` was not called nor could the bundle start be found.');
|
|
336
|
+
appStartDataFlushed = true;
|
|
291
337
|
return;
|
|
292
338
|
}
|
|
293
339
|
const isAppStartWithinBounds = !!event.start_timestamp && appStartTimestampMs >= event.start_timestamp * 1000 - MAX_APP_START_AGE_MS;
|
|
294
340
|
if (!__DEV__ && !isAppStartWithinBounds) {
|
|
295
341
|
debug.warn('[AppStart] App start timestamp is too far in the past to be used for app start span.');
|
|
342
|
+
appStartDataFlushed = true;
|
|
296
343
|
return;
|
|
297
344
|
}
|
|
298
345
|
const appStartDurationMs = appStartEndTimestampMs - appStartTimestampMs;
|
|
299
346
|
if (!__DEV__ && appStartDurationMs >= MAX_APP_START_DURATION_MS) {
|
|
300
347
|
// Dev builds can have long app start waiting over minute for the first bundle to be produced
|
|
301
348
|
debug.warn('[AppStart] App start duration is over a minute long, not adding app start span.');
|
|
349
|
+
appStartDataFlushed = true;
|
|
302
350
|
return;
|
|
303
351
|
}
|
|
304
352
|
if (appStartDurationMs < 0) {
|
|
@@ -306,6 +354,7 @@ export const appStartIntegration = ({ standalone = false, } = {}) => {
|
|
|
306
354
|
// and the app start end timestamp is not updated, for example
|
|
307
355
|
// due to missing `Sentry.wrap(RootComponent)` call.
|
|
308
356
|
debug.warn('[AppStart] Last recorded app start end timestamp is before the app start timestamp.', 'This is usually caused by missing `Sentry.wrap(RootComponent)` call.');
|
|
357
|
+
appStartDataFlushed = true;
|
|
309
358
|
return;
|
|
310
359
|
}
|
|
311
360
|
appStartDataFlushed = true;
|