appRouterRoutingInstrumentation.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const react = require('@sentry/react');
  3. const utils = require('@sentry/utils');
  4. const DEFAULT_TAGS = {
  5. 'routing.instrumentation': 'next-app-router',
  6. } ;
  7. /**
  8. * Instruments the Next.js Client App Router.
  9. */
  10. // TODO(v8): Clean this function up by splitting into pageload and navigation instrumentation respectively. Also remove startTransactionCb in the process.
  11. function appRouterInstrumentation(
  12. startTransactionCb,
  13. startTransactionOnPageLoad = true,
  14. startTransactionOnLocationChange = true,
  15. startPageloadSpanCallback,
  16. startNavigationSpanCallback,
  17. ) {
  18. // We keep track of the active transaction so we can finish it when we start a navigation transaction.
  19. let activeTransaction = undefined;
  20. // We keep track of the previous location name so we can set the `from` field on navigation transactions.
  21. // This is either a route or a pathname.
  22. let prevLocationName = react.WINDOW.location.pathname;
  23. if (startTransactionOnPageLoad) {
  24. const transactionContext = {
  25. name: prevLocationName,
  26. op: 'pageload',
  27. origin: 'auto.pageload.nextjs.app_router_instrumentation',
  28. tags: DEFAULT_TAGS,
  29. // pageload should always start at timeOrigin (and needs to be in s, not ms)
  30. startTimestamp: utils.browserPerformanceTimeOrigin ? utils.browserPerformanceTimeOrigin / 1000 : undefined,
  31. metadata: { source: 'url' },
  32. } ;
  33. activeTransaction = startTransactionCb(transactionContext);
  34. startPageloadSpanCallback(transactionContext);
  35. }
  36. if (startTransactionOnLocationChange) {
  37. utils.addFetchInstrumentationHandler(handlerData => {
  38. // The instrumentation handler is invoked twice - once for starting a request and once when the req finishes
  39. // We can use the existence of the end-timestamp to filter out "finishing"-events.
  40. if (handlerData.endTimestamp !== undefined) {
  41. return;
  42. }
  43. // Only GET requests can be navigating RSC requests
  44. if (handlerData.fetchData.method !== 'GET') {
  45. return;
  46. }
  47. const parsedNavigatingRscFetchArgs = parseNavigatingRscFetchArgs(handlerData.args);
  48. if (parsedNavigatingRscFetchArgs === null) {
  49. return;
  50. }
  51. const transactionName = parsedNavigatingRscFetchArgs.targetPathname;
  52. const tags = {
  53. ...DEFAULT_TAGS,
  54. from: prevLocationName,
  55. };
  56. prevLocationName = transactionName;
  57. if (activeTransaction) {
  58. activeTransaction.end();
  59. }
  60. const transactionContext = {
  61. name: transactionName,
  62. op: 'navigation',
  63. origin: 'auto.navigation.nextjs.app_router_instrumentation',
  64. tags,
  65. metadata: { source: 'url' },
  66. } ;
  67. startTransactionCb(transactionContext);
  68. startNavigationSpanCallback(transactionContext);
  69. });
  70. }
  71. }
  72. function parseNavigatingRscFetchArgs(fetchArgs)
  73. {
  74. // Make sure the first arg is a URL object
  75. if (!fetchArgs[0] || typeof fetchArgs[0] !== 'object' || (fetchArgs[0] ).searchParams === undefined) {
  76. return null;
  77. }
  78. // Make sure the second argument is some kind of fetch config obj that contains headers
  79. if (!fetchArgs[1] || typeof fetchArgs[1] !== 'object' || !('headers' in fetchArgs[1])) {
  80. return null;
  81. }
  82. try {
  83. const url = fetchArgs[0] ;
  84. const headers = fetchArgs[1].headers ;
  85. // Not an RSC request
  86. if (headers['RSC'] !== '1') {
  87. return null;
  88. }
  89. // Prefetch requests are not navigating RSC requests
  90. if (headers['Next-Router-Prefetch'] === '1') {
  91. return null;
  92. }
  93. return {
  94. targetPathname: url.pathname,
  95. };
  96. } catch (e) {
  97. return null;
  98. }
  99. }
  100. exports.appRouterInstrumentation = appRouterInstrumentation;
  101. //# sourceMappingURL=appRouterRoutingInstrumentation.js.map