@grafana/faro-web-sdk 2.8.1 → 2.8.2

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.
Files changed (46) hide show
  1. package/dist/bundle/faro-web-sdk.iife.js +1 -1
  2. package/dist/bundle/types/instrumentations/console/instrumentation.d.ts +1 -1
  3. package/dist/bundle/types/instrumentations/csp/instrumentation.d.ts +1 -1
  4. package/dist/bundle/types/instrumentations/errors/instrumentation.d.ts +1 -1
  5. package/dist/bundle/types/instrumentations/navigation/instrumentation.d.ts +1 -1
  6. package/dist/bundle/types/instrumentations/performance/instrumentation.d.ts +1 -1
  7. package/dist/bundle/types/instrumentations/session/instrumentation.d.ts +2 -1
  8. package/dist/bundle/types/instrumentations/session/sessionManager/PersistentSessionsManager.d.ts +3 -0
  9. package/dist/bundle/types/instrumentations/session/sessionManager/VolatileSessionManager.d.ts +1 -0
  10. package/dist/bundle/types/instrumentations/session/sessionManager/sessionManagerUtils.d.ts +2 -1
  11. package/dist/bundle/types/instrumentations/userActions/instrumentation.d.ts +1 -1
  12. package/dist/bundle/types/instrumentations/view/instrumentation.d.ts +1 -1
  13. package/dist/bundle/types/instrumentations/webVitals/instrumentation.d.ts +1 -1
  14. package/dist/bundle/types/transports/console/transport.d.ts +1 -1
  15. package/dist/bundle/types/transports/fetch/transport.d.ts +1 -1
  16. package/dist/cjs/instrumentations/session/instrumentation.js +30 -5
  17. package/dist/cjs/instrumentations/session/instrumentation.js.map +1 -1
  18. package/dist/cjs/instrumentations/session/sessionManager/PersistentSessionsManager.js +15 -0
  19. package/dist/cjs/instrumentations/session/sessionManager/PersistentSessionsManager.js.map +1 -1
  20. package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js +3 -0
  21. package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
  22. package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js +11 -4
  23. package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
  24. package/dist/esm/instrumentations/session/instrumentation.js +30 -5
  25. package/dist/esm/instrumentations/session/instrumentation.js.map +1 -1
  26. package/dist/esm/instrumentations/session/sessionManager/PersistentSessionsManager.js +15 -0
  27. package/dist/esm/instrumentations/session/sessionManager/PersistentSessionsManager.js.map +1 -1
  28. package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js +3 -0
  29. package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
  30. package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js +11 -4
  31. package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
  32. package/dist/types/instrumentations/console/instrumentation.d.ts +1 -1
  33. package/dist/types/instrumentations/csp/instrumentation.d.ts +1 -1
  34. package/dist/types/instrumentations/errors/instrumentation.d.ts +1 -1
  35. package/dist/types/instrumentations/navigation/instrumentation.d.ts +1 -1
  36. package/dist/types/instrumentations/performance/instrumentation.d.ts +1 -1
  37. package/dist/types/instrumentations/session/instrumentation.d.ts +2 -1
  38. package/dist/types/instrumentations/session/sessionManager/PersistentSessionsManager.d.ts +3 -0
  39. package/dist/types/instrumentations/session/sessionManager/VolatileSessionManager.d.ts +1 -0
  40. package/dist/types/instrumentations/session/sessionManager/sessionManagerUtils.d.ts +2 -1
  41. package/dist/types/instrumentations/userActions/instrumentation.d.ts +1 -1
  42. package/dist/types/instrumentations/view/instrumentation.d.ts +1 -1
  43. package/dist/types/instrumentations/webVitals/instrumentation.d.ts +1 -1
  44. package/dist/types/transports/console/transport.d.ts +1 -1
  45. package/dist/types/transports/fetch/transport.d.ts +1 -1
  46. package/package.json +3 -3
@@ -11,11 +11,12 @@ export declare function isUserSessionValid(session: FaroUserSession | null): boo
11
11
  type GetUserSessionUpdaterParams = {
12
12
  storeUserSession: (session: FaroUserSession) => void;
13
13
  fetchUserSession: () => FaroUserSession | null;
14
+ adoptSession?: (sessionMeta: NonNullable<FaroUserSession['sessionMeta']>) => void;
14
15
  };
15
16
  type UpdateSessionParams = {
16
17
  forceSessionExtend: boolean;
17
18
  };
18
- export declare function getUserSessionUpdater({ fetchUserSession, storeUserSession, }: GetUserSessionUpdaterParams): (options?: UpdateSessionParams) => void;
19
+ export declare function getUserSessionUpdater({ fetchUserSession, storeUserSession, adoptSession, }: GetUserSessionUpdaterParams): (options?: UpdateSessionParams) => void;
19
20
  export declare function addSessionMetadataToNextSession(newSession: FaroUserSession, previousSession: FaroUserSession | null): Required<FaroUserSession>;
20
21
  type GetUserSessionMetaUpdateHandlerParams = {
21
22
  storeUserSession: (session: FaroUserSession) => void;
@@ -1,7 +1,7 @@
1
1
  import { BaseInstrumentation } from '@grafana/faro-core';
2
2
  export declare class UserActionInstrumentation extends BaseInstrumentation {
3
3
  readonly name = "@grafana/faro-web-sdk:instrumentation-user-action";
4
- readonly version = "2.8.1";
4
+ readonly version = "2.8.2";
5
5
  private _userActionSub?;
6
6
  initialize(): void;
7
7
  destroy(): void;
@@ -1,7 +1,7 @@
1
1
  import { BaseInstrumentation } from '@grafana/faro-core';
2
2
  export declare class ViewInstrumentation extends BaseInstrumentation {
3
3
  readonly name = "@grafana/faro-web-sdk:instrumentation-view";
4
- readonly version = "2.8.1";
4
+ readonly version = "2.8.2";
5
5
  private notifiedView;
6
6
  private sendViewChangedEvent;
7
7
  initialize(): void;
@@ -1,6 +1,6 @@
1
1
  import { BaseInstrumentation } from '@grafana/faro-core';
2
2
  export declare class WebVitalsInstrumentation extends BaseInstrumentation {
3
3
  readonly name = "@grafana/faro-web-sdk:instrumentation-web-vitals";
4
- readonly version = "2.8.1";
4
+ readonly version = "2.8.2";
5
5
  initialize(): void;
6
6
  }
@@ -4,7 +4,7 @@ import type { ConsoleTransportOptions } from './types';
4
4
  export declare class ConsoleTransport extends BaseTransport {
5
5
  private options;
6
6
  readonly name = "@grafana/faro-web-sdk:transport-console";
7
- readonly version = "2.8.1";
7
+ readonly version = "2.8.2";
8
8
  constructor(options?: ConsoleTransportOptions);
9
9
  send(item: TransportItem): void;
10
10
  }
@@ -4,7 +4,7 @@ import type { FetchTransportOptions } from './types';
4
4
  export declare class FetchTransport extends BaseTransport {
5
5
  private options;
6
6
  readonly name = "@grafana/faro-web-sdk:transport-fetch";
7
- readonly version = "2.8.1";
7
+ readonly version = "2.8.2";
8
8
  promiseBuffer: PromiseBuffer<Response | void>;
9
9
  private readonly rateLimitBackoffMs;
10
10
  private readonly getNow;
@@ -11,11 +11,18 @@ class SessionInstrumentation extends faro_core_1.BaseInstrumentation {
11
11
  super(...arguments);
12
12
  this.name = '@grafana/faro-web-sdk:instrumentation-session';
13
13
  this.version = faro_core_1.VERSION;
14
+ // Reads the session manager's adoption flag (set once the manager exists).
15
+ this.isAdoptingSession = () => false;
14
16
  }
15
17
  sendSessionStartEvent(meta) {
16
18
  var _a, _b;
17
19
  const session = meta.session;
18
20
  if (session && session.id !== ((_a = this.notifiedSession) === null || _a === void 0 ? void 0 : _a.id)) {
21
+ // Adopting another tab's session: track it but emit nothing (the creating tab already did).
22
+ if (this.isAdoptingSession()) {
23
+ this.notifiedSession = session;
24
+ return;
25
+ }
19
26
  if (this.notifiedSession && this.notifiedSession.id === ((_b = session.attributes) === null || _b === void 0 ? void 0 : _b['previousSession'])) {
20
27
  this.api.pushEvent(faro_core_1.EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });
21
28
  this.notifiedSession = session;
@@ -70,17 +77,35 @@ class SessionInstrumentation extends faro_core_1.BaseInstrumentation {
70
77
  }
71
78
  registerBeforeSendHook(SessionManager) {
72
79
  var _a;
73
- const { updateSession } = new SessionManager();
80
+ const sessionManager = new SessionManager();
81
+ this.isAdoptingSession = sessionManager.isAdopting;
82
+ const { updateSession } = sessionManager;
83
+ // Most recent rotation updateSession performed. A batch buffered before the
84
+ // rotation is all stamped with the now-expired id, not just the triggering item.
85
+ let lastRotation;
74
86
  (_a = this.transports) === null || _a === void 0 ? void 0 : _a.addBeforeSendHooks((item) => {
75
- var _a, _b, _c;
87
+ var _a, _b, _c, _d;
88
+ const previousSessionId = (_a = this.metas.value.session) === null || _a === void 0 ? void 0 : _a.id;
76
89
  updateSession();
77
- const attributes = (_a = item.meta.session) === null || _a === void 0 ? void 0 : _a.attributes;
90
+ const currentSession = this.metas.value.session;
91
+ if (currentSession != null && previousSessionId != null && currentSession.id !== previousSessionId) {
92
+ lastRotation = { from: previousSessionId, to: currentSession };
93
+ }
94
+ // Re-stamp items still carrying the rotated-from session. Keyed on that id,
95
+ // so items from a genuinely earlier session (explicit setSession, which
96
+ // updateSession never rotates) keep their own sampling decision.
97
+ const reStamp = lastRotation != null && ((_b = item.meta.session) === null || _b === void 0 ? void 0 : _b.id) === lastRotation.from;
98
+ const session = reStamp ? lastRotation.to : item.meta.session;
99
+ const attributes = session === null || session === void 0 ? void 0 : session.attributes;
78
100
  if (attributes && (attributes === null || attributes === void 0 ? void 0 : attributes['isSampled']) === 'true') {
79
101
  let newItem = JSON.parse(JSON.stringify(item));
80
- const newAttributes = (_b = newItem.meta.session) === null || _b === void 0 ? void 0 : _b.attributes;
102
+ if (reStamp) {
103
+ newItem.meta.session = JSON.parse(JSON.stringify(lastRotation.to));
104
+ }
105
+ const newAttributes = (_c = newItem.meta.session) === null || _c === void 0 ? void 0 : _c.attributes;
81
106
  newAttributes === null || newAttributes === void 0 ? true : delete newAttributes['isSampled'];
82
107
  if (Object.keys(newAttributes !== null && newAttributes !== void 0 ? newAttributes : {}).length === 0) {
83
- (_c = newItem.meta.session) === null || _c === void 0 ? true : delete _c.attributes;
108
+ (_d = newItem.meta.session) === null || _d === void 0 ? true : delete _d.attributes;
84
109
  }
85
110
  return newItem;
86
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseInstrumentation,\n dateNow,\n EVENT_SESSION_EXTEND,\n EVENT_SESSION_RESUME,\n EVENT_SESSION_START,\n VERSION,\n} from '@grafana/faro-core';\nimport type { Config, Meta, MetaSession } from '@grafana/faro-core';\n\nimport type { TransportItem } from '../..';\nimport { createSession } from '../../metas';\n\nimport { type FaroUserSession, getSessionManagerByConfig, isSampled } from './sessionManager';\nimport { PersistentSessionsManager } from './sessionManager/PersistentSessionsManager';\nimport { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';\nimport type { SessionManager } from './sessionManager/types';\n\ntype LifecycleType = typeof EVENT_SESSION_RESUME | typeof EVENT_SESSION_START;\n\nexport class SessionInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-web-sdk:instrumentation-session';\n readonly version = VERSION;\n\n // previously notified session, to ensure we don't send session start\n // event twice for the same session\n private notifiedSession: MetaSession | undefined;\n\n private sendSessionStartEvent(meta: Meta): void {\n const session = meta.session;\n\n if (session && session.id !== this.notifiedSession?.id) {\n if (this.notifiedSession && this.notifiedSession.id === session.attributes?.['previousSession']) {\n this.api.pushEvent(EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });\n this.notifiedSession = session;\n return;\n }\n\n this.notifiedSession = session;\n // no need to add attributes and session id, they are included as part of meta\n // automatically\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n }\n\n private createInitialSession(\n SessionManager: SessionManager,\n sessionsConfig: Required<Config>['sessionTracking']\n ): {\n initialSession: FaroUserSession;\n lifecycleType: LifecycleType;\n } {\n let storedUserSession: FaroUserSession | null = SessionManager.fetchUserSession();\n\n if (sessionsConfig.persistent && sessionsConfig.maxSessionPersistenceTime && storedUserSession) {\n const now = dateNow();\n const shouldClearPersistentSession =\n storedUserSession.lastActivity < now - sessionsConfig.maxSessionPersistenceTime;\n\n if (shouldClearPersistentSession) {\n PersistentSessionsManager.removeUserSession();\n storedUserSession = null;\n }\n }\n\n let lifecycleType: LifecycleType;\n let initialSession: FaroUserSession;\n\n if (isUserSessionValid(storedUserSession)) {\n const sessionId = storedUserSession?.sessionId;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: storedUserSession!.isSampled || false,\n started: storedUserSession?.started,\n });\n\n const storedUserSessionMeta = storedUserSession?.sessionMeta;\n\n // For resumed sessions we want to merge the previous overrides with the configured ones.\n // If the same key is present in both, the new one will override the old one.\n const overrides = { ...sessionsConfig.session?.overrides, ...storedUserSessionMeta?.overrides };\n\n initialSession.sessionMeta = {\n ...sessionsConfig.session,\n id: sessionId,\n attributes: {\n ...sessionsConfig.session?.attributes,\n ...storedUserSessionMeta?.attributes,\n // For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.\n isSampled: initialSession.isSampled.toString(),\n },\n overrides,\n };\n\n lifecycleType = EVENT_SESSION_RESUME;\n } else {\n const sessionId = sessionsConfig.session?.id ?? createSession().id;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: isSampled(),\n });\n\n const overrides = sessionsConfig.session?.overrides;\n\n initialSession.sessionMeta = {\n id: sessionId,\n attributes: {\n isSampled: initialSession.isSampled.toString(),\n ...sessionsConfig.session?.attributes,\n },\n // new session we don't care about previous overrides\n ...(overrides ? { overrides } : {}),\n };\n\n lifecycleType = EVENT_SESSION_START;\n }\n\n return { initialSession, lifecycleType };\n }\n\n private registerBeforeSendHook(SessionManager: SessionManager) {\n const { updateSession } = new SessionManager();\n\n this.transports?.addBeforeSendHooks((item) => {\n updateSession();\n\n const attributes = item.meta.session?.attributes;\n\n if (attributes && attributes?.['isSampled'] === 'true') {\n let newItem: TransportItem = JSON.parse(JSON.stringify(item));\n\n const newAttributes = newItem.meta.session?.attributes;\n delete newAttributes?.['isSampled'];\n\n if (Object.keys(newAttributes ?? {}).length === 0) {\n delete newItem.meta.session?.attributes;\n }\n\n return newItem;\n }\n\n return null;\n });\n }\n\n initialize() {\n this.logDebug('init session instrumentation');\n\n const sessionTrackingConfig = this.config.sessionTracking;\n\n if (sessionTrackingConfig?.enabled) {\n const SessionManager = getSessionManagerByConfig(sessionTrackingConfig);\n\n this.registerBeforeSendHook(SessionManager);\n\n const { initialSession, lifecycleType } = this.createInitialSession(SessionManager, sessionTrackingConfig);\n\n SessionManager.storeUserSession(initialSession);\n\n const initialSessionMeta = initialSession.sessionMeta;\n\n this.notifiedSession = initialSessionMeta;\n this.api.setSession(initialSessionMeta);\n\n if (lifecycleType === EVENT_SESSION_START) {\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n\n if (lifecycleType === EVENT_SESSION_RESUME) {\n this.api.pushEvent(EVENT_SESSION_RESUME, {}, undefined, { skipDedupe: true });\n }\n }\n\n this.metas.addListener(this.sendSessionStartEvent.bind(this));\n }\n}\n"]}
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseInstrumentation,\n dateNow,\n EVENT_SESSION_EXTEND,\n EVENT_SESSION_RESUME,\n EVENT_SESSION_START,\n VERSION,\n} from '@grafana/faro-core';\nimport type { Config, Meta, MetaSession } from '@grafana/faro-core';\n\nimport type { TransportItem } from '../..';\nimport { createSession } from '../../metas';\n\nimport { type FaroUserSession, getSessionManagerByConfig, isSampled } from './sessionManager';\nimport { PersistentSessionsManager } from './sessionManager/PersistentSessionsManager';\nimport { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';\nimport type { SessionManager } from './sessionManager/types';\n\ntype LifecycleType = typeof EVENT_SESSION_RESUME | typeof EVENT_SESSION_START;\n\nexport class SessionInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-web-sdk:instrumentation-session';\n readonly version = VERSION;\n\n // previously notified session, to ensure we don't send session start\n // event twice for the same session\n private notifiedSession: MetaSession | undefined;\n\n // Reads the session manager's adoption flag (set once the manager exists).\n private isAdoptingSession: () => boolean = () => false;\n\n private sendSessionStartEvent(meta: Meta): void {\n const session = meta.session;\n\n if (session && session.id !== this.notifiedSession?.id) {\n // Adopting another tab's session: track it but emit nothing (the creating tab already did).\n if (this.isAdoptingSession()) {\n this.notifiedSession = session;\n return;\n }\n\n if (this.notifiedSession && this.notifiedSession.id === session.attributes?.['previousSession']) {\n this.api.pushEvent(EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });\n this.notifiedSession = session;\n return;\n }\n\n this.notifiedSession = session;\n // no need to add attributes and session id, they are included as part of meta\n // automatically\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n }\n\n private createInitialSession(\n SessionManager: SessionManager,\n sessionsConfig: Required<Config>['sessionTracking']\n ): {\n initialSession: FaroUserSession;\n lifecycleType: LifecycleType;\n } {\n let storedUserSession: FaroUserSession | null = SessionManager.fetchUserSession();\n\n if (sessionsConfig.persistent && sessionsConfig.maxSessionPersistenceTime && storedUserSession) {\n const now = dateNow();\n const shouldClearPersistentSession =\n storedUserSession.lastActivity < now - sessionsConfig.maxSessionPersistenceTime;\n\n if (shouldClearPersistentSession) {\n PersistentSessionsManager.removeUserSession();\n storedUserSession = null;\n }\n }\n\n let lifecycleType: LifecycleType;\n let initialSession: FaroUserSession;\n\n if (isUserSessionValid(storedUserSession)) {\n const sessionId = storedUserSession?.sessionId;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: storedUserSession!.isSampled || false,\n started: storedUserSession?.started,\n });\n\n const storedUserSessionMeta = storedUserSession?.sessionMeta;\n\n // For resumed sessions we want to merge the previous overrides with the configured ones.\n // If the same key is present in both, the new one will override the old one.\n const overrides = { ...sessionsConfig.session?.overrides, ...storedUserSessionMeta?.overrides };\n\n initialSession.sessionMeta = {\n ...sessionsConfig.session,\n id: sessionId,\n attributes: {\n ...sessionsConfig.session?.attributes,\n ...storedUserSessionMeta?.attributes,\n // For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.\n isSampled: initialSession.isSampled.toString(),\n },\n overrides,\n };\n\n lifecycleType = EVENT_SESSION_RESUME;\n } else {\n const sessionId = sessionsConfig.session?.id ?? createSession().id;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: isSampled(),\n });\n\n const overrides = sessionsConfig.session?.overrides;\n\n initialSession.sessionMeta = {\n id: sessionId,\n attributes: {\n isSampled: initialSession.isSampled.toString(),\n ...sessionsConfig.session?.attributes,\n },\n // new session we don't care about previous overrides\n ...(overrides ? { overrides } : {}),\n };\n\n lifecycleType = EVENT_SESSION_START;\n }\n\n return { initialSession, lifecycleType };\n }\n\n private registerBeforeSendHook(SessionManager: SessionManager) {\n const sessionManager = new SessionManager();\n this.isAdoptingSession = sessionManager.isAdopting;\n const { updateSession } = sessionManager;\n\n // Most recent rotation updateSession performed. A batch buffered before the\n // rotation is all stamped with the now-expired id, not just the triggering item.\n let lastRotation: { from: string; to: MetaSession } | undefined;\n\n this.transports?.addBeforeSendHooks((item) => {\n const previousSessionId = this.metas.value.session?.id;\n updateSession();\n const currentSession = this.metas.value.session;\n\n if (currentSession != null && previousSessionId != null && currentSession.id !== previousSessionId) {\n lastRotation = { from: previousSessionId, to: currentSession };\n }\n\n // Re-stamp items still carrying the rotated-from session. Keyed on that id,\n // so items from a genuinely earlier session (explicit setSession, which\n // updateSession never rotates) keep their own sampling decision.\n const reStamp = lastRotation != null && item.meta.session?.id === lastRotation.from;\n const session = reStamp ? lastRotation!.to : item.meta.session;\n\n const attributes = session?.attributes;\n\n if (attributes && attributes?.['isSampled'] === 'true') {\n let newItem: TransportItem = JSON.parse(JSON.stringify(item));\n\n if (reStamp) {\n newItem.meta.session = JSON.parse(JSON.stringify(lastRotation!.to));\n }\n\n const newAttributes = newItem.meta.session?.attributes;\n delete newAttributes?.['isSampled'];\n\n if (Object.keys(newAttributes ?? {}).length === 0) {\n delete newItem.meta.session?.attributes;\n }\n\n return newItem;\n }\n\n return null;\n });\n }\n\n initialize() {\n this.logDebug('init session instrumentation');\n\n const sessionTrackingConfig = this.config.sessionTracking;\n\n if (sessionTrackingConfig?.enabled) {\n const SessionManager = getSessionManagerByConfig(sessionTrackingConfig);\n\n this.registerBeforeSendHook(SessionManager);\n\n const { initialSession, lifecycleType } = this.createInitialSession(SessionManager, sessionTrackingConfig);\n\n SessionManager.storeUserSession(initialSession);\n\n const initialSessionMeta = initialSession.sessionMeta;\n\n this.notifiedSession = initialSessionMeta;\n this.api.setSession(initialSessionMeta);\n\n if (lifecycleType === EVENT_SESSION_START) {\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n\n if (lifecycleType === EVENT_SESSION_RESUME) {\n this.api.pushEvent(EVENT_SESSION_RESUME, {}, undefined, { skipDedupe: true });\n }\n }\n\n this.metas.addListener(this.sendSessionStartEvent.bind(this));\n }\n}\n"]}
@@ -8,10 +8,25 @@ const sessionConstants_1 = require("./sessionConstants");
8
8
  const sessionManagerUtils_1 = require("./sessionManagerUtils");
9
9
  class PersistentSessionsManager {
10
10
  constructor() {
11
+ // Set only for the synchronous span of an adopting setSession(); the session
12
+ // instrumentation reads isAdopting() to suppress its lifecycle event.
13
+ this.adopting = false;
14
+ this.isAdopting = () => this.adopting;
15
+ this.adoptSession = (sessionMeta) => {
16
+ var _a;
17
+ this.adopting = true;
18
+ try {
19
+ (_a = faro_core_1.faro.api) === null || _a === void 0 ? void 0 : _a.setSession(sessionMeta);
20
+ }
21
+ finally {
22
+ this.adopting = false;
23
+ }
24
+ };
11
25
  this.updateSession = (0, utils_1.throttle)(() => this.updateUserSession(), sessionConstants_1.STORAGE_UPDATE_DELAY);
12
26
  this.updateUserSession = (0, sessionManagerUtils_1.getUserSessionUpdater)({
13
27
  fetchUserSession: PersistentSessionsManager.fetchUserSession,
14
28
  storeUserSession: PersistentSessionsManager.storeUserSession,
29
+ adoptSession: this.adoptSession,
15
30
  });
16
31
  this.init();
17
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PersistentSessionsManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/PersistentSessionsManager.ts"],"names":[],"mappings":";;;AAAA,kDAAiE;AAEjE,0CAA0C;AAC1C,0DAAyF;AAEzF,yDAAuE;AACvE,+DAA2F;AAG3F,MAAa,yBAAyB;IAIpC;QA2BA,kBAAa,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,uCAAoB,CAAC,CAAC;QA1B7E,IAAI,CAAC,iBAAiB,GAAG,IAAA,2CAAqB,EAAC;YAC7C,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;SAC7D,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,IAAA,uBAAU,EAAC,8BAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,IAAA,oBAAO,EAAC,8BAAW,EAAE,IAAA,iCAAqB,EAAC,OAAO,CAAC,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,IAAA,oBAAO,EAAC,8BAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,gBAAI,CAAC,KAAK,CAAC,WAAW,CACpB,IAAA,iDAA2B,EAAC;YAC1B,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;SAC7D,CAAC,CACH,CAAC;IACJ,CAAC;;AA/CH,8DAgDC;AA/CgB,0CAAgB,GAAG,2BAAc,CAAC,KAAK,AAAvB,CAAwB","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class PersistentSessionsManager {\n private static storageTypeLocal = webStorageType.local;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), PersistentSessionsManager.storageTypeLocal);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
1
+ {"version":3,"file":"PersistentSessionsManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/PersistentSessionsManager.ts"],"names":[],"mappings":";;;AAAA,kDAAiE;AAGjE,0CAA0C;AAC1C,0DAAyF;AAEzF,yDAAuE;AACvE,+DAA2F;AAG3F,MAAa,yBAAyB;IAmBpC;QAfA,6EAA6E;QAC7E,sEAAsE;QAC9D,aAAQ,GAAG,KAAK,CAAC;QAEzB,eAAU,GAAG,GAAY,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;QAElC,iBAAY,GAAG,CAAC,WAAwB,EAAQ,EAAE;;YACxD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC;gBACH,MAAA,gBAAI,CAAC,GAAG,0CAAE,UAAU,CAAC,WAAW,CAAC,CAAC;YACpC,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACxB,CAAC;QACH,CAAC,CAAC;QA8BF,kBAAa,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,uCAAoB,CAAC,CAAC;QA3B7E,IAAI,CAAC,iBAAiB,GAAG,IAAA,2CAAqB,EAAC;YAC7C,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,IAAA,uBAAU,EAAC,8BAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,IAAA,oBAAO,EAAC,8BAAW,EAAE,IAAA,iCAAqB,EAAC,OAAO,CAAC,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,IAAA,oBAAO,EAAC,8BAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,gBAAI,CAAC,KAAK,CAAC,WAAW,CACpB,IAAA,iDAA2B,EAAC;YAC1B,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;SAC7D,CAAC,CACH,CAAC;IACJ,CAAC;;AA/DH,8DAgEC;AA/DgB,0CAAgB,GAAG,2BAAc,CAAC,KAAK,AAAvB,CAAwB","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\nimport type { MetaSession } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class PersistentSessionsManager {\n private static storageTypeLocal = webStorageType.local;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n // Set only for the synchronous span of an adopting setSession(); the session\n // instrumentation reads isAdopting() to suppress its lifecycle event.\n private adopting = false;\n\n isAdopting = (): boolean => this.adopting;\n\n private adoptSession = (sessionMeta: MetaSession): void => {\n this.adopting = true;\n try {\n faro.api?.setSession(sessionMeta);\n } finally {\n this.adopting = false;\n }\n };\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n adoptSession: this.adoptSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), PersistentSessionsManager.storageTypeLocal);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
@@ -8,6 +8,9 @@ const sessionConstants_1 = require("./sessionConstants");
8
8
  const sessionManagerUtils_1 = require("./sessionManagerUtils");
9
9
  class VolatileSessionsManager {
10
10
  constructor() {
11
+ // sessionStorage is tab-local, so this manager never adopts another tab's
12
+ // session. Stubbed so the instrumentation can treat both managers uniformly.
13
+ this.isAdopting = () => false;
11
14
  this.updateSession = (0, utils_1.throttle)(() => this.updateUserSession(), sessionConstants_1.STORAGE_UPDATE_DELAY);
12
15
  this.updateUserSession = (0, sessionManagerUtils_1.getUserSessionUpdater)({
13
16
  fetchUserSession: VolatileSessionsManager.fetchUserSession,
@@ -1 +1 @@
1
- {"version":3,"file":"VolatileSessionManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/VolatileSessionManager.ts"],"names":[],"mappings":";;;AAAA,kDAAiE;AAEjE,0CAA0C;AAC1C,0DAAyF;AAEzF,yDAAuE;AACvE,+DAA2F;AAG3F,MAAa,uBAAuB;IAIlC;QA2BA,kBAAa,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,uCAAoB,CAAC,CAAC;QA1B7E,IAAI,CAAC,iBAAiB,GAAG,IAAA,2CAAqB,EAAC;YAC7C,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,IAAA,uBAAU,EAAC,8BAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,IAAA,oBAAO,EAAC,8BAAW,EAAE,IAAA,iCAAqB,EAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,IAAA,oBAAO,EAAC,8BAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,gBAAI,CAAC,KAAK,CAAC,WAAW,CACpB,IAAA,iDAA2B,EAAC;YAC1B,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CACH,CAAC;IACJ,CAAC;;AA/CH,0DAgDC;AA/CgB,0CAAkB,GAAG,2BAAc,CAAC,OAAO,AAAzB,CAA0B","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class VolatileSessionsManager {\n private static storageTypeSession = webStorageType.session;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), VolatileSessionsManager.storageTypeSession);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
1
+ {"version":3,"file":"VolatileSessionManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/VolatileSessionManager.ts"],"names":[],"mappings":";;;AAAA,kDAAiE;AAEjE,0CAA0C;AAC1C,0DAAyF;AAEzF,yDAAuE;AACvE,+DAA2F;AAG3F,MAAa,uBAAuB;IAQlC;QAJA,0EAA0E;QAC1E,6EAA6E;QAC7E,eAAU,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC;QA6BlC,kBAAa,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,uCAAoB,CAAC,CAAC;QA1B7E,IAAI,CAAC,iBAAiB,GAAG,IAAA,2CAAqB,EAAC;YAC7C,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,IAAA,uBAAU,EAAC,8BAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,IAAA,oBAAO,EAAC,8BAAW,EAAE,IAAA,iCAAqB,EAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,IAAA,oBAAO,EAAC,8BAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,gBAAI,CAAC,KAAK,CAAC,WAAW,CACpB,IAAA,iDAA2B,EAAC;YAC1B,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CACH,CAAC;IACJ,CAAC;;AAnDH,0DAoDC;AAnDgB,0CAAkB,GAAG,2BAAc,CAAC,OAAO,AAAzB,CAA0B","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class VolatileSessionsManager {\n private static storageTypeSession = webStorageType.session;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n // sessionStorage is tab-local, so this manager never adopts another tab's\n // session. Stubbed so the instrumentation can treat both managers uniformly.\n isAdopting = (): boolean => false;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), VolatileSessionsManager.storageTypeSession);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
@@ -35,9 +35,9 @@ function isUserSessionValid(session) {
35
35
  const inactivityPeriodValid = now - session.lastActivity < sessionConstants_1.SESSION_INACTIVITY_TIME;
36
36
  return inactivityPeriodValid;
37
37
  }
38
- function getUserSessionUpdater({ fetchUserSession, storeUserSession, }) {
38
+ function getUserSessionUpdater({ fetchUserSession, storeUserSession, adoptSession, }) {
39
39
  return function updateSession({ forceSessionExtend } = { forceSessionExtend: false }) {
40
- var _a, _b, _c;
40
+ var _a, _b, _c, _d;
41
41
  if (!fetchUserSession || !storeUserSession) {
42
42
  return;
43
43
  }
@@ -49,12 +49,19 @@ function getUserSessionUpdater({ fetchUserSession, storeUserSession, }) {
49
49
  const sessionFromStorage = fetchUserSession();
50
50
  if (forceSessionExtend === false && isUserSessionValid(sessionFromStorage)) {
51
51
  storeUserSession(Object.assign(Object.assign({}, sessionFromStorage), { lastActivity: (0, faro_core_1.dateNow)() }));
52
+ // Another tab rotated the shared session; adopt it so we stop emitting the stale id.
53
+ const inMemorySessionId = (_a = faro_core_1.faro.metas.value.session) === null || _a === void 0 ? void 0 : _a.id;
54
+ if (adoptSession != null &&
55
+ sessionFromStorage.sessionMeta != null &&
56
+ sessionFromStorage.sessionId !== inMemorySessionId) {
57
+ adoptSession(sessionFromStorage.sessionMeta);
58
+ }
52
59
  }
53
60
  else {
54
61
  let newSession = addSessionMetadataToNextSession(createUserSessionObject({ isSampled: (0, sampling_1.isSampled)() }), sessionFromStorage);
55
62
  storeUserSession(newSession);
56
- (_a = faro_core_1.faro.api) === null || _a === void 0 ? void 0 : _a.setSession(newSession.sessionMeta);
57
- (_b = sessionTrackingConfig === null || sessionTrackingConfig === void 0 ? void 0 : sessionTrackingConfig.onSessionChange) === null || _b === void 0 ? void 0 : _b.call(sessionTrackingConfig, (_c = sessionFromStorage === null || sessionFromStorage === void 0 ? void 0 : sessionFromStorage.sessionMeta) !== null && _c !== void 0 ? _c : null, newSession.sessionMeta);
63
+ (_b = faro_core_1.faro.api) === null || _b === void 0 ? void 0 : _b.setSession(newSession.sessionMeta);
64
+ (_c = sessionTrackingConfig === null || sessionTrackingConfig === void 0 ? void 0 : sessionTrackingConfig.onSessionChange) === null || _c === void 0 ? void 0 : _c.call(sessionTrackingConfig, (_d = sessionFromStorage === null || sessionFromStorage === void 0 ? void 0 : sessionFromStorage.sessionMeta) !== null && _d !== void 0 ? _d : null, newSession.sessionMeta);
58
65
  }
59
66
  };
60
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sessionManagerUtils.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/sessionManagerUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { dateNow, deepEqual, EVENT_OVERRIDES_SERVICE_NAME, faro, genShortID, isEmpty } from '@grafana/faro-core';\nimport type { Meta, MetaOverrides } from '@grafana/faro-core';\n\nimport { isLocalStorageAvailable, isSessionStorageAvailable } from '../../../utils';\n\nimport { isSampled } from './sampling';\nimport { SESSION_EXPIRATION_TIME, SESSION_INACTIVITY_TIME } from './sessionConstants';\nimport type { FaroUserSession } from './types';\n\ntype CreateUserSessionObjectParams = {\n sessionId?: string;\n started?: number;\n lastActivity?: number;\n isSampled?: boolean;\n};\n\nexport function createUserSessionObject({\n sessionId,\n started,\n lastActivity,\n isSampled = true,\n}: CreateUserSessionObjectParams = {}): FaroUserSession {\n const now = dateNow();\n\n const generateSessionId = faro.config?.sessionTracking?.generateSessionId;\n\n if (sessionId == null) {\n sessionId = typeof generateSessionId === 'function' ? generateSessionId() : genShortID();\n }\n\n return {\n sessionId,\n lastActivity: lastActivity ?? now,\n started: started ?? now,\n isSampled: isSampled,\n };\n}\n\nexport function isUserSessionValid(session: FaroUserSession | null): boolean {\n if (session == null) {\n return false;\n }\n\n const now = dateNow();\n const lifetimeValid = now - session.started < SESSION_EXPIRATION_TIME;\n\n if (!lifetimeValid) {\n return false;\n }\n\n const inactivityPeriodValid = now - session.lastActivity < SESSION_INACTIVITY_TIME;\n return inactivityPeriodValid;\n}\n\ntype GetUserSessionUpdaterParams = {\n storeUserSession: (session: FaroUserSession) => void;\n fetchUserSession: () => FaroUserSession | null;\n};\n\ntype UpdateSessionParams = { forceSessionExtend: boolean };\n\nexport function getUserSessionUpdater({\n fetchUserSession,\n storeUserSession,\n}: GetUserSessionUpdaterParams): (options?: UpdateSessionParams) => void {\n return function updateSession({ forceSessionExtend } = { forceSessionExtend: false }): void {\n if (!fetchUserSession || !storeUserSession) {\n return;\n }\n\n const sessionTrackingConfig = faro.config.sessionTracking;\n const isPersistentSessions = sessionTrackingConfig?.persistent;\n\n if ((isPersistentSessions && !isLocalStorageAvailable) || (!isPersistentSessions && !isSessionStorageAvailable)) {\n return;\n }\n\n const sessionFromStorage = fetchUserSession();\n\n if (forceSessionExtend === false && isUserSessionValid(sessionFromStorage)) {\n storeUserSession({ ...sessionFromStorage!, lastActivity: dateNow() });\n } else {\n let newSession = addSessionMetadataToNextSession(\n createUserSessionObject({ isSampled: isSampled() }),\n sessionFromStorage\n );\n\n storeUserSession(newSession);\n\n faro.api?.setSession(newSession.sessionMeta);\n sessionTrackingConfig?.onSessionChange?.(sessionFromStorage?.sessionMeta ?? null, newSession.sessionMeta!);\n }\n };\n}\n\nexport function addSessionMetadataToNextSession(newSession: FaroUserSession, previousSession: FaroUserSession | null) {\n const sessionWithMeta: Required<FaroUserSession> = {\n ...newSession,\n sessionMeta: {\n id: newSession.sessionId,\n attributes: removeUndefinedValues({\n ...faro.config.sessionTracking?.session?.attributes,\n ...(faro.metas.value.session?.attributes ?? {}),\n isSampled: newSession.isSampled.toString(),\n }),\n },\n };\n\n const overrides = faro.metas.value.session?.overrides ?? previousSession?.sessionMeta?.overrides;\n if (!isEmpty(overrides)) {\n sessionWithMeta.sessionMeta.overrides = overrides;\n }\n\n const previousSessionId = previousSession?.sessionId;\n if (previousSessionId != null) {\n sessionWithMeta.sessionMeta.attributes!['previousSession'] = previousSessionId;\n }\n\n return sessionWithMeta;\n}\n\ntype GetUserSessionMetaUpdateHandlerParams = {\n storeUserSession: (session: FaroUserSession) => void;\n fetchUserSession: () => FaroUserSession | null;\n};\n\nexport function getSessionMetaUpdateHandler({\n fetchUserSession,\n storeUserSession,\n}: GetUserSessionMetaUpdateHandlerParams) {\n let isSyncing = false;\n\n return function syncSessionIfChangedExternally(meta: Meta) {\n if (isSyncing) {\n return;\n }\n const session = meta.session;\n const sessionFromSessionStorage = fetchUserSession();\n\n let sessionId = session?.id;\n const sessionAttributes = session?.attributes;\n const sessionOverrides = session?.overrides;\n\n const storedSessionMeta = sessionFromSessionStorage?.sessionMeta;\n const storedSessionMetaOverrides = storedSessionMeta?.overrides;\n\n const hasSessionOverridesChanged = !!sessionOverrides && !deepEqual(sessionOverrides, storedSessionMetaOverrides);\n const hasAttributesChanged = !!sessionAttributes && !deepEqual(sessionAttributes, storedSessionMeta?.attributes);\n const hasSessionIdChanged = !!session && sessionId !== sessionFromSessionStorage?.sessionId;\n\n if (hasSessionIdChanged || hasAttributesChanged || hasSessionOverridesChanged) {\n const userSession = addSessionMetadataToNextSession(\n createUserSessionObject({ sessionId, isSampled: isSampled() }),\n sessionFromSessionStorage\n );\n\n storeUserSession(userSession);\n sendOverrideEvent(hasSessionOverridesChanged, sessionOverrides, storedSessionMetaOverrides);\n\n isSyncing = true;\n try {\n faro.api.setSession(userSession.sessionMeta);\n } finally {\n isSyncing = false;\n }\n }\n };\n}\n\nfunction removeUndefinedValues(obj: Record<string, string | undefined>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const key of Object.keys(obj)) {\n const value = obj[key];\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction sendOverrideEvent(\n hasSessionOverridesChanged: boolean,\n sessionOverrides: MetaOverrides = {},\n storedSessionOverrides: MetaOverrides = {}\n) {\n if (!hasSessionOverridesChanged) {\n return;\n }\n\n const serviceName = sessionOverrides.serviceName;\n const previousServiceName = storedSessionOverrides.serviceName ?? faro.metas.value.app?.name ?? '';\n\n if (serviceName && serviceName !== previousServiceName) {\n faro.api.pushEvent(EVENT_OVERRIDES_SERVICE_NAME, {\n serviceName,\n previousServiceName,\n });\n }\n}\n"]}
1
+ {"version":3,"file":"sessionManagerUtils.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/sessionManagerUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { dateNow, deepEqual, EVENT_OVERRIDES_SERVICE_NAME, faro, genShortID, isEmpty } from '@grafana/faro-core';\nimport type { Meta, MetaOverrides } from '@grafana/faro-core';\n\nimport { isLocalStorageAvailable, isSessionStorageAvailable } from '../../../utils';\n\nimport { isSampled } from './sampling';\nimport { SESSION_EXPIRATION_TIME, SESSION_INACTIVITY_TIME } from './sessionConstants';\nimport type { FaroUserSession } from './types';\n\ntype CreateUserSessionObjectParams = {\n sessionId?: string;\n started?: number;\n lastActivity?: number;\n isSampled?: boolean;\n};\n\nexport function createUserSessionObject({\n sessionId,\n started,\n lastActivity,\n isSampled = true,\n}: CreateUserSessionObjectParams = {}): FaroUserSession {\n const now = dateNow();\n\n const generateSessionId = faro.config?.sessionTracking?.generateSessionId;\n\n if (sessionId == null) {\n sessionId = typeof generateSessionId === 'function' ? generateSessionId() : genShortID();\n }\n\n return {\n sessionId,\n lastActivity: lastActivity ?? now,\n started: started ?? now,\n isSampled: isSampled,\n };\n}\n\nexport function isUserSessionValid(session: FaroUserSession | null): boolean {\n if (session == null) {\n return false;\n }\n\n const now = dateNow();\n const lifetimeValid = now - session.started < SESSION_EXPIRATION_TIME;\n\n if (!lifetimeValid) {\n return false;\n }\n\n const inactivityPeriodValid = now - session.lastActivity < SESSION_INACTIVITY_TIME;\n return inactivityPeriodValid;\n}\n\ntype GetUserSessionUpdaterParams = {\n storeUserSession: (session: FaroUserSession) => void;\n fetchUserSession: () => FaroUserSession | null;\n // Silently adopt another tab's session into in-memory metas (cross-tab sync).\n // Optional: only the valid (non-force-extend) branch uses it.\n adoptSession?: (sessionMeta: NonNullable<FaroUserSession['sessionMeta']>) => void;\n};\n\ntype UpdateSessionParams = { forceSessionExtend: boolean };\n\nexport function getUserSessionUpdater({\n fetchUserSession,\n storeUserSession,\n adoptSession,\n}: GetUserSessionUpdaterParams): (options?: UpdateSessionParams) => void {\n return function updateSession({ forceSessionExtend } = { forceSessionExtend: false }): void {\n if (!fetchUserSession || !storeUserSession) {\n return;\n }\n\n const sessionTrackingConfig = faro.config.sessionTracking;\n const isPersistentSessions = sessionTrackingConfig?.persistent;\n\n if ((isPersistentSessions && !isLocalStorageAvailable) || (!isPersistentSessions && !isSessionStorageAvailable)) {\n return;\n }\n\n const sessionFromStorage = fetchUserSession();\n\n if (forceSessionExtend === false && isUserSessionValid(sessionFromStorage)) {\n storeUserSession({ ...sessionFromStorage!, lastActivity: dateNow() });\n\n // Another tab rotated the shared session; adopt it so we stop emitting the stale id.\n const inMemorySessionId = faro.metas.value.session?.id;\n if (\n adoptSession != null &&\n sessionFromStorage!.sessionMeta != null &&\n sessionFromStorage!.sessionId !== inMemorySessionId\n ) {\n adoptSession(sessionFromStorage!.sessionMeta);\n }\n } else {\n let newSession = addSessionMetadataToNextSession(\n createUserSessionObject({ isSampled: isSampled() }),\n sessionFromStorage\n );\n\n storeUserSession(newSession);\n\n faro.api?.setSession(newSession.sessionMeta);\n sessionTrackingConfig?.onSessionChange?.(sessionFromStorage?.sessionMeta ?? null, newSession.sessionMeta!);\n }\n };\n}\n\nexport function addSessionMetadataToNextSession(newSession: FaroUserSession, previousSession: FaroUserSession | null) {\n const sessionWithMeta: Required<FaroUserSession> = {\n ...newSession,\n sessionMeta: {\n id: newSession.sessionId,\n attributes: removeUndefinedValues({\n ...faro.config.sessionTracking?.session?.attributes,\n ...(faro.metas.value.session?.attributes ?? {}),\n isSampled: newSession.isSampled.toString(),\n }),\n },\n };\n\n const overrides = faro.metas.value.session?.overrides ?? previousSession?.sessionMeta?.overrides;\n if (!isEmpty(overrides)) {\n sessionWithMeta.sessionMeta.overrides = overrides;\n }\n\n const previousSessionId = previousSession?.sessionId;\n if (previousSessionId != null) {\n sessionWithMeta.sessionMeta.attributes!['previousSession'] = previousSessionId;\n }\n\n return sessionWithMeta;\n}\n\ntype GetUserSessionMetaUpdateHandlerParams = {\n storeUserSession: (session: FaroUserSession) => void;\n fetchUserSession: () => FaroUserSession | null;\n};\n\nexport function getSessionMetaUpdateHandler({\n fetchUserSession,\n storeUserSession,\n}: GetUserSessionMetaUpdateHandlerParams) {\n let isSyncing = false;\n\n return function syncSessionIfChangedExternally(meta: Meta) {\n if (isSyncing) {\n return;\n }\n const session = meta.session;\n const sessionFromSessionStorage = fetchUserSession();\n\n let sessionId = session?.id;\n const sessionAttributes = session?.attributes;\n const sessionOverrides = session?.overrides;\n\n const storedSessionMeta = sessionFromSessionStorage?.sessionMeta;\n const storedSessionMetaOverrides = storedSessionMeta?.overrides;\n\n const hasSessionOverridesChanged = !!sessionOverrides && !deepEqual(sessionOverrides, storedSessionMetaOverrides);\n const hasAttributesChanged = !!sessionAttributes && !deepEqual(sessionAttributes, storedSessionMeta?.attributes);\n const hasSessionIdChanged = !!session && sessionId !== sessionFromSessionStorage?.sessionId;\n\n if (hasSessionIdChanged || hasAttributesChanged || hasSessionOverridesChanged) {\n const userSession = addSessionMetadataToNextSession(\n createUserSessionObject({ sessionId, isSampled: isSampled() }),\n sessionFromSessionStorage\n );\n\n storeUserSession(userSession);\n sendOverrideEvent(hasSessionOverridesChanged, sessionOverrides, storedSessionMetaOverrides);\n\n isSyncing = true;\n try {\n faro.api.setSession(userSession.sessionMeta);\n } finally {\n isSyncing = false;\n }\n }\n };\n}\n\nfunction removeUndefinedValues(obj: Record<string, string | undefined>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const key of Object.keys(obj)) {\n const value = obj[key];\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction sendOverrideEvent(\n hasSessionOverridesChanged: boolean,\n sessionOverrides: MetaOverrides = {},\n storedSessionOverrides: MetaOverrides = {}\n) {\n if (!hasSessionOverridesChanged) {\n return;\n }\n\n const serviceName = sessionOverrides.serviceName;\n const previousServiceName = storedSessionOverrides.serviceName ?? faro.metas.value.app?.name ?? '';\n\n if (serviceName && serviceName !== previousServiceName) {\n faro.api.pushEvent(EVENT_OVERRIDES_SERVICE_NAME, {\n serviceName,\n previousServiceName,\n });\n }\n}\n"]}
@@ -8,11 +8,18 @@ export class SessionInstrumentation extends BaseInstrumentation {
8
8
  super(...arguments);
9
9
  this.name = '@grafana/faro-web-sdk:instrumentation-session';
10
10
  this.version = VERSION;
11
+ // Reads the session manager's adoption flag (set once the manager exists).
12
+ this.isAdoptingSession = () => false;
11
13
  }
12
14
  sendSessionStartEvent(meta) {
13
15
  var _a, _b;
14
16
  const session = meta.session;
15
17
  if (session && session.id !== ((_a = this.notifiedSession) === null || _a === void 0 ? void 0 : _a.id)) {
18
+ // Adopting another tab's session: track it but emit nothing (the creating tab already did).
19
+ if (this.isAdoptingSession()) {
20
+ this.notifiedSession = session;
21
+ return;
22
+ }
16
23
  if (this.notifiedSession && this.notifiedSession.id === ((_b = session.attributes) === null || _b === void 0 ? void 0 : _b['previousSession'])) {
17
24
  this.api.pushEvent(EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });
18
25
  this.notifiedSession = session;
@@ -67,17 +74,35 @@ export class SessionInstrumentation extends BaseInstrumentation {
67
74
  }
68
75
  registerBeforeSendHook(SessionManager) {
69
76
  var _a;
70
- const { updateSession } = new SessionManager();
77
+ const sessionManager = new SessionManager();
78
+ this.isAdoptingSession = sessionManager.isAdopting;
79
+ const { updateSession } = sessionManager;
80
+ // Most recent rotation updateSession performed. A batch buffered before the
81
+ // rotation is all stamped with the now-expired id, not just the triggering item.
82
+ let lastRotation;
71
83
  (_a = this.transports) === null || _a === void 0 ? void 0 : _a.addBeforeSendHooks((item) => {
72
- var _a, _b, _c;
84
+ var _a, _b, _c, _d;
85
+ const previousSessionId = (_a = this.metas.value.session) === null || _a === void 0 ? void 0 : _a.id;
73
86
  updateSession();
74
- const attributes = (_a = item.meta.session) === null || _a === void 0 ? void 0 : _a.attributes;
87
+ const currentSession = this.metas.value.session;
88
+ if (currentSession != null && previousSessionId != null && currentSession.id !== previousSessionId) {
89
+ lastRotation = { from: previousSessionId, to: currentSession };
90
+ }
91
+ // Re-stamp items still carrying the rotated-from session. Keyed on that id,
92
+ // so items from a genuinely earlier session (explicit setSession, which
93
+ // updateSession never rotates) keep their own sampling decision.
94
+ const reStamp = lastRotation != null && ((_b = item.meta.session) === null || _b === void 0 ? void 0 : _b.id) === lastRotation.from;
95
+ const session = reStamp ? lastRotation.to : item.meta.session;
96
+ const attributes = session === null || session === void 0 ? void 0 : session.attributes;
75
97
  if (attributes && (attributes === null || attributes === void 0 ? void 0 : attributes['isSampled']) === 'true') {
76
98
  let newItem = JSON.parse(JSON.stringify(item));
77
- const newAttributes = (_b = newItem.meta.session) === null || _b === void 0 ? void 0 : _b.attributes;
99
+ if (reStamp) {
100
+ newItem.meta.session = JSON.parse(JSON.stringify(lastRotation.to));
101
+ }
102
+ const newAttributes = (_c = newItem.meta.session) === null || _c === void 0 ? void 0 : _c.attributes;
78
103
  newAttributes === null || newAttributes === void 0 ? true : delete newAttributes['isSampled'];
79
104
  if (Object.keys(newAttributes !== null && newAttributes !== void 0 ? newAttributes : {}).length === 0) {
80
- (_c = newItem.meta.session) === null || _c === void 0 ? true : delete _c.attributes;
105
+ (_d = newItem.meta.session) === null || _d === void 0 ? true : delete _d.attributes;
81
106
  }
82
107
  return newItem;
83
108
  }
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseInstrumentation,\n dateNow,\n EVENT_SESSION_EXTEND,\n EVENT_SESSION_RESUME,\n EVENT_SESSION_START,\n VERSION,\n} from '@grafana/faro-core';\nimport type { Config, Meta, MetaSession } from '@grafana/faro-core';\n\nimport type { TransportItem } from '../..';\nimport { createSession } from '../../metas';\n\nimport { type FaroUserSession, getSessionManagerByConfig, isSampled } from './sessionManager';\nimport { PersistentSessionsManager } from './sessionManager/PersistentSessionsManager';\nimport { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';\nimport type { SessionManager } from './sessionManager/types';\n\ntype LifecycleType = typeof EVENT_SESSION_RESUME | typeof EVENT_SESSION_START;\n\nexport class SessionInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-web-sdk:instrumentation-session';\n readonly version = VERSION;\n\n // previously notified session, to ensure we don't send session start\n // event twice for the same session\n private notifiedSession: MetaSession | undefined;\n\n private sendSessionStartEvent(meta: Meta): void {\n const session = meta.session;\n\n if (session && session.id !== this.notifiedSession?.id) {\n if (this.notifiedSession && this.notifiedSession.id === session.attributes?.['previousSession']) {\n this.api.pushEvent(EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });\n this.notifiedSession = session;\n return;\n }\n\n this.notifiedSession = session;\n // no need to add attributes and session id, they are included as part of meta\n // automatically\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n }\n\n private createInitialSession(\n SessionManager: SessionManager,\n sessionsConfig: Required<Config>['sessionTracking']\n ): {\n initialSession: FaroUserSession;\n lifecycleType: LifecycleType;\n } {\n let storedUserSession: FaroUserSession | null = SessionManager.fetchUserSession();\n\n if (sessionsConfig.persistent && sessionsConfig.maxSessionPersistenceTime && storedUserSession) {\n const now = dateNow();\n const shouldClearPersistentSession =\n storedUserSession.lastActivity < now - sessionsConfig.maxSessionPersistenceTime;\n\n if (shouldClearPersistentSession) {\n PersistentSessionsManager.removeUserSession();\n storedUserSession = null;\n }\n }\n\n let lifecycleType: LifecycleType;\n let initialSession: FaroUserSession;\n\n if (isUserSessionValid(storedUserSession)) {\n const sessionId = storedUserSession?.sessionId;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: storedUserSession!.isSampled || false,\n started: storedUserSession?.started,\n });\n\n const storedUserSessionMeta = storedUserSession?.sessionMeta;\n\n // For resumed sessions we want to merge the previous overrides with the configured ones.\n // If the same key is present in both, the new one will override the old one.\n const overrides = { ...sessionsConfig.session?.overrides, ...storedUserSessionMeta?.overrides };\n\n initialSession.sessionMeta = {\n ...sessionsConfig.session,\n id: sessionId,\n attributes: {\n ...sessionsConfig.session?.attributes,\n ...storedUserSessionMeta?.attributes,\n // For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.\n isSampled: initialSession.isSampled.toString(),\n },\n overrides,\n };\n\n lifecycleType = EVENT_SESSION_RESUME;\n } else {\n const sessionId = sessionsConfig.session?.id ?? createSession().id;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: isSampled(),\n });\n\n const overrides = sessionsConfig.session?.overrides;\n\n initialSession.sessionMeta = {\n id: sessionId,\n attributes: {\n isSampled: initialSession.isSampled.toString(),\n ...sessionsConfig.session?.attributes,\n },\n // new session we don't care about previous overrides\n ...(overrides ? { overrides } : {}),\n };\n\n lifecycleType = EVENT_SESSION_START;\n }\n\n return { initialSession, lifecycleType };\n }\n\n private registerBeforeSendHook(SessionManager: SessionManager) {\n const { updateSession } = new SessionManager();\n\n this.transports?.addBeforeSendHooks((item) => {\n updateSession();\n\n const attributes = item.meta.session?.attributes;\n\n if (attributes && attributes?.['isSampled'] === 'true') {\n let newItem: TransportItem = JSON.parse(JSON.stringify(item));\n\n const newAttributes = newItem.meta.session?.attributes;\n delete newAttributes?.['isSampled'];\n\n if (Object.keys(newAttributes ?? {}).length === 0) {\n delete newItem.meta.session?.attributes;\n }\n\n return newItem;\n }\n\n return null;\n });\n }\n\n initialize() {\n this.logDebug('init session instrumentation');\n\n const sessionTrackingConfig = this.config.sessionTracking;\n\n if (sessionTrackingConfig?.enabled) {\n const SessionManager = getSessionManagerByConfig(sessionTrackingConfig);\n\n this.registerBeforeSendHook(SessionManager);\n\n const { initialSession, lifecycleType } = this.createInitialSession(SessionManager, sessionTrackingConfig);\n\n SessionManager.storeUserSession(initialSession);\n\n const initialSessionMeta = initialSession.sessionMeta;\n\n this.notifiedSession = initialSessionMeta;\n this.api.setSession(initialSessionMeta);\n\n if (lifecycleType === EVENT_SESSION_START) {\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n\n if (lifecycleType === EVENT_SESSION_RESUME) {\n this.api.pushEvent(EVENT_SESSION_RESUME, {}, undefined, { skipDedupe: true });\n }\n }\n\n this.metas.addListener(this.sendSessionStartEvent.bind(this));\n }\n}\n"]}
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseInstrumentation,\n dateNow,\n EVENT_SESSION_EXTEND,\n EVENT_SESSION_RESUME,\n EVENT_SESSION_START,\n VERSION,\n} from '@grafana/faro-core';\nimport type { Config, Meta, MetaSession } from '@grafana/faro-core';\n\nimport type { TransportItem } from '../..';\nimport { createSession } from '../../metas';\n\nimport { type FaroUserSession, getSessionManagerByConfig, isSampled } from './sessionManager';\nimport { PersistentSessionsManager } from './sessionManager/PersistentSessionsManager';\nimport { createUserSessionObject, isUserSessionValid } from './sessionManager/sessionManagerUtils';\nimport type { SessionManager } from './sessionManager/types';\n\ntype LifecycleType = typeof EVENT_SESSION_RESUME | typeof EVENT_SESSION_START;\n\nexport class SessionInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-web-sdk:instrumentation-session';\n readonly version = VERSION;\n\n // previously notified session, to ensure we don't send session start\n // event twice for the same session\n private notifiedSession: MetaSession | undefined;\n\n // Reads the session manager's adoption flag (set once the manager exists).\n private isAdoptingSession: () => boolean = () => false;\n\n private sendSessionStartEvent(meta: Meta): void {\n const session = meta.session;\n\n if (session && session.id !== this.notifiedSession?.id) {\n // Adopting another tab's session: track it but emit nothing (the creating tab already did).\n if (this.isAdoptingSession()) {\n this.notifiedSession = session;\n return;\n }\n\n if (this.notifiedSession && this.notifiedSession.id === session.attributes?.['previousSession']) {\n this.api.pushEvent(EVENT_SESSION_EXTEND, {}, undefined, { skipDedupe: true });\n this.notifiedSession = session;\n return;\n }\n\n this.notifiedSession = session;\n // no need to add attributes and session id, they are included as part of meta\n // automatically\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n }\n\n private createInitialSession(\n SessionManager: SessionManager,\n sessionsConfig: Required<Config>['sessionTracking']\n ): {\n initialSession: FaroUserSession;\n lifecycleType: LifecycleType;\n } {\n let storedUserSession: FaroUserSession | null = SessionManager.fetchUserSession();\n\n if (sessionsConfig.persistent && sessionsConfig.maxSessionPersistenceTime && storedUserSession) {\n const now = dateNow();\n const shouldClearPersistentSession =\n storedUserSession.lastActivity < now - sessionsConfig.maxSessionPersistenceTime;\n\n if (shouldClearPersistentSession) {\n PersistentSessionsManager.removeUserSession();\n storedUserSession = null;\n }\n }\n\n let lifecycleType: LifecycleType;\n let initialSession: FaroUserSession;\n\n if (isUserSessionValid(storedUserSession)) {\n const sessionId = storedUserSession?.sessionId;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: storedUserSession!.isSampled || false,\n started: storedUserSession?.started,\n });\n\n const storedUserSessionMeta = storedUserSession?.sessionMeta;\n\n // For resumed sessions we want to merge the previous overrides with the configured ones.\n // If the same key is present in both, the new one will override the old one.\n const overrides = { ...sessionsConfig.session?.overrides, ...storedUserSessionMeta?.overrides };\n\n initialSession.sessionMeta = {\n ...sessionsConfig.session,\n id: sessionId,\n attributes: {\n ...sessionsConfig.session?.attributes,\n ...storedUserSessionMeta?.attributes,\n // For valid resumed sessions we do not want to recalculate the sampling decision on each init phase.\n isSampled: initialSession.isSampled.toString(),\n },\n overrides,\n };\n\n lifecycleType = EVENT_SESSION_RESUME;\n } else {\n const sessionId = sessionsConfig.session?.id ?? createSession().id;\n\n initialSession = createUserSessionObject({\n sessionId,\n isSampled: isSampled(),\n });\n\n const overrides = sessionsConfig.session?.overrides;\n\n initialSession.sessionMeta = {\n id: sessionId,\n attributes: {\n isSampled: initialSession.isSampled.toString(),\n ...sessionsConfig.session?.attributes,\n },\n // new session we don't care about previous overrides\n ...(overrides ? { overrides } : {}),\n };\n\n lifecycleType = EVENT_SESSION_START;\n }\n\n return { initialSession, lifecycleType };\n }\n\n private registerBeforeSendHook(SessionManager: SessionManager) {\n const sessionManager = new SessionManager();\n this.isAdoptingSession = sessionManager.isAdopting;\n const { updateSession } = sessionManager;\n\n // Most recent rotation updateSession performed. A batch buffered before the\n // rotation is all stamped with the now-expired id, not just the triggering item.\n let lastRotation: { from: string; to: MetaSession } | undefined;\n\n this.transports?.addBeforeSendHooks((item) => {\n const previousSessionId = this.metas.value.session?.id;\n updateSession();\n const currentSession = this.metas.value.session;\n\n if (currentSession != null && previousSessionId != null && currentSession.id !== previousSessionId) {\n lastRotation = { from: previousSessionId, to: currentSession };\n }\n\n // Re-stamp items still carrying the rotated-from session. Keyed on that id,\n // so items from a genuinely earlier session (explicit setSession, which\n // updateSession never rotates) keep their own sampling decision.\n const reStamp = lastRotation != null && item.meta.session?.id === lastRotation.from;\n const session = reStamp ? lastRotation!.to : item.meta.session;\n\n const attributes = session?.attributes;\n\n if (attributes && attributes?.['isSampled'] === 'true') {\n let newItem: TransportItem = JSON.parse(JSON.stringify(item));\n\n if (reStamp) {\n newItem.meta.session = JSON.parse(JSON.stringify(lastRotation!.to));\n }\n\n const newAttributes = newItem.meta.session?.attributes;\n delete newAttributes?.['isSampled'];\n\n if (Object.keys(newAttributes ?? {}).length === 0) {\n delete newItem.meta.session?.attributes;\n }\n\n return newItem;\n }\n\n return null;\n });\n }\n\n initialize() {\n this.logDebug('init session instrumentation');\n\n const sessionTrackingConfig = this.config.sessionTracking;\n\n if (sessionTrackingConfig?.enabled) {\n const SessionManager = getSessionManagerByConfig(sessionTrackingConfig);\n\n this.registerBeforeSendHook(SessionManager);\n\n const { initialSession, lifecycleType } = this.createInitialSession(SessionManager, sessionTrackingConfig);\n\n SessionManager.storeUserSession(initialSession);\n\n const initialSessionMeta = initialSession.sessionMeta;\n\n this.notifiedSession = initialSessionMeta;\n this.api.setSession(initialSessionMeta);\n\n if (lifecycleType === EVENT_SESSION_START) {\n this.api.pushEvent(EVENT_SESSION_START, {}, undefined, { skipDedupe: true });\n }\n\n if (lifecycleType === EVENT_SESSION_RESUME) {\n this.api.pushEvent(EVENT_SESSION_RESUME, {}, undefined, { skipDedupe: true });\n }\n }\n\n this.metas.addListener(this.sendSessionStartEvent.bind(this));\n }\n}\n"]}
@@ -5,10 +5,25 @@ import { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';
5
5
  import { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';
6
6
  export class PersistentSessionsManager {
7
7
  constructor() {
8
+ // Set only for the synchronous span of an adopting setSession(); the session
9
+ // instrumentation reads isAdopting() to suppress its lifecycle event.
10
+ this.adopting = false;
11
+ this.isAdopting = () => this.adopting;
12
+ this.adoptSession = (sessionMeta) => {
13
+ var _a;
14
+ this.adopting = true;
15
+ try {
16
+ (_a = faro.api) === null || _a === void 0 ? void 0 : _a.setSession(sessionMeta);
17
+ }
18
+ finally {
19
+ this.adopting = false;
20
+ }
21
+ };
8
22
  this.updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);
9
23
  this.updateUserSession = getUserSessionUpdater({
10
24
  fetchUserSession: PersistentSessionsManager.fetchUserSession,
11
25
  storeUserSession: PersistentSessionsManager.storeUserSession,
26
+ adoptSession: this.adoptSession,
12
27
  });
13
28
  this.init();
14
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PersistentSessionsManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/PersistentSessionsManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEzF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG3F,MAAM,OAAO,yBAAyB;IAIpC;QA2BA,kBAAa,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,oBAAoB,CAAC,CAAC;QA1B7E,IAAI,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;YAC7C,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;SAC7D,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,UAAU,CAAC,WAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,OAAO,CAAC,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,IAAI,CAAC,KAAK,CAAC,WAAW,CACpB,2BAA2B,CAAC;YAC1B,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;SAC7D,CAAC,CACH,CAAC;IACJ,CAAC;;AA9Cc,0CAAgB,GAAG,cAAc,CAAC,KAAK,AAAvB,CAAwB","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class PersistentSessionsManager {\n private static storageTypeLocal = webStorageType.local;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), PersistentSessionsManager.storageTypeLocal);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
1
+ {"version":3,"file":"PersistentSessionsManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/PersistentSessionsManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEzF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG3F,MAAM,OAAO,yBAAyB;IAmBpC;QAfA,6EAA6E;QAC7E,sEAAsE;QAC9D,aAAQ,GAAG,KAAK,CAAC;QAEzB,eAAU,GAAG,GAAY,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;QAElC,iBAAY,GAAG,CAAC,WAAwB,EAAQ,EAAE;;YACxD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC;gBACH,MAAA,IAAI,CAAC,GAAG,0CAAE,UAAU,CAAC,WAAW,CAAC,CAAC;YACpC,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACxB,CAAC;QACH,CAAC,CAAC;QA8BF,kBAAa,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,oBAAoB,CAAC,CAAC;QA3B7E,IAAI,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;YAC7C,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,UAAU,CAAC,WAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,OAAO,CAAC,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,IAAI,CAAC,KAAK,CAAC,WAAW,CACpB,2BAA2B,CAAC;YAC1B,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;YAC5D,gBAAgB,EAAE,yBAAyB,CAAC,gBAAgB;SAC7D,CAAC,CACH,CAAC;IACJ,CAAC;;AA9Dc,0CAAgB,GAAG,cAAc,CAAC,KAAK,AAAvB,CAAwB","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\nimport type { MetaSession } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class PersistentSessionsManager {\n private static storageTypeLocal = webStorageType.local;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n // Set only for the synchronous span of an adopting setSession(); the session\n // instrumentation reads isAdopting() to suppress its lifecycle event.\n private adopting = false;\n\n isAdopting = (): boolean => this.adopting;\n\n private adoptSession = (sessionMeta: MetaSession): void => {\n this.adopting = true;\n try {\n faro.api?.setSession(sessionMeta);\n } finally {\n this.adopting = false;\n }\n };\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n adoptSession: this.adoptSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), PersistentSessionsManager.storageTypeLocal);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, PersistentSessionsManager.storageTypeLocal);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: PersistentSessionsManager.fetchUserSession,\n storeUserSession: PersistentSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
@@ -5,6 +5,9 @@ import { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';
5
5
  import { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';
6
6
  export class VolatileSessionsManager {
7
7
  constructor() {
8
+ // sessionStorage is tab-local, so this manager never adopts another tab's
9
+ // session. Stubbed so the instrumentation can treat both managers uniformly.
10
+ this.isAdopting = () => false;
8
11
  this.updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);
9
12
  this.updateUserSession = getUserSessionUpdater({
10
13
  fetchUserSession: VolatileSessionsManager.fetchUserSession,
@@ -1 +1 @@
1
- {"version":3,"file":"VolatileSessionManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/VolatileSessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEzF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG3F,MAAM,OAAO,uBAAuB;IAIlC;QA2BA,kBAAa,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,oBAAoB,CAAC,CAAC;QA1B7E,IAAI,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;YAC7C,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,UAAU,CAAC,WAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,OAAO,CAAC,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,IAAI,CAAC,KAAK,CAAC,WAAW,CACpB,2BAA2B,CAAC;YAC1B,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CACH,CAAC;IACJ,CAAC;;AA9Cc,0CAAkB,GAAG,cAAc,CAAC,OAAO,AAAzB,CAA0B","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class VolatileSessionsManager {\n private static storageTypeSession = webStorageType.session;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), VolatileSessionsManager.storageTypeSession);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
1
+ {"version":3,"file":"VolatileSessionManager.js","sourceRoot":"","sources":["../../../../../src/instrumentations/session/sessionManager/VolatileSessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEzF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG3F,MAAM,OAAO,uBAAuB;IAQlC;QAJA,0EAA0E;QAC1E,6EAA6E;QAC7E,eAAU,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC;QA6BlC,kBAAa,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,oBAAoB,CAAC,CAAC;QA1B7E,IAAI,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;YAC7C,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,MAAM,CAAC,iBAAiB;QACtB,UAAU,CAAC,WAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAwB;QAC9C,OAAO,CAAC,WAAW,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,CAAC,gBAAgB;QACrB,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;QAEvF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAoB,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAIO,IAAI;QACV,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,iGAAiG;QACjG,IAAI,CAAC,KAAK,CAAC,WAAW,CACpB,2BAA2B,CAAC;YAC1B,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;YAC1D,gBAAgB,EAAE,uBAAuB,CAAC,gBAAgB;SAC3D,CAAC,CACH,CAAC;IACJ,CAAC;;AAlDc,0CAAkB,GAAG,cAAc,CAAC,OAAO,AAAzB,CAA0B","sourcesContent":["import { faro, stringifyExternalJson } from '@grafana/faro-core';\n\nimport { throttle } from '../../../utils';\nimport { getItem, removeItem, setItem, webStorageType } from '../../../utils/webStorage';\n\nimport { STORAGE_KEY, STORAGE_UPDATE_DELAY } from './sessionConstants';\nimport { getSessionMetaUpdateHandler, getUserSessionUpdater } from './sessionManagerUtils';\nimport type { FaroUserSession } from './types';\n\nexport class VolatileSessionsManager {\n private static storageTypeSession = webStorageType.session;\n private updateUserSession: ReturnType<typeof getUserSessionUpdater>;\n\n // sessionStorage is tab-local, so this manager never adopts another tab's\n // session. Stubbed so the instrumentation can treat both managers uniformly.\n isAdopting = (): boolean => false;\n\n constructor() {\n this.updateUserSession = getUserSessionUpdater({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n });\n\n this.init();\n }\n\n static removeUserSession() {\n removeItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n }\n\n static storeUserSession(session: FaroUserSession): void {\n setItem(STORAGE_KEY, stringifyExternalJson(session), VolatileSessionsManager.storageTypeSession);\n }\n\n static fetchUserSession(): FaroUserSession | null {\n const storedSession = getItem(STORAGE_KEY, VolatileSessionsManager.storageTypeSession);\n\n if (storedSession) {\n return JSON.parse(storedSession) as FaroUserSession;\n }\n\n return null;\n }\n\n updateSession = throttle(() => this.updateUserSession(), STORAGE_UPDATE_DELAY);\n\n private init(): void {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.updateSession();\n }\n });\n\n // Users can call the setSession() method, so we need to sync this with the local storage session\n faro.metas.addListener(\n getSessionMetaUpdateHandler({\n fetchUserSession: VolatileSessionsManager.fetchUserSession,\n storeUserSession: VolatileSessionsManager.storeUserSession,\n })\n );\n }\n}\n"]}
@@ -28,9 +28,9 @@ export function isUserSessionValid(session) {
28
28
  const inactivityPeriodValid = now - session.lastActivity < SESSION_INACTIVITY_TIME;
29
29
  return inactivityPeriodValid;
30
30
  }
31
- export function getUserSessionUpdater({ fetchUserSession, storeUserSession, }) {
31
+ export function getUserSessionUpdater({ fetchUserSession, storeUserSession, adoptSession, }) {
32
32
  return function updateSession({ forceSessionExtend } = { forceSessionExtend: false }) {
33
- var _a, _b, _c;
33
+ var _a, _b, _c, _d;
34
34
  if (!fetchUserSession || !storeUserSession) {
35
35
  return;
36
36
  }
@@ -42,12 +42,19 @@ export function getUserSessionUpdater({ fetchUserSession, storeUserSession, }) {
42
42
  const sessionFromStorage = fetchUserSession();
43
43
  if (forceSessionExtend === false && isUserSessionValid(sessionFromStorage)) {
44
44
  storeUserSession(Object.assign(Object.assign({}, sessionFromStorage), { lastActivity: dateNow() }));
45
+ // Another tab rotated the shared session; adopt it so we stop emitting the stale id.
46
+ const inMemorySessionId = (_a = faro.metas.value.session) === null || _a === void 0 ? void 0 : _a.id;
47
+ if (adoptSession != null &&
48
+ sessionFromStorage.sessionMeta != null &&
49
+ sessionFromStorage.sessionId !== inMemorySessionId) {
50
+ adoptSession(sessionFromStorage.sessionMeta);
51
+ }
45
52
  }
46
53
  else {
47
54
  let newSession = addSessionMetadataToNextSession(createUserSessionObject({ isSampled: isSampled() }), sessionFromStorage);
48
55
  storeUserSession(newSession);
49
- (_a = faro.api) === null || _a === void 0 ? void 0 : _a.setSession(newSession.sessionMeta);
50
- (_b = sessionTrackingConfig === null || sessionTrackingConfig === void 0 ? void 0 : sessionTrackingConfig.onSessionChange) === null || _b === void 0 ? void 0 : _b.call(sessionTrackingConfig, (_c = sessionFromStorage === null || sessionFromStorage === void 0 ? void 0 : sessionFromStorage.sessionMeta) !== null && _c !== void 0 ? _c : null, newSession.sessionMeta);
56
+ (_b = faro.api) === null || _b === void 0 ? void 0 : _b.setSession(newSession.sessionMeta);
57
+ (_c = sessionTrackingConfig === null || sessionTrackingConfig === void 0 ? void 0 : sessionTrackingConfig.onSessionChange) === null || _c === void 0 ? void 0 : _c.call(sessionTrackingConfig, (_d = sessionFromStorage === null || sessionFromStorage === void 0 ? void 0 : sessionFromStorage.sessionMeta) !== null && _d !== void 0 ? _d : null, newSession.sessionMeta);
51
58
  }
52
59
  };
53
60
  }