{"version":3,"file":"NavigationInstrumentation.cjs","names":["EmbraceInstrumentationBase","page","KEY_EMB_TYPE","KEY_EMB_PAGE_PATH","KEY_EMB_PAGE_ID"],"sources":["../../../../src/instrumentations/navigation/NavigationInstrumentation/NavigationInstrumentation.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport type { PageManager, Route } from '../../../api-page/index.ts';\nimport { page } from '../../../api-page/index.ts';\nimport type { SessionPartStartedEvent } from '../../../api-sessions/index.ts';\nimport {\n  EMB_TYPES,\n  KEY_EMB_PAGE_ID,\n  KEY_EMB_PAGE_PATH,\n  KEY_EMB_TYPE,\n} from '../../../constants/index.ts';\nimport { EmbraceInstrumentationBase } from '../../EmbraceInstrumentationBase/index.ts';\nimport type { NavigationInstrumentationArgs } from './types.ts';\n\n// Regular expression to match path options in the format \"(option)\"\n// Used to clean up paths that are like \"/order/:orderState(pending|shipped|delivered)/type:(sale|normal)\" to \"/order/:orderState/:type\"\n// Could be simplified but done this way to prevent: https://javascript.info/regexp-catastrophic-backtracking\nconst PATH_OPTIONS_RE = /\\([^()]+\\)/g;\n\nexport class NavigationInstrumentation extends EmbraceInstrumentationBase {\n  private readonly _shouldCleanupPathOptionsFromRouteName: boolean = true;\n  private readonly _pageManager: PageManager;\n  private _currentRouteSpan: Span | null = null;\n  private _currentRouteSpanUrl: string | null = null;\n  private _currentRouteSpanPath: string | null = null;\n\n  public constructor({\n    diag,\n    shouldCleanupPathOptionsFromRouteName = true,\n    pageManager,\n  }: NavigationInstrumentationArgs) {\n    super({\n      instrumentationName: 'NavigationInstrumentation',\n      instrumentationVersion: '1.0.0',\n      diag,\n      config: {},\n    });\n\n    this._shouldCleanupPathOptionsFromRouteName =\n      shouldCleanupPathOptionsFromRouteName;\n    this._pageManager = pageManager ?? page.getPageManager();\n    this._pageManager.addRouteChangedListener(this._onRouteChanged);\n\n    if (this._config.enabled) {\n      this.enable();\n    }\n\n    // Starts the inital route span if the page manager already has a current route\n    const currentRoute = this._pageManager.getCurrentRoute();\n    if (currentRoute) {\n      this._onRouteChanged(currentRoute);\n    }\n  }\n\n  private readonly _onRouteChanged = (route: Route): void => {\n    if (!this._config.enabled) {\n      return;\n    }\n\n    if (this._currentRouteSpan && this._currentRouteSpanUrl === route.url) {\n      // Same url as the currently open span: either the templated path just\n      // resolved (rename in place), or a redundant re-render — no-op if the\n      // path hasn't actually changed.\n      if (this._currentRouteSpanPath !== route.path) {\n        this._renameCurrentRouteSpan(route.path);\n      }\n      return;\n    }\n\n    this._endRouteSpan();\n    this._startRouteSpan(route);\n  };\n\n  private readonly _startRouteSpan = (route: Route): Span => {\n    this._diag.debug(`Starting route span for url: ${route.url}`);\n\n    const pathName = this._cleanPath(route.path);\n    this._currentRouteSpan = this.tracer.startSpan(pathName, {\n      startTime: this.perf.getNowMillis(),\n      attributes: {\n        [KEY_EMB_TYPE]: EMB_TYPES.Surface,\n        [KEY_EMB_PAGE_PATH]: pathName,\n        [KEY_EMB_PAGE_ID]: this._pageManager.getCurrentPageId() || undefined,\n      },\n    });\n    this._currentRouteSpanUrl = route.url;\n    this._currentRouteSpanPath = route.path;\n\n    return this._currentRouteSpan;\n  };\n\n  private readonly _endRouteSpan = (): void => {\n    if (this._currentRouteSpan) {\n      this._diag.debug(\n        `Ending route span for url: ${this._currentRouteSpanUrl ?? 'unknown'}`,\n      );\n\n      this._currentRouteSpan.end(this.perf.getNowMillis());\n      this._currentRouteSpan = null;\n      this._currentRouteSpanUrl = null;\n      this._currentRouteSpanPath = null;\n    }\n  };\n\n  private readonly _renameCurrentRouteSpan = (path: string): void => {\n    const pathName = this._cleanPath(path);\n    this._diag.debug(`Resolved route span path to: ${pathName}`);\n\n    this._currentRouteSpan?.updateName(pathName);\n    this._currentRouteSpan?.setAttribute(KEY_EMB_PAGE_PATH, pathName);\n    this._currentRouteSpanPath = path;\n  };\n\n  private readonly _cleanPath = (path: string): string =>\n    this._shouldCleanupPathOptionsFromRouteName\n      ? path.replace(PATH_OPTIONS_RE, '')\n      : path;\n\n  // A route span must not outlive the session part it started in — e.g. the\n  // tab backgrounds, or the session ends, with no further navigation to\n  // trigger _onRouteChanged.\n  private readonly _onSessionPartEnded = (): void => {\n    if (this._currentRouteSpan) {\n      this._diag.debug('Session ended, ending route span.');\n      this._endRouteSpan();\n    }\n  };\n\n  // Only needed when a session part starts with no route change (e.g.\n  // resuming after background/inactivity). Skipped for soft-nav rollovers,\n  // since the real new route is reported right after (see\n  // EmbracePageManager._onSoftNavigation).\n  private readonly _onSessionPartStarted = ({\n    reason,\n  }: SessionPartStartedEvent): void => {\n    if (reason === 'web_soft_navigation') {\n      return;\n    }\n\n    const currentRoute = this._pageManager.getCurrentRoute();\n    if (currentRoute) {\n      this._onRouteChanged(currentRoute);\n    }\n  };\n\n  public override onEnable = () => {\n    this.setConfig({\n      enabled: true,\n    });\n    this.setSessionPartListeners({\n      start: this._onSessionPartStarted,\n      end: this._onSessionPartEnded,\n    });\n    this._diag.debug(\n      'NavigationInstrumentation enabled, listening for navigation events.',\n    );\n  };\n\n  public override onDisable = () => {\n    this._endRouteSpan();\n    this.setConfig({\n      enabled: false,\n    });\n    this._diag.debug(\n      'NavigationInstrumentation disabled, stopped listening for navigation events.',\n    );\n  };\n}\n"],"mappings":";;;;;AAgBA,MAAM,kBAAkB;AAExB,IAAa,4BAAb,cAA+CA,+EAAAA,2BAA2B;CACxE,yCAAmE;CACnE;CACA,oBAAyC;CACzC,uBAA8C;CAC9C,wBAA+C;CAE/C,YAAmB,EACjB,MACA,wCAAwC,MACxC,eACgC;EAChC,MAAM;GACJ,qBAAqB;GACrB,wBAAwB;GACxB;GACA,QAAQ,CAAC;EACX,CAAC;EAED,KAAK,yCACH;EACF,KAAK,eAAe,eAAeC,yBAAAA,KAAK,eAAe;EACvD,KAAK,aAAa,wBAAwB,KAAK,eAAe;EAE9D,IAAI,KAAK,QAAQ,SACf,KAAK,OAAO;EAId,MAAM,eAAe,KAAK,aAAa,gBAAgB;EACvD,IAAI,cACF,KAAK,gBAAgB,YAAY;CAErC;CAEA,mBAAoC,UAAuB;EACzD,IAAI,CAAC,KAAK,QAAQ,SAChB;EAGF,IAAI,KAAK,qBAAqB,KAAK,yBAAyB,MAAM,KAAK;GAIrE,IAAI,KAAK,0BAA0B,MAAM,MACvC,KAAK,wBAAwB,MAAM,IAAI;GAEzC;EACF;EAEA,KAAK,cAAc;EACnB,KAAK,gBAAgB,KAAK;CAC5B;CAEA,mBAAoC,UAAuB;EACzD,KAAK,MAAM,MAAM,gCAAgC,MAAM,KAAK;EAE5D,MAAM,WAAW,KAAK,WAAW,MAAM,IAAI;EAC3C,KAAK,oBAAoB,KAAK,OAAO,UAAU,UAAU;GACvD,WAAW,KAAK,KAAK,aAAa;GAClC,YAAY;KACTC,6BAAAA,eAAAA;KACAC,6BAAAA,oBAAoB;KACpBC,6BAAAA,kBAAkB,KAAK,aAAa,iBAAiB,KAAK,KAAA;GAC7D;EACF,CAAC;EACD,KAAK,uBAAuB,MAAM;EAClC,KAAK,wBAAwB,MAAM;EAEnC,OAAO,KAAK;CACd;CAEA,sBAA6C;EAC3C,IAAI,KAAK,mBAAmB;GAC1B,KAAK,MAAM,MACT,8BAA8B,KAAK,wBAAwB,WAC7D;GAEA,KAAK,kBAAkB,IAAI,KAAK,KAAK,aAAa,CAAC;GACnD,KAAK,oBAAoB;GACzB,KAAK,uBAAuB;GAC5B,KAAK,wBAAwB;EAC/B;CACF;CAEA,2BAA4C,SAAuB;EACjE,MAAM,WAAW,KAAK,WAAW,IAAI;EACrC,KAAK,MAAM,MAAM,gCAAgC,UAAU;EAE3D,KAAK,mBAAmB,WAAW,QAAQ;EAC3C,KAAK,mBAAmB,aAAaD,6BAAAA,mBAAmB,QAAQ;EAChE,KAAK,wBAAwB;CAC/B;CAEA,cAA+B,SAC7B,KAAK,yCACD,KAAK,QAAQ,iBAAiB,EAAE,IAChC;CAKN,4BAAmD;EACjD,IAAI,KAAK,mBAAmB;GAC1B,KAAK,MAAM,MAAM,mCAAmC;GACpD,KAAK,cAAc;EACrB;CACF;CAMA,yBAA0C,EACxC,aACmC;EACnC,IAAI,WAAW,uBACb;EAGF,MAAM,eAAe,KAAK,aAAa,gBAAgB;EACvD,IAAI,cACF,KAAK,gBAAgB,YAAY;CAErC;CAEA,iBAAiC;EAC/B,KAAK,UAAU,EACb,SAAS,KACX,CAAC;EACD,KAAK,wBAAwB;GAC3B,OAAO,KAAK;GACZ,KAAK,KAAK;EACZ,CAAC;EACD,KAAK,MAAM,MACT,qEACF;CACF;CAEA,kBAAkC;EAChC,KAAK,cAAc;EACnB,KAAK,UAAU,EACb,SAAS,MACX,CAAC;EACD,KAAK,MAAM,MACT,8EACF;CACF;AACF"}