{"version":3,"file":"router.js","sources":["../../src/router.ts"],"sourcesContent":["import { captureException } from '@sentry/browser';\nimport type { Span, SpanAttributes, StartSpanOptions, TransactionSource } from '@sentry/core';\nimport {\n getActiveSpan,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n spanToJSON,\n} from '@sentry/core';\n\n// The following type is an intersection of the Route type from VueRouter v2, v3, and v4.\n// This is not great, but kinda necessary to make it work with all versions at the same time.\nexport type Route = {\n /** Unparameterized URL */\n path: string;\n /**\n * Query params (keys map to null when there is no value associated, e.g. \"?foo\" and to an array when there are\n * multiple query params that have the same key, e.g. \"?foo&foo=bar\")\n */\n query: Record;\n /** Route name (VueRouter provides a way to give routes individual names) */\n name?: string | symbol | null | undefined;\n /** Evaluated parameters */\n params: Record;\n /** All the matched route objects as defined in VueRouter constructor */\n matched: { path: string }[];\n};\n\ninterface VueRouter {\n onError: (fn: (err: Error) => void) => void;\n beforeEach: (fn: (to: Route, from: Route, next?: () => void) => void) => void;\n // Vue Router 3 exposes a `mode` property ('hash' | 'history' | 'abstract').\n // Vue Router 4+ replaced it with `options.history`. Used for version detection.\n mode?: string;\n}\n\n/**\n * Instrument the Vue router to create navigation spans.\n */\nexport function instrumentVueRouter(\n router: VueRouter,\n options: {\n /**\n * What to use for route labels.\n * By default, we use route.name (if set) and else the path.\n *\n * Default: 'name'\n */\n routeLabel: 'name' | 'path';\n instrumentPageLoad: boolean;\n instrumentNavigation: boolean;\n },\n startNavigationSpanFn: (context: StartSpanOptions) => void,\n): void {\n let hasHandledFirstPageLoad = false;\n\n // Detect Vue Router 3 by checking for the `mode` property which only exists in VR3.\n // Vue Router 4+ uses `options.history` instead and does not expose `mode`.\n const isLegacyRouter = 'mode' in router;\n\n router.onError(error => captureException(error, { mechanism: { handled: false } }));\n\n // Use rest params to capture `next` without declaring it as a named parameter.\n // This keeps Function.length === 2, which tells Vue Router 4+/5+ to use the\n // modern return-based resolution (no deprecation warning in Vue Router 5.0.3+).\n router.beforeEach((to: Route, _from: Route, ...rest: [(() => void)?]) => {\n // We avoid trying to re-fetch the page load span when we know we already handled it the first time\n const activePageLoadSpan = !hasHandledFirstPageLoad ? getActivePageLoadSpan() : undefined;\n\n const attributes: SpanAttributes = {};\n\n for (const key of Object.keys(to.params)) {\n attributes[`url.path.parameter.${key}`] = to.params[key];\n attributes[`params.${key}`] = to.params[key]; // params.[key] is an alias\n }\n for (const key of Object.keys(to.query)) {\n const value = to.query[key];\n if (value) {\n attributes[`query.${key}`] = value;\n }\n }\n\n // Determine a name for the routing transaction and where that name came from\n let spanName: string = to.path;\n let transactionSource: TransactionSource = 'url';\n if (to.name && options.routeLabel !== 'path') {\n spanName = to.name.toString();\n transactionSource = 'custom';\n } else if (to.matched.length > 0) {\n const lastIndex = to.matched.length - 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n spanName = to.matched[lastIndex]!.path;\n transactionSource = 'route';\n }\n\n getCurrentScope().setTransactionName(spanName);\n\n // Update the existing page load span with parametrized route information\n if (options.instrumentPageLoad && activePageLoadSpan) {\n const existingAttributes = spanToJSON(activePageLoadSpan).data;\n if (existingAttributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] !== 'custom') {\n activePageLoadSpan.updateName(spanName);\n activePageLoadSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, transactionSource);\n }\n\n // Set router attributes on the existing pageload transaction\n // This will override the origin, and add params & query attributes\n activePageLoadSpan.setAttributes({\n ...attributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.vue',\n });\n\n hasHandledFirstPageLoad = true;\n }\n\n if (options.instrumentNavigation && !activePageLoadSpan) {\n startNavigationSpanFn({\n name: spanName,\n op: 'navigation',\n attributes: {\n ...attributes,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.vue',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: transactionSource,\n },\n });\n }\n\n // Vue Router 3 requires `next()` to be called to resolve the navigation guard.\n // Vue Router 4+ auto-resolves guards with Function.length < 3 via `guardToPromiseFn`.\n // In Vue Router 5.0.3+, the `next` callback passed to guards is wrapped with\n // `withDeprecationWarning()`, so calling it emits a console warning. We avoid\n // calling it on modern routers where it is both unnecessary and noisy.\n if (isLegacyRouter) {\n const next = rest[0];\n if (typeof next === 'function') {\n next();\n }\n }\n });\n}\n\nfunction getActivePageLoadSpan(): Span | undefined {\n const span = getActiveSpan();\n const rootSpan = span && getRootSpan(span);\n\n if (!rootSpan) {\n return undefined;\n }\n\n const op = spanToJSON(rootSpan).op;\n\n return op === 'pageload' ? rootSpan : undefined;\n}\n"],"names":[],"mappings":";;;AAwCO,SAAS,mBAAA,CACd,MAAA,EACA,OAAA,EAWA,qBAAA,EACM;AACN,EAAA,IAAI,uBAAA,GAA0B,KAAA;AAI9B,EAAA,MAAM,iBAAiB,MAAA,IAAU,MAAA;AAEjC,EAAA,MAAA,CAAO,OAAA,CAAQ,CAAA,KAAA,KAAS,gBAAA,CAAiB,KAAA,EAAO,EAAE,SAAA,EAAW,EAAE,OAAA,EAAS,KAAA,EAAM,EAAG,CAAC,CAAA;AAKlF,EAAA,MAAA,CAAO,UAAA,CAAW,CAAC,EAAA,EAAW,KAAA,EAAA,GAAiB,IAAA,KAA0B;AAEvE,IAAA,MAAM,kBAAA,GAAqB,CAAC,uBAAA,GAA0B,qBAAA,EAAsB,GAAI,MAAA;AAEhF,IAAA,MAAM,aAA6B,EAAC;AAEpC,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,EAAA,CAAG,MAAM,CAAA,EAAG;AACxC,MAAA,UAAA,CAAW,sBAAsB,GAAG,CAAA,CAAE,CAAA,GAAI,EAAA,CAAG,OAAO,GAAG,CAAA;AACvD,MAAA,UAAA,CAAW,UAAU,GAAG,CAAA,CAAE,CAAA,GAAI,EAAA,CAAG,OAAO,GAAG,CAAA;AAAA,IAC7C;AACA,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,EAAA,CAAG,KAAK,CAAA,EAAG;AACvC,MAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,KAAA,CAAM,GAAG,CAAA;AAC1B,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,UAAA,CAAW,CAAA,MAAA,EAAS,GAAG,CAAA,CAAE,CAAA,GAAI,KAAA;AAAA,MAC/B;AAAA,IACF;AAGA,IAAA,IAAI,WAAmB,EAAA,CAAG,IAAA;AAC1B,IAAA,IAAI,iBAAA,GAAuC,KAAA;AAC3C,IAAA,IAAI,EAAA,CAAG,IAAA,IAAQ,OAAA,CAAQ,UAAA,KAAe,MAAA,EAAQ;AAC5C,MAAA,QAAA,GAAW,EAAA,CAAG,KAAK,QAAA,EAAS;AAC5B,MAAA,iBAAA,GAAoB,QAAA;AAAA,IACtB,CAAA,MAAA,IAAW,EAAA,CAAG,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AAChC,MAAA,MAAM,SAAA,GAAY,EAAA,CAAG,OAAA,CAAQ,MAAA,GAAS,CAAA;AAEtC,MAAA,QAAA,GAAW,EAAA,CAAG,OAAA,CAAQ,SAAS,CAAA,CAAG,IAAA;AAClC,MAAA,iBAAA,GAAoB,OAAA;AAAA,IACtB;AAEA,IAAA,eAAA,EAAgB,CAAE,mBAAmB,QAAQ,CAAA;AAG7C,IAAA,IAAI,OAAA,CAAQ,sBAAsB,kBAAA,EAAoB;AACpD,MAAA,MAAM,kBAAA,GAAqB,UAAA,CAAW,kBAAkB,CAAA,CAAE,IAAA;AAC1D,MAAA,IAAI,kBAAA,CAAmB,gCAAgC,CAAA,KAAM,QAAA,EAAU;AACrE,QAAA,kBAAA,CAAmB,WAAW,QAAQ,CAAA;AACtC,QAAA,kBAAA,CAAmB,YAAA,CAAa,kCAAkC,iBAAiB,CAAA;AAAA,MACrF;AAIA,MAAA,kBAAA,CAAmB,aAAA,CAAc;AAAA,QAC/B,GAAG,UAAA;AAAA,QACH,CAAC,gCAAgC,GAAG;AAAA,OACrC,CAAA;AAED,MAAA,uBAAA,GAA0B,IAAA;AAAA,IAC5B;AAEA,IAAA,IAAI,OAAA,CAAQ,oBAAA,IAAwB,CAAC,kBAAA,EAAoB;AACvD,MAAA,qBAAA,CAAsB;AAAA,QACpB,IAAA,EAAM,QAAA;AAAA,QACN,EAAA,EAAI,YAAA;AAAA,QACJ,UAAA,EAAY;AAAA,UACV,GAAG,UAAA;AAAA,UACH,CAAC,gCAAgC,GAAG,qBAAA;AAAA,UACpC,CAAC,gCAAgC,GAAG;AAAA;AACtC,OACD,CAAA;AAAA,IACH;AAOA,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,MAAM,IAAA,GAAO,KAAK,CAAC,CAAA;AACnB,MAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAC9B,QAAA,IAAA,EAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,qBAAA,GAA0C;AACjD,EAAA,MAAM,OAAO,aAAA,EAAc;AAC3B,EAAA,MAAM,QAAA,GAAW,IAAA,IAAQ,WAAA,CAAY,IAAI,CAAA;AAEzC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAA,GAAK,UAAA,CAAW,QAAQ,CAAA,CAAE,EAAA;AAEhC,EAAA,OAAO,EAAA,KAAO,aAAa,QAAA,GAAW,MAAA;AACxC;;;;"}