@safaricom-mxl/web-sdk 0.0.20 → 0.0.21

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 (38) hide show
  1. package/DATA_EXPORT.md +2 -1
  2. package/dist/instrumentations/replay/session-recording/SessionRecording.cjs +1 -0
  3. package/dist/instrumentations/replay/session-recording/SessionRecording.cjs.map +1 -1
  4. package/dist/instrumentations/replay/session-recording/SessionRecording.js +1 -0
  5. package/dist/instrumentations/replay/session-recording/SessionRecording.js.map +1 -1
  6. package/dist/instrumentations/replay/transport/replayTransport.cjs +21 -4
  7. package/dist/instrumentations/replay/transport/replayTransport.cjs.map +1 -1
  8. package/dist/instrumentations/replay/transport/replayTransport.d.cts +1 -1
  9. package/dist/instrumentations/replay/transport/replayTransport.d.cts.map +1 -1
  10. package/dist/instrumentations/replay/transport/replayTransport.d.ts +1 -1
  11. package/dist/instrumentations/replay/transport/replayTransport.d.ts.map +1 -1
  12. package/dist/instrumentations/replay/transport/replayTransport.js +21 -4
  13. package/dist/instrumentations/replay/transport/replayTransport.js.map +1 -1
  14. package/dist/instrumentations/replay/transport/types.d.cts +2 -1
  15. package/dist/instrumentations/replay/transport/types.d.cts.map +1 -1
  16. package/dist/instrumentations/replay/transport/types.d.ts +2 -1
  17. package/dist/instrumentations/replay/transport/types.d.ts.map +1 -1
  18. package/dist/mxl-web-sdk.js +2 -2
  19. package/dist/mxl-web-sdk.js.map +1 -1
  20. package/dist/resources/sdkConstants.cjs +1 -1
  21. package/dist/resources/sdkConstants.cjs.map +1 -1
  22. package/dist/resources/sdkConstants.d.cts +1 -1
  23. package/dist/resources/sdkConstants.d.ts +1 -1
  24. package/dist/resources/sdkConstants.js +1 -1
  25. package/dist/resources/sdkConstants.js.map +1 -1
  26. package/dist/sdk/initSDK.cjs +3 -3
  27. package/dist/sdk/initSDK.cjs.map +1 -1
  28. package/dist/sdk/initSDK.d.cts +1 -0
  29. package/dist/sdk/initSDK.d.cts.map +1 -1
  30. package/dist/sdk/initSDK.d.ts +1 -0
  31. package/dist/sdk/initSDK.d.ts.map +1 -1
  32. package/dist/sdk/initSDK.js +3 -3
  33. package/dist/sdk/initSDK.js.map +1 -1
  34. package/dist/sdk/types.d.cts +6 -0
  35. package/dist/sdk/types.d.cts.map +1 -1
  36. package/dist/sdk/types.d.ts +6 -0
  37. package/dist/sdk/types.d.ts.map +1 -1
  38. package/package.json +1 -1
package/DATA_EXPORT.md CHANGED
@@ -22,11 +22,12 @@ failures following an exponential backoff. See [FetchTransport](./src/transport/
22
22
 
23
23
  ## Session replay
24
24
 
25
- When `recordUserSessions` is enabled (default) and the SDK sends data to MXL, session replay uses **three dedicated HTTP ingest paths** (gzip NDJSON or octet-stream), separate from OTLP spans and logs:
25
+ When `recordUserSessions` is enabled (default) and the SDK sends data to MXL, session replay uses **dedicated HTTP ingest paths** (gzip NDJSON or octet-stream), separate from OTLP spans and logs. All replay ingest requests include the `mxl-client-id` header set to the init `appID`.
26
26
 
27
27
 
28
28
  | Endpoint | Payload |
29
29
  | --------------------------- | -------------------------------------------------------------------------------------------------------- |
30
+ | `POST /replay` | Session registration at session start when `apiKey` is configured (`mxl-api-key` header, body `null`) |
30
31
  | `POST /sessionReplays/meta` | Session metadata (`active` on start, `ended` on finalize with `trace_ids`, `click_count`, `duration_ms`) |
31
32
  | `POST /sessionReplays/blob` | Gzip-compressed rrweb event chunks (`x-mxl-session-id`, `x-mxl-chunk-seq`, `x-mxl-is-checkpoint`, …) |
32
33
  | `POST /sessionEvents` | Distilled timeline events (console, network, errors, navigation, interactions) |
@@ -136,6 +136,7 @@ var SessionRecording = class {
136
136
  sessionId,
137
137
  transport: this._context.transport
138
138
  });
139
+ this._context.transport.postReplay();
139
140
  this._context.transport.postSessionMeta(require_instrumentations_replay_session_recording_lifecycle.sessionMetaRow(this._config, sessionId, startedAt, 1, "active", null));
140
141
  const removeLifecycle = globalThis.__MXL_TEST_SKIP_REPLAY_LIFECYCLE__ === true ? () => void 0 : require_instrumentations_replay_session_recording_lifecycle.installReplayLifecycleHandlers({
141
142
  config: this._config,
@@ -1 +1 @@
1
- {"version":3,"file":"SessionRecording.cjs","names":["createChunkSeqStore","resolveReplayConfig","loadRrwebModule","getSessionStartedAt","startRecording","startEventCapture","sessionMetaRow","installReplayLifecycleHandlers"],"sources":["../../../../src/instrumentations/replay/session-recording/SessionRecording.ts"],"sourcesContent":["import type { DiagLogger } from '@opentelemetry/api';\nimport type { UserSessionManagerInternal } from '../../../managers/MXLUserSessionManager/types';\nimport type { NamespacedStorage } from '../../../utils/NamespacedStorage/NamespacedStorage';\nimport { startEventCapture } from '../events/startEventCapture';\nimport { loadRrwebModule } from '../rrwebLoader/loadRrwebModule';\nimport type { RrwebModule } from '../rrwebLoader/types';\nimport { startRecording } from '../recorder/startRecording';\nimport type { Recorder } from '../recorder/types';\nimport {\n clearObservedTraceIds,\n clearReplaySessionSink,\n publishReplaySessionSink,\n} from '../trace-sink/traceSink';\nimport { createChunkSeqStore } from './chunkSeq';\nimport {\n type ReplayContext,\n type ResolvedReplayConfig,\n resolveReplayConfig,\n} from './config';\nimport type { EventCapture } from '../events/startEventCapture';\nimport {\n getSessionStartedAt,\n installReplayLifecycleHandlers,\n sessionMetaRow,\n} from './lifecycle';\n\nexport interface SessionRecordingArgs {\n context: ReplayContext;\n userSessionManager: UserSessionManagerInternal;\n sessionStorage: NamespacedStorage;\n rrwebOptions?: ResolvedReplayConfig['rrwebOptions'] & {\n debug?: boolean;\n enabled?: boolean;\n sampleRate?: number;\n maskAllInputs?: boolean;\n maskAllText?: boolean;\n flushIntervalMs?: number;\n };\n recordUserSessions?: boolean;\n diag?: DiagLogger;\n}\n\ninterface ActiveRecording {\n recorder: Recorder;\n events: EventCapture;\n rrwebModule: RrwebModule;\n removeLifecycle: () => void;\n removeSessionEndListener?: () => void;\n}\n\nexport class SessionRecording {\n private readonly _context: ReplayContext;\n private readonly _userSessionManager: UserSessionManagerInternal;\n private readonly _chunkSeqStore: ReturnType<typeof createChunkSeqStore>;\n private readonly _config: ResolvedReplayConfig;\n private readonly _diag: DiagLogger;\n private readonly _recordUserSessions: boolean;\n\n private _active: ActiveRecording | undefined;\n private _paused = false;\n private _enableRequested = false;\n private _startInProgress = false;\n private _stopInProgress = false;\n private _removeSessionStartedListener?: () => void;\n\n public constructor({\n context,\n userSessionManager,\n sessionStorage,\n rrwebOptions,\n recordUserSessions = true,\n diag,\n }: SessionRecordingArgs) {\n this._context = context;\n this._userSessionManager = userSessionManager;\n this._chunkSeqStore = createChunkSeqStore(sessionStorage);\n this._recordUserSessions = recordUserSessions;\n this._config = resolveReplayConfig(\n context,\n rrwebOptions,\n recordUserSessions,\n );\n this._diag = diag ?? {\n debug: () => undefined,\n error: () => undefined,\n info: () => undefined,\n verbose: () => undefined,\n warn: () => undefined,\n };\n }\n\n public isRecording(): boolean {\n return Boolean(this._active) && !this._paused;\n }\n\n public enable(options?: { force?: boolean }): void {\n if (!this._recordUserSessions && !options?.force) {\n this._diag.debug(\n 'recordUserSessions disabled, waiting for manual enable',\n );\n return;\n }\n\n this._enableRequested = true;\n this._paused = false;\n void this._tryStart();\n }\n\n public pause(): void {\n if (!this._active) {\n return;\n }\n this._enableRequested = false;\n this._paused = true;\n this._active.recorder.pause();\n this._active.events.pause();\n }\n\n public resume(): void {\n if (!this._active) {\n this.enable();\n return;\n }\n\n const wasPaused = this._paused;\n this._enableRequested = true;\n this._paused = false;\n this._active.recorder.resume();\n this._active.events.resume();\n if (wasPaused) {\n this._active.rrwebModule.record.takeFullSnapshot();\n }\n }\n\n public async stop(): Promise<void> {\n this._enableRequested = false;\n this._paused = false;\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n\n if (!this._active) {\n clearReplaySessionSink();\n clearObservedTraceIds();\n return;\n }\n\n if (this._stopInProgress) {\n return;\n }\n\n this._stopInProgress = true;\n\n const active = this._active;\n this._active = undefined;\n\n try {\n active.removeSessionEndListener?.();\n active.removeLifecycle();\n\n await active.recorder.flush(true);\n await active.events.flush(true);\n active.recorder.stop();\n active.events.stop();\n } finally {\n clearReplaySessionSink();\n clearObservedTraceIds();\n this._stopInProgress = false;\n }\n }\n\n private async _tryStart(): Promise<void> {\n if (\n !this._enableRequested ||\n this._active ||\n this._startInProgress ||\n this._stopInProgress\n ) {\n return;\n }\n\n if (typeof window === 'undefined') {\n this._diag.warn('session replay cannot run in non-browser environments');\n return;\n }\n\n const sessionId = this._userSessionManager.getUserSessionId();\n if (sessionId === null) {\n if (!this._removeSessionStartedListener) {\n this._removeSessionStartedListener =\n this._userSessionManager.addSessionPartStartedListener(() => {\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n void this._tryStart();\n });\n }\n return;\n }\n\n if (!this._config.replayEnabled) {\n return;\n }\n\n if (Math.random() >= this._config.replaySampleRate) {\n this._diag.debug('session replay skipped by sample rate');\n return;\n }\n\n this._startInProgress = true;\n\n try {\n const rrwebModule = await loadRrwebModule();\n if (!this._enableRequested || this._active) {\n return;\n }\n\n publishReplaySessionSink(sessionId);\n clearObservedTraceIds();\n\n const startedAt = getSessionStartedAt(this._userSessionManager);\n const onActivity = () => {\n // Activity is tracked via MXL user session manager; no-op hook for now.\n };\n\n const recorder = startRecording({\n chunkSeqStore: this._chunkSeqStore,\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n rrwebModule,\n sessionId,\n transport: this._context.transport,\n });\n\n const events = startEventCapture({\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n sessionId,\n transport: this._context.transport,\n });\n\n void this._context.transport.postSessionMeta(\n sessionMetaRow(this._config, sessionId, startedAt, 1, 'active', null),\n );\n\n const skipLifecycle = (\n globalThis as Record<string, unknown>\n ).__MXL_TEST_SKIP_REPLAY_LIFECYCLE__;\n\n const removeLifecycle =\n skipLifecycle === true\n ? () => undefined\n : installReplayLifecycleHandlers({\n config: this._config,\n events,\n onFinalize: () => {\n this._active = undefined;\n clearReplaySessionSink();\n clearObservedTraceIds();\n },\n recorder,\n sessionId,\n startedAt,\n transport: this._context.transport,\n });\n\n const removeSessionEndListener =\n this._userSessionManager.addSessionPartEndedListener(() => {\n void recorder.flush(true);\n void events.flush(true);\n });\n\n this._active = {\n events,\n recorder,\n removeLifecycle,\n removeSessionEndListener,\n rrwebModule,\n };\n\n this._diag.debug('session recording started');\n } catch (error) {\n this._diag.error('failed to start session recording', error);\n } finally {\n this._startInProgress = false;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SessionRecording.cjs","names":["createChunkSeqStore","resolveReplayConfig","loadRrwebModule","getSessionStartedAt","startRecording","startEventCapture","sessionMetaRow","installReplayLifecycleHandlers"],"sources":["../../../../src/instrumentations/replay/session-recording/SessionRecording.ts"],"sourcesContent":["import type { DiagLogger } from '@opentelemetry/api';\nimport type { UserSessionManagerInternal } from '../../../managers/MXLUserSessionManager/types';\nimport type { NamespacedStorage } from '../../../utils/NamespacedStorage/NamespacedStorage';\nimport { startEventCapture } from '../events/startEventCapture';\nimport { loadRrwebModule } from '../rrwebLoader/loadRrwebModule';\nimport type { RrwebModule } from '../rrwebLoader/types';\nimport { startRecording } from '../recorder/startRecording';\nimport type { Recorder } from '../recorder/types';\nimport {\n clearObservedTraceIds,\n clearReplaySessionSink,\n publishReplaySessionSink,\n} from '../trace-sink/traceSink';\nimport { createChunkSeqStore } from './chunkSeq';\nimport {\n type ReplayContext,\n type ResolvedReplayConfig,\n resolveReplayConfig,\n} from './config';\nimport type { EventCapture } from '../events/startEventCapture';\nimport {\n getSessionStartedAt,\n installReplayLifecycleHandlers,\n sessionMetaRow,\n} from './lifecycle';\n\nexport interface SessionRecordingArgs {\n context: ReplayContext;\n userSessionManager: UserSessionManagerInternal;\n sessionStorage: NamespacedStorage;\n rrwebOptions?: ResolvedReplayConfig['rrwebOptions'] & {\n debug?: boolean;\n enabled?: boolean;\n sampleRate?: number;\n maskAllInputs?: boolean;\n maskAllText?: boolean;\n flushIntervalMs?: number;\n };\n recordUserSessions?: boolean;\n diag?: DiagLogger;\n}\n\ninterface ActiveRecording {\n recorder: Recorder;\n events: EventCapture;\n rrwebModule: RrwebModule;\n removeLifecycle: () => void;\n removeSessionEndListener?: () => void;\n}\n\nexport class SessionRecording {\n private readonly _context: ReplayContext;\n private readonly _userSessionManager: UserSessionManagerInternal;\n private readonly _chunkSeqStore: ReturnType<typeof createChunkSeqStore>;\n private readonly _config: ResolvedReplayConfig;\n private readonly _diag: DiagLogger;\n private readonly _recordUserSessions: boolean;\n\n private _active: ActiveRecording | undefined;\n private _paused = false;\n private _enableRequested = false;\n private _startInProgress = false;\n private _stopInProgress = false;\n private _removeSessionStartedListener?: () => void;\n\n public constructor({\n context,\n userSessionManager,\n sessionStorage,\n rrwebOptions,\n recordUserSessions = true,\n diag,\n }: SessionRecordingArgs) {\n this._context = context;\n this._userSessionManager = userSessionManager;\n this._chunkSeqStore = createChunkSeqStore(sessionStorage);\n this._recordUserSessions = recordUserSessions;\n this._config = resolveReplayConfig(\n context,\n rrwebOptions,\n recordUserSessions,\n );\n this._diag = diag ?? {\n debug: () => undefined,\n error: () => undefined,\n info: () => undefined,\n verbose: () => undefined,\n warn: () => undefined,\n };\n }\n\n public isRecording(): boolean {\n return Boolean(this._active) && !this._paused;\n }\n\n public enable(options?: { force?: boolean }): void {\n if (!this._recordUserSessions && !options?.force) {\n this._diag.debug(\n 'recordUserSessions disabled, waiting for manual enable',\n );\n return;\n }\n\n this._enableRequested = true;\n this._paused = false;\n void this._tryStart();\n }\n\n public pause(): void {\n if (!this._active) {\n return;\n }\n this._enableRequested = false;\n this._paused = true;\n this._active.recorder.pause();\n this._active.events.pause();\n }\n\n public resume(): void {\n if (!this._active) {\n this.enable();\n return;\n }\n\n const wasPaused = this._paused;\n this._enableRequested = true;\n this._paused = false;\n this._active.recorder.resume();\n this._active.events.resume();\n if (wasPaused) {\n this._active.rrwebModule.record.takeFullSnapshot();\n }\n }\n\n public async stop(): Promise<void> {\n this._enableRequested = false;\n this._paused = false;\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n\n if (!this._active) {\n clearReplaySessionSink();\n clearObservedTraceIds();\n return;\n }\n\n if (this._stopInProgress) {\n return;\n }\n\n this._stopInProgress = true;\n\n const active = this._active;\n this._active = undefined;\n\n try {\n active.removeSessionEndListener?.();\n active.removeLifecycle();\n\n await active.recorder.flush(true);\n await active.events.flush(true);\n active.recorder.stop();\n active.events.stop();\n } finally {\n clearReplaySessionSink();\n clearObservedTraceIds();\n this._stopInProgress = false;\n }\n }\n\n private async _tryStart(): Promise<void> {\n if (\n !this._enableRequested ||\n this._active ||\n this._startInProgress ||\n this._stopInProgress\n ) {\n return;\n }\n\n if (typeof window === 'undefined') {\n this._diag.warn('session replay cannot run in non-browser environments');\n return;\n }\n\n const sessionId = this._userSessionManager.getUserSessionId();\n if (sessionId === null) {\n if (!this._removeSessionStartedListener) {\n this._removeSessionStartedListener =\n this._userSessionManager.addSessionPartStartedListener(() => {\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n void this._tryStart();\n });\n }\n return;\n }\n\n if (!this._config.replayEnabled) {\n return;\n }\n\n if (Math.random() >= this._config.replaySampleRate) {\n this._diag.debug('session replay skipped by sample rate');\n return;\n }\n\n this._startInProgress = true;\n\n try {\n const rrwebModule = await loadRrwebModule();\n if (!this._enableRequested || this._active) {\n return;\n }\n\n publishReplaySessionSink(sessionId);\n clearObservedTraceIds();\n\n const startedAt = getSessionStartedAt(this._userSessionManager);\n const onActivity = () => {\n // Activity is tracked via MXL user session manager; no-op hook for now.\n };\n\n const recorder = startRecording({\n chunkSeqStore: this._chunkSeqStore,\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n rrwebModule,\n sessionId,\n transport: this._context.transport,\n });\n\n const events = startEventCapture({\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n sessionId,\n transport: this._context.transport,\n });\n\n void this._context.transport.postReplay();\n void this._context.transport.postSessionMeta(\n sessionMetaRow(this._config, sessionId, startedAt, 1, 'active', null),\n );\n\n const skipLifecycle = (\n globalThis as Record<string, unknown>\n ).__MXL_TEST_SKIP_REPLAY_LIFECYCLE__;\n\n const removeLifecycle =\n skipLifecycle === true\n ? () => undefined\n : installReplayLifecycleHandlers({\n config: this._config,\n events,\n onFinalize: () => {\n this._active = undefined;\n clearReplaySessionSink();\n clearObservedTraceIds();\n },\n recorder,\n sessionId,\n startedAt,\n transport: this._context.transport,\n });\n\n const removeSessionEndListener =\n this._userSessionManager.addSessionPartEndedListener(() => {\n void recorder.flush(true);\n void events.flush(true);\n });\n\n this._active = {\n events,\n recorder,\n removeLifecycle,\n removeSessionEndListener,\n rrwebModule,\n };\n\n this._diag.debug('session recording started');\n } catch (error) {\n this._diag.error('failed to start session recording', error);\n } finally {\n this._startInProgress = false;\n }\n }\n}\n"],"mappings":"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"}
@@ -135,6 +135,7 @@ var SessionRecording = class {
135
135
  sessionId,
136
136
  transport: this._context.transport
137
137
  });
138
+ this._context.transport.postReplay();
138
139
  this._context.transport.postSessionMeta(sessionMetaRow(this._config, sessionId, startedAt, 1, "active", null));
139
140
  const removeLifecycle = globalThis.__MXL_TEST_SKIP_REPLAY_LIFECYCLE__ === true ? () => void 0 : installReplayLifecycleHandlers({
140
141
  config: this._config,
@@ -1 +1 @@
1
- {"version":3,"file":"SessionRecording.js","names":[],"sources":["../../../../src/instrumentations/replay/session-recording/SessionRecording.ts"],"sourcesContent":["import type { DiagLogger } from '@opentelemetry/api';\nimport type { UserSessionManagerInternal } from '../../../managers/MXLUserSessionManager/types';\nimport type { NamespacedStorage } from '../../../utils/NamespacedStorage/NamespacedStorage';\nimport { startEventCapture } from '../events/startEventCapture';\nimport { loadRrwebModule } from '../rrwebLoader/loadRrwebModule';\nimport type { RrwebModule } from '../rrwebLoader/types';\nimport { startRecording } from '../recorder/startRecording';\nimport type { Recorder } from '../recorder/types';\nimport {\n clearObservedTraceIds,\n clearReplaySessionSink,\n publishReplaySessionSink,\n} from '../trace-sink/traceSink';\nimport { createChunkSeqStore } from './chunkSeq';\nimport {\n type ReplayContext,\n type ResolvedReplayConfig,\n resolveReplayConfig,\n} from './config';\nimport type { EventCapture } from '../events/startEventCapture';\nimport {\n getSessionStartedAt,\n installReplayLifecycleHandlers,\n sessionMetaRow,\n} from './lifecycle';\n\nexport interface SessionRecordingArgs {\n context: ReplayContext;\n userSessionManager: UserSessionManagerInternal;\n sessionStorage: NamespacedStorage;\n rrwebOptions?: ResolvedReplayConfig['rrwebOptions'] & {\n debug?: boolean;\n enabled?: boolean;\n sampleRate?: number;\n maskAllInputs?: boolean;\n maskAllText?: boolean;\n flushIntervalMs?: number;\n };\n recordUserSessions?: boolean;\n diag?: DiagLogger;\n}\n\ninterface ActiveRecording {\n recorder: Recorder;\n events: EventCapture;\n rrwebModule: RrwebModule;\n removeLifecycle: () => void;\n removeSessionEndListener?: () => void;\n}\n\nexport class SessionRecording {\n private readonly _context: ReplayContext;\n private readonly _userSessionManager: UserSessionManagerInternal;\n private readonly _chunkSeqStore: ReturnType<typeof createChunkSeqStore>;\n private readonly _config: ResolvedReplayConfig;\n private readonly _diag: DiagLogger;\n private readonly _recordUserSessions: boolean;\n\n private _active: ActiveRecording | undefined;\n private _paused = false;\n private _enableRequested = false;\n private _startInProgress = false;\n private _stopInProgress = false;\n private _removeSessionStartedListener?: () => void;\n\n public constructor({\n context,\n userSessionManager,\n sessionStorage,\n rrwebOptions,\n recordUserSessions = true,\n diag,\n }: SessionRecordingArgs) {\n this._context = context;\n this._userSessionManager = userSessionManager;\n this._chunkSeqStore = createChunkSeqStore(sessionStorage);\n this._recordUserSessions = recordUserSessions;\n this._config = resolveReplayConfig(\n context,\n rrwebOptions,\n recordUserSessions,\n );\n this._diag = diag ?? {\n debug: () => undefined,\n error: () => undefined,\n info: () => undefined,\n verbose: () => undefined,\n warn: () => undefined,\n };\n }\n\n public isRecording(): boolean {\n return Boolean(this._active) && !this._paused;\n }\n\n public enable(options?: { force?: boolean }): void {\n if (!this._recordUserSessions && !options?.force) {\n this._diag.debug(\n 'recordUserSessions disabled, waiting for manual enable',\n );\n return;\n }\n\n this._enableRequested = true;\n this._paused = false;\n void this._tryStart();\n }\n\n public pause(): void {\n if (!this._active) {\n return;\n }\n this._enableRequested = false;\n this._paused = true;\n this._active.recorder.pause();\n this._active.events.pause();\n }\n\n public resume(): void {\n if (!this._active) {\n this.enable();\n return;\n }\n\n const wasPaused = this._paused;\n this._enableRequested = true;\n this._paused = false;\n this._active.recorder.resume();\n this._active.events.resume();\n if (wasPaused) {\n this._active.rrwebModule.record.takeFullSnapshot();\n }\n }\n\n public async stop(): Promise<void> {\n this._enableRequested = false;\n this._paused = false;\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n\n if (!this._active) {\n clearReplaySessionSink();\n clearObservedTraceIds();\n return;\n }\n\n if (this._stopInProgress) {\n return;\n }\n\n this._stopInProgress = true;\n\n const active = this._active;\n this._active = undefined;\n\n try {\n active.removeSessionEndListener?.();\n active.removeLifecycle();\n\n await active.recorder.flush(true);\n await active.events.flush(true);\n active.recorder.stop();\n active.events.stop();\n } finally {\n clearReplaySessionSink();\n clearObservedTraceIds();\n this._stopInProgress = false;\n }\n }\n\n private async _tryStart(): Promise<void> {\n if (\n !this._enableRequested ||\n this._active ||\n this._startInProgress ||\n this._stopInProgress\n ) {\n return;\n }\n\n if (typeof window === 'undefined') {\n this._diag.warn('session replay cannot run in non-browser environments');\n return;\n }\n\n const sessionId = this._userSessionManager.getUserSessionId();\n if (sessionId === null) {\n if (!this._removeSessionStartedListener) {\n this._removeSessionStartedListener =\n this._userSessionManager.addSessionPartStartedListener(() => {\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n void this._tryStart();\n });\n }\n return;\n }\n\n if (!this._config.replayEnabled) {\n return;\n }\n\n if (Math.random() >= this._config.replaySampleRate) {\n this._diag.debug('session replay skipped by sample rate');\n return;\n }\n\n this._startInProgress = true;\n\n try {\n const rrwebModule = await loadRrwebModule();\n if (!this._enableRequested || this._active) {\n return;\n }\n\n publishReplaySessionSink(sessionId);\n clearObservedTraceIds();\n\n const startedAt = getSessionStartedAt(this._userSessionManager);\n const onActivity = () => {\n // Activity is tracked via MXL user session manager; no-op hook for now.\n };\n\n const recorder = startRecording({\n chunkSeqStore: this._chunkSeqStore,\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n rrwebModule,\n sessionId,\n transport: this._context.transport,\n });\n\n const events = startEventCapture({\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n sessionId,\n transport: this._context.transport,\n });\n\n void this._context.transport.postSessionMeta(\n sessionMetaRow(this._config, sessionId, startedAt, 1, 'active', null),\n );\n\n const skipLifecycle = (\n globalThis as Record<string, unknown>\n ).__MXL_TEST_SKIP_REPLAY_LIFECYCLE__;\n\n const removeLifecycle =\n skipLifecycle === true\n ? () => undefined\n : installReplayLifecycleHandlers({\n config: this._config,\n events,\n onFinalize: () => {\n this._active = undefined;\n clearReplaySessionSink();\n clearObservedTraceIds();\n },\n recorder,\n sessionId,\n startedAt,\n transport: this._context.transport,\n });\n\n const removeSessionEndListener =\n this._userSessionManager.addSessionPartEndedListener(() => {\n void recorder.flush(true);\n void events.flush(true);\n });\n\n this._active = {\n events,\n recorder,\n removeLifecycle,\n removeSessionEndListener,\n rrwebModule,\n };\n\n this._diag.debug('session recording started');\n } catch (error) {\n this._diag.error('failed to start session recording', error);\n } finally {\n this._startInProgress = false;\n }\n }\n}\n"],"mappings":";;;;;;;;AAkDA,IAAa,mBAAb,MAA8B;CAC5B;CACA;CACA;CACA;CACA;CACA;CAEA;CACA,UAAkB;CAClB,mBAA2B;CAC3B,mBAA2B;CAC3B,kBAA0B;CAC1B;CAEA,YAAmB,EACjB,SACA,oBACA,gBACA,cACA,qBAAqB,MACrB,QACuB;AACvB,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,iBAAiB,oBAAoB,eAAe;AACzD,OAAK,sBAAsB;AAC3B,OAAK,UAAU,oBACb,SACA,cACA,mBACD;AACD,OAAK,QAAQ,QAAQ;GACnB,aAAa,KAAA;GACb,aAAa,KAAA;GACb,YAAY,KAAA;GACZ,eAAe,KAAA;GACf,YAAY,KAAA;GACb;;CAGH,cAA8B;AAC5B,SAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK;;CAGxC,OAAc,SAAqC;AACjD,MAAI,CAAC,KAAK,uBAAuB,CAAC,SAAS,OAAO;AAChD,QAAK,MAAM,MACT,yDACD;AACD;;AAGF,OAAK,mBAAmB;AACxB,OAAK,UAAU;AACV,OAAK,WAAW;;CAGvB,QAAqB;AACnB,MAAI,CAAC,KAAK,QACR;AAEF,OAAK,mBAAmB;AACxB,OAAK,UAAU;AACf,OAAK,QAAQ,SAAS,OAAO;AAC7B,OAAK,QAAQ,OAAO,OAAO;;CAG7B,SAAsB;AACpB,MAAI,CAAC,KAAK,SAAS;AACjB,QAAK,QAAQ;AACb;;EAGF,MAAM,YAAY,KAAK;AACvB,OAAK,mBAAmB;AACxB,OAAK,UAAU;AACf,OAAK,QAAQ,SAAS,QAAQ;AAC9B,OAAK,QAAQ,OAAO,QAAQ;AAC5B,MAAI,UACF,MAAK,QAAQ,YAAY,OAAO,kBAAkB;;CAItD,MAAa,OAAsB;AACjC,OAAK,mBAAmB;AACxB,OAAK,UAAU;AACf,OAAK,iCAAiC;AACtC,OAAK,gCAAgC,KAAA;AAErC,MAAI,CAAC,KAAK,SAAS;AACjB,2BAAwB;AACxB,0BAAuB;AACvB;;AAGF,MAAI,KAAK,gBACP;AAGF,OAAK,kBAAkB;EAEvB,MAAM,SAAS,KAAK;AACpB,OAAK,UAAU,KAAA;AAEf,MAAI;AACF,UAAO,4BAA4B;AACnC,UAAO,iBAAiB;AAExB,SAAM,OAAO,SAAS,MAAM,KAAK;AACjC,SAAM,OAAO,OAAO,MAAM,KAAK;AAC/B,UAAO,SAAS,MAAM;AACtB,UAAO,OAAO,MAAM;YACZ;AACR,2BAAwB;AACxB,0BAAuB;AACvB,QAAK,kBAAkB;;;CAI3B,MAAc,YAA2B;AACvC,MACE,CAAC,KAAK,oBACN,KAAK,WACL,KAAK,oBACL,KAAK,gBAEL;AAGF,MAAI,OAAO,WAAW,aAAa;AACjC,QAAK,MAAM,KAAK,wDAAwD;AACxE;;EAGF,MAAM,YAAY,KAAK,oBAAoB,kBAAkB;AAC7D,MAAI,cAAc,MAAM;AACtB,OAAI,CAAC,KAAK,8BACR,MAAK,gCACH,KAAK,oBAAoB,oCAAoC;AAC3D,SAAK,iCAAiC;AACtC,SAAK,gCAAgC,KAAA;AAChC,SAAK,WAAW;KACrB;AAEN;;AAGF,MAAI,CAAC,KAAK,QAAQ,cAChB;AAGF,MAAI,KAAK,QAAQ,IAAI,KAAK,QAAQ,kBAAkB;AAClD,QAAK,MAAM,MAAM,wCAAwC;AACzD;;AAGF,OAAK,mBAAmB;AAExB,MAAI;GACF,MAAM,cAAc,MAAM,iBAAiB;AAC3C,OAAI,CAAC,KAAK,oBAAoB,KAAK,QACjC;AAGF,4BAAyB,UAAU;AACnC,0BAAuB;GAEvB,MAAM,YAAY,oBAAoB,KAAK,oBAAoB;GAC/D,MAAM,mBAAmB;GAIzB,MAAM,WAAW,eAAe;IAC9B,eAAe,KAAK;IACpB,QAAQ,KAAK;IACb,iBAAiB,KAAK,QAAQ;IAC9B;IACA;IACA;IACA,WAAW,KAAK,SAAS;IAC1B,CAAC;GAEF,MAAM,SAAS,kBAAkB;IAC/B,QAAQ,KAAK;IACb,iBAAiB,KAAK,QAAQ;IAC9B;IACA;IACA,WAAW,KAAK,SAAS;IAC1B,CAAC;AAEG,QAAK,SAAS,UAAU,gBAC3B,eAAe,KAAK,SAAS,WAAW,WAAW,GAAG,UAAU,KAAK,CACtE;GAMD,MAAM,kBAHJ,WACA,uCAGkB,aACR,KAAA,IACN,+BAA+B;IAC7B,QAAQ,KAAK;IACb;IACA,kBAAkB;AAChB,UAAK,UAAU,KAAA;AACf,6BAAwB;AACxB,4BAAuB;;IAEzB;IACA;IACA;IACA,WAAW,KAAK,SAAS;IAC1B,CAAC;GAER,MAAM,2BACJ,KAAK,oBAAoB,kCAAkC;AACpD,aAAS,MAAM,KAAK;AACpB,WAAO,MAAM,KAAK;KACvB;AAEJ,QAAK,UAAU;IACb;IACA;IACA;IACA;IACA;IACD;AAED,QAAK,MAAM,MAAM,4BAA4B;WACtC,OAAO;AACd,QAAK,MAAM,MAAM,qCAAqC,MAAM;YACpD;AACR,QAAK,mBAAmB"}
1
+ {"version":3,"file":"SessionRecording.js","names":[],"sources":["../../../../src/instrumentations/replay/session-recording/SessionRecording.ts"],"sourcesContent":["import type { DiagLogger } from '@opentelemetry/api';\nimport type { UserSessionManagerInternal } from '../../../managers/MXLUserSessionManager/types';\nimport type { NamespacedStorage } from '../../../utils/NamespacedStorage/NamespacedStorage';\nimport { startEventCapture } from '../events/startEventCapture';\nimport { loadRrwebModule } from '../rrwebLoader/loadRrwebModule';\nimport type { RrwebModule } from '../rrwebLoader/types';\nimport { startRecording } from '../recorder/startRecording';\nimport type { Recorder } from '../recorder/types';\nimport {\n clearObservedTraceIds,\n clearReplaySessionSink,\n publishReplaySessionSink,\n} from '../trace-sink/traceSink';\nimport { createChunkSeqStore } from './chunkSeq';\nimport {\n type ReplayContext,\n type ResolvedReplayConfig,\n resolveReplayConfig,\n} from './config';\nimport type { EventCapture } from '../events/startEventCapture';\nimport {\n getSessionStartedAt,\n installReplayLifecycleHandlers,\n sessionMetaRow,\n} from './lifecycle';\n\nexport interface SessionRecordingArgs {\n context: ReplayContext;\n userSessionManager: UserSessionManagerInternal;\n sessionStorage: NamespacedStorage;\n rrwebOptions?: ResolvedReplayConfig['rrwebOptions'] & {\n debug?: boolean;\n enabled?: boolean;\n sampleRate?: number;\n maskAllInputs?: boolean;\n maskAllText?: boolean;\n flushIntervalMs?: number;\n };\n recordUserSessions?: boolean;\n diag?: DiagLogger;\n}\n\ninterface ActiveRecording {\n recorder: Recorder;\n events: EventCapture;\n rrwebModule: RrwebModule;\n removeLifecycle: () => void;\n removeSessionEndListener?: () => void;\n}\n\nexport class SessionRecording {\n private readonly _context: ReplayContext;\n private readonly _userSessionManager: UserSessionManagerInternal;\n private readonly _chunkSeqStore: ReturnType<typeof createChunkSeqStore>;\n private readonly _config: ResolvedReplayConfig;\n private readonly _diag: DiagLogger;\n private readonly _recordUserSessions: boolean;\n\n private _active: ActiveRecording | undefined;\n private _paused = false;\n private _enableRequested = false;\n private _startInProgress = false;\n private _stopInProgress = false;\n private _removeSessionStartedListener?: () => void;\n\n public constructor({\n context,\n userSessionManager,\n sessionStorage,\n rrwebOptions,\n recordUserSessions = true,\n diag,\n }: SessionRecordingArgs) {\n this._context = context;\n this._userSessionManager = userSessionManager;\n this._chunkSeqStore = createChunkSeqStore(sessionStorage);\n this._recordUserSessions = recordUserSessions;\n this._config = resolveReplayConfig(\n context,\n rrwebOptions,\n recordUserSessions,\n );\n this._diag = diag ?? {\n debug: () => undefined,\n error: () => undefined,\n info: () => undefined,\n verbose: () => undefined,\n warn: () => undefined,\n };\n }\n\n public isRecording(): boolean {\n return Boolean(this._active) && !this._paused;\n }\n\n public enable(options?: { force?: boolean }): void {\n if (!this._recordUserSessions && !options?.force) {\n this._diag.debug(\n 'recordUserSessions disabled, waiting for manual enable',\n );\n return;\n }\n\n this._enableRequested = true;\n this._paused = false;\n void this._tryStart();\n }\n\n public pause(): void {\n if (!this._active) {\n return;\n }\n this._enableRequested = false;\n this._paused = true;\n this._active.recorder.pause();\n this._active.events.pause();\n }\n\n public resume(): void {\n if (!this._active) {\n this.enable();\n return;\n }\n\n const wasPaused = this._paused;\n this._enableRequested = true;\n this._paused = false;\n this._active.recorder.resume();\n this._active.events.resume();\n if (wasPaused) {\n this._active.rrwebModule.record.takeFullSnapshot();\n }\n }\n\n public async stop(): Promise<void> {\n this._enableRequested = false;\n this._paused = false;\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n\n if (!this._active) {\n clearReplaySessionSink();\n clearObservedTraceIds();\n return;\n }\n\n if (this._stopInProgress) {\n return;\n }\n\n this._stopInProgress = true;\n\n const active = this._active;\n this._active = undefined;\n\n try {\n active.removeSessionEndListener?.();\n active.removeLifecycle();\n\n await active.recorder.flush(true);\n await active.events.flush(true);\n active.recorder.stop();\n active.events.stop();\n } finally {\n clearReplaySessionSink();\n clearObservedTraceIds();\n this._stopInProgress = false;\n }\n }\n\n private async _tryStart(): Promise<void> {\n if (\n !this._enableRequested ||\n this._active ||\n this._startInProgress ||\n this._stopInProgress\n ) {\n return;\n }\n\n if (typeof window === 'undefined') {\n this._diag.warn('session replay cannot run in non-browser environments');\n return;\n }\n\n const sessionId = this._userSessionManager.getUserSessionId();\n if (sessionId === null) {\n if (!this._removeSessionStartedListener) {\n this._removeSessionStartedListener =\n this._userSessionManager.addSessionPartStartedListener(() => {\n this._removeSessionStartedListener?.();\n this._removeSessionStartedListener = undefined;\n void this._tryStart();\n });\n }\n return;\n }\n\n if (!this._config.replayEnabled) {\n return;\n }\n\n if (Math.random() >= this._config.replaySampleRate) {\n this._diag.debug('session replay skipped by sample rate');\n return;\n }\n\n this._startInProgress = true;\n\n try {\n const rrwebModule = await loadRrwebModule();\n if (!this._enableRequested || this._active) {\n return;\n }\n\n publishReplaySessionSink(sessionId);\n clearObservedTraceIds();\n\n const startedAt = getSessionStartedAt(this._userSessionManager);\n const onActivity = () => {\n // Activity is tracked via MXL user session manager; no-op hook for now.\n };\n\n const recorder = startRecording({\n chunkSeqStore: this._chunkSeqStore,\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n rrwebModule,\n sessionId,\n transport: this._context.transport,\n });\n\n const events = startEventCapture({\n config: this._config,\n flushIntervalMs: this._config.flushIntervalMs,\n onActivity,\n sessionId,\n transport: this._context.transport,\n });\n\n void this._context.transport.postReplay();\n void this._context.transport.postSessionMeta(\n sessionMetaRow(this._config, sessionId, startedAt, 1, 'active', null),\n );\n\n const skipLifecycle = (\n globalThis as Record<string, unknown>\n ).__MXL_TEST_SKIP_REPLAY_LIFECYCLE__;\n\n const removeLifecycle =\n skipLifecycle === true\n ? () => undefined\n : installReplayLifecycleHandlers({\n config: this._config,\n events,\n onFinalize: () => {\n this._active = undefined;\n clearReplaySessionSink();\n clearObservedTraceIds();\n },\n recorder,\n sessionId,\n startedAt,\n transport: this._context.transport,\n });\n\n const removeSessionEndListener =\n this._userSessionManager.addSessionPartEndedListener(() => {\n void recorder.flush(true);\n void events.flush(true);\n });\n\n this._active = {\n events,\n recorder,\n removeLifecycle,\n removeSessionEndListener,\n rrwebModule,\n };\n\n this._diag.debug('session recording started');\n } catch (error) {\n this._diag.error('failed to start session recording', error);\n } finally {\n this._startInProgress = false;\n }\n }\n}\n"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_instrumentations_replay_transport_gzip = require("./gzip.cjs");
3
- const require_exporters_utils = require("../../../exporters/utils.cjs");
4
3
  const require_managers_MXLConfigManager_utils = require("../../../managers/MXLConfigManager/utils.cjs");
5
4
  //#region src/instrumentations/replay/transport/replayTransport.ts
6
5
  /**
7
6
  * Ported from Maple browser SDK (MIT), adapted for MXL auth and endpoints.
8
7
  * @see https://github.com/Makisuo/maple/tree/main/packages/browser/src/replay/transport.ts
9
8
  */
9
+ const getReplayHeaders = (appID) => ({ "mxl-client-id": appID });
10
10
  let lastWarnAt = 0;
11
11
  function warnDropped(what, error) {
12
12
  const now = Date.now();
@@ -15,16 +15,32 @@ function warnDropped(what, error) {
15
15
  console.warn(`[mxl] session replay ${what} failed (dropping; will retry on next chunk):`, error);
16
16
  }
17
17
  function createReplayTransport(config) {
18
- const headers = require_exporters_utils.getMXLHeaders(config.appID, config.userID);
18
+ const headers = getReplayHeaders(config.appID);
19
19
  const baseUrl = config.baseUrl.replace(/\/$/, "");
20
20
  const otelCollectorURL = config.otelCollectorURL.replace(/\/$/, "");
21
21
  const ignoredPathPrefixes = [
22
22
  `${baseUrl}/sessionReplays/`,
23
23
  `${baseUrl}/sessionEvents`,
24
+ `${baseUrl}/replay`,
24
25
  `${otelCollectorURL}/v1/logs`,
25
26
  `${otelCollectorURL}/v1/traces`
26
27
  ];
27
28
  const shouldIgnoreUrl = (url) => ignoredPathPrefixes.some((prefix) => url.startsWith(prefix));
29
+ const postReplay = async (keepalive = false) => {
30
+ if (!config.apiKey) return;
31
+ await fetch(`${baseUrl}/replay`, {
32
+ body: "null",
33
+ headers: {
34
+ ...headers,
35
+ "content-type": "application/json",
36
+ "mxl-api-key": config.apiKey
37
+ },
38
+ keepalive,
39
+ method: "POST"
40
+ }).catch((error) => {
41
+ warnDropped("replay POST", error);
42
+ });
43
+ };
28
44
  const postSessionMeta = async (row, keepalive = false) => {
29
45
  const body = `${JSON.stringify(row)}\n`;
30
46
  await fetch(`${baseUrl}/sessionReplays/meta`, {
@@ -73,6 +89,7 @@ function createReplayTransport(config) {
73
89
  });
74
90
  };
75
91
  return {
92
+ postReplay,
76
93
  postSessionBlob,
77
94
  postSessionEvents,
78
95
  postSessionMeta,
@@ -81,10 +98,10 @@ function createReplayTransport(config) {
81
98
  }
82
99
  function createReplayTransportFromInit(args) {
83
100
  return createReplayTransport({
101
+ apiKey: args.apiKey,
84
102
  appID: args.appID,
85
103
  baseUrl: require_managers_MXLConfigManager_utils.getApiURL(args.apiURL),
86
- otelCollectorURL: require_managers_MXLConfigManager_utils.getOtelCollectorURL(args.otelCollectorURL),
87
- userID: args.userID
104
+ otelCollectorURL: require_managers_MXLConfigManager_utils.getOtelCollectorURL(args.otelCollectorURL)
88
105
  });
89
106
  }
90
107
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"replayTransport.cjs","names":["getMXLHeaders","getApiURL","getOtelCollectorURL"],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"sourcesContent":["/**\n * Ported from Maple browser SDK (MIT), adapted for MXL auth and endpoints.\n * @see https://github.com/Makisuo/maple/tree/main/packages/browser/src/replay/transport.ts\n */\n\nimport { getMXLHeaders } from '../../../exporters/utils';\nimport {\n getApiURL,\n getOtelCollectorURL\n} from '../../../managers/MXLConfigManager/utils';\nimport { gzip } from './gzip';\nimport type { ChunkMeta, ReplayTransport, ReplayTransportConfig } from './types';\n\nexport { gzip };\n\nlet lastWarnAt = 0;\n\nfunction warnDropped(what: string, error: unknown): void {\n const now = Date.now();\n if (now - lastWarnAt < 30_000) {\n return;\n }\n lastWarnAt = now;\n // oxlint-disable-next-line no-console -- best-effort replay diagnostics\n console.warn(\n `[mxl] session replay ${what} failed (dropping; will retry on next chunk):`,\n error,\n );\n}\n\nexport function createReplayTransport(\n config: ReplayTransportConfig,\n): ReplayTransport {\n const headers = getMXLHeaders(config.appID, config.userID);\n const baseUrl = config.baseUrl.replace(/\\/$/, '');\n const otelCollectorURL = config.otelCollectorURL.replace(/\\/$/, '');\n\n const ignoredPathPrefixes = [\n `${baseUrl}/sessionReplays/`,\n `${baseUrl}/sessionEvents`,\n `${otelCollectorURL}/v1/logs`,\n `${otelCollectorURL}/v1/traces`,\n ];\n\n const shouldIgnoreUrl = (url: string): boolean =>\n ignoredPathPrefixes.some((prefix) => url.startsWith(prefix));\n\n const postSessionMeta = async (\n row: Record<string, unknown>,\n keepalive = false,\n ): Promise<void> => {\n const body = `${JSON.stringify(row)}\\n`;\n await fetch(`${baseUrl}/sessionReplays/meta`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('metadata POST', error);\n });\n };\n\n const postSessionEvents = async (\n rows: ReadonlyArray<Record<string, unknown>>,\n keepalive = false,\n ): Promise<void> => {\n if (rows.length === 0) {\n return;\n }\n const body = `${rows.map((row) => JSON.stringify(row)).join('\\n')}\\n`;\n await fetch(`${baseUrl}/sessionEvents`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('events POST', error);\n });\n };\n\n const postSessionBlob = async (\n meta: ChunkMeta,\n gzipped: Uint8Array,\n keepalive = false,\n ): Promise<void> => {\n await fetch(`${baseUrl}/sessionReplays/blob`, {\n body: gzipped as unknown as BodyInit,\n headers: {\n ...headers,\n 'content-type': 'application/octet-stream',\n 'x-mxl-chunk-seq': String(meta.chunkSeq),\n 'x-mxl-duration-ms': String(meta.durationMs),\n 'x-mxl-event-count': String(meta.eventCount),\n 'x-mxl-is-checkpoint': meta.isCheckpoint ? '1' : '0',\n 'x-mxl-session-id': meta.sessionId,\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('blob POST', error);\n });\n };\n\n return {\n postSessionBlob,\n postSessionEvents,\n postSessionMeta,\n shouldIgnoreUrl,\n };\n}\n\nexport function createReplayTransportFromInit(args: {\n appID: string;\n userID: string;\n apiURL?: string;\n otelCollectorURL?: string;\n}): ReplayTransport {\n return createReplayTransport({\n appID: args.appID,\n baseUrl: getApiURL(args.apiURL),\n otelCollectorURL: getOtelCollectorURL(args.otelCollectorURL),\n userID: args.userID,\n });\n}\n"],"mappings":";;;;;;;;;AAeA,IAAI,aAAa;AAEjB,SAAS,YAAY,MAAc,OAAsB;CACvD,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,MAAM,aAAa,IACrB;AAEF,cAAa;AAEb,SAAQ,KACN,wBAAwB,KAAK,gDAC7B,MACD;;AAGH,SAAgB,sBACd,QACiB;CACjB,MAAM,UAAUA,wBAAAA,cAAc,OAAO,OAAO,OAAO,OAAO;CAC1D,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GAAG;CACjD,MAAM,mBAAmB,OAAO,iBAAiB,QAAQ,OAAO,GAAG;CAEnE,MAAM,sBAAsB;EAC1B,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,iBAAiB;EACpB,GAAG,iBAAiB;EACrB;CAED,MAAM,mBAAmB,QACvB,oBAAoB,MAAM,WAAW,IAAI,WAAW,OAAO,CAAC;CAE9D,MAAM,kBAAkB,OACtB,KACA,YAAY,UACM;EAClB,MAAM,OAAO,GAAG,KAAK,UAAU,IAAI,CAAC;AACpC,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,iBAAiB,MAAM;IACnC;;CAGJ,MAAM,oBAAoB,OACxB,MACA,YAAY,UACM;AAClB,MAAI,KAAK,WAAW,EAClB;EAEF,MAAM,OAAO,GAAG,KAAK,KAAK,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;AAClE,QAAM,MAAM,GAAG,QAAQ,iBAAiB;GACtC;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,eAAe,MAAM;IACjC;;CAGJ,MAAM,kBAAkB,OACtB,MACA,SACA,YAAY,UACM;AAClB,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C,MAAM;GACN,SAAS;IACP,GAAG;IACH,gBAAgB;IAChB,mBAAmB,OAAO,KAAK,SAAS;IACxC,qBAAqB,OAAO,KAAK,WAAW;IAC5C,qBAAqB,OAAO,KAAK,WAAW;IAC5C,uBAAuB,KAAK,eAAe,MAAM;IACjD,oBAAoB,KAAK;IAC1B;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,aAAa,MAAM;IAC/B;;AAGJ,QAAO;EACL;EACA;EACA;EACA;EACD;;AAGH,SAAgB,8BAA8B,MAK1B;AAClB,QAAO,sBAAsB;EAC3B,OAAO,KAAK;EACZ,SAASC,wCAAAA,UAAU,KAAK,OAAO;EAC/B,kBAAkBC,wCAAAA,oBAAoB,KAAK,iBAAiB;EAC5D,QAAQ,KAAK;EACd,CAAC"}
1
+ {"version":3,"file":"replayTransport.cjs","names":["getApiURL","getOtelCollectorURL"],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"sourcesContent":["/**\n * Ported from Maple browser SDK (MIT), adapted for MXL auth and endpoints.\n * @see https://github.com/Makisuo/maple/tree/main/packages/browser/src/replay/transport.ts\n */\n\nimport {\n getApiURL,\n getOtelCollectorURL,\n} from '../../../managers/MXLConfigManager/utils';\nimport { gzip } from './gzip';\nimport type { ChunkMeta, ReplayTransport, ReplayTransportConfig } from './types';\n\nexport { gzip };\n\nconst getReplayHeaders = (appID: string): Record<string, string> => ({\n 'mxl-client-id': appID,\n});\n\nlet lastWarnAt = 0;\n\nfunction warnDropped(what: string, error: unknown): void {\n const now = Date.now();\n if (now - lastWarnAt < 30_000) {\n return;\n }\n lastWarnAt = now;\n // oxlint-disable-next-line no-console -- best-effort replay diagnostics\n console.warn(\n `[mxl] session replay ${what} failed (dropping; will retry on next chunk):`,\n error,\n );\n}\n\nexport function createReplayTransport(\n config: ReplayTransportConfig,\n): ReplayTransport {\n const headers = getReplayHeaders(config.appID);\n const baseUrl = config.baseUrl.replace(/\\/$/, '');\n const otelCollectorURL = config.otelCollectorURL.replace(/\\/$/, '');\n\n const ignoredPathPrefixes = [\n `${baseUrl}/sessionReplays/`,\n `${baseUrl}/sessionEvents`,\n `${baseUrl}/replay`,\n `${otelCollectorURL}/v1/logs`,\n `${otelCollectorURL}/v1/traces`,\n ];\n\n const shouldIgnoreUrl = (url: string): boolean =>\n ignoredPathPrefixes.some((prefix) => url.startsWith(prefix));\n\n const postReplay = async (keepalive = false): Promise<void> => {\n if (!config.apiKey) {\n return;\n }\n await fetch(`${baseUrl}/replay`, {\n body: 'null',\n headers: {\n ...headers,\n 'content-type': 'application/json',\n 'mxl-api-key': config.apiKey,\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('replay POST', error);\n });\n };\n\n const postSessionMeta = async (\n row: Record<string, unknown>,\n keepalive = false,\n ): Promise<void> => {\n const body = `${JSON.stringify(row)}\\n`;\n await fetch(`${baseUrl}/sessionReplays/meta`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('metadata POST', error);\n });\n };\n\n const postSessionEvents = async (\n rows: ReadonlyArray<Record<string, unknown>>,\n keepalive = false,\n ): Promise<void> => {\n if (rows.length === 0) {\n return;\n }\n const body = `${rows.map((row) => JSON.stringify(row)).join('\\n')}\\n`;\n await fetch(`${baseUrl}/sessionEvents`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('events POST', error);\n });\n };\n\n const postSessionBlob = async (\n meta: ChunkMeta,\n gzipped: Uint8Array,\n keepalive = false,\n ): Promise<void> => {\n await fetch(`${baseUrl}/sessionReplays/blob`, {\n body: gzipped as unknown as BodyInit,\n headers: {\n ...headers,\n 'content-type': 'application/octet-stream',\n 'x-mxl-chunk-seq': String(meta.chunkSeq),\n 'x-mxl-duration-ms': String(meta.durationMs),\n 'x-mxl-event-count': String(meta.eventCount),\n 'x-mxl-is-checkpoint': meta.isCheckpoint ? '1' : '0',\n 'x-mxl-session-id': meta.sessionId,\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('blob POST', error);\n });\n };\n\n return {\n postReplay,\n postSessionBlob,\n postSessionEvents,\n postSessionMeta,\n shouldIgnoreUrl,\n };\n}\n\nexport function createReplayTransportFromInit(args: {\n appID: string;\n apiKey?: string;\n apiURL?: string;\n otelCollectorURL?: string;\n}): ReplayTransport {\n return createReplayTransport({\n apiKey: args.apiKey,\n appID: args.appID,\n baseUrl: getApiURL(args.apiURL),\n otelCollectorURL: getOtelCollectorURL(args.otelCollectorURL),\n });\n}\n"],"mappings":";;;;;;;;AAcA,MAAM,oBAAoB,WAA2C,EACnE,iBAAiB,OAClB;AAED,IAAI,aAAa;AAEjB,SAAS,YAAY,MAAc,OAAsB;CACvD,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,MAAM,aAAa,IACrB;AAEF,cAAa;AAEb,SAAQ,KACN,wBAAwB,KAAK,gDAC7B,MACD;;AAGH,SAAgB,sBACd,QACiB;CACjB,MAAM,UAAU,iBAAiB,OAAO,MAAM;CAC9C,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GAAG;CACjD,MAAM,mBAAmB,OAAO,iBAAiB,QAAQ,OAAO,GAAG;CAEnE,MAAM,sBAAsB;EAC1B,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,iBAAiB;EACpB,GAAG,iBAAiB;EACrB;CAED,MAAM,mBAAmB,QACvB,oBAAoB,MAAM,WAAW,IAAI,WAAW,OAAO,CAAC;CAE9D,MAAM,aAAa,OAAO,YAAY,UAAyB;AAC7D,MAAI,CAAC,OAAO,OACV;AAEF,QAAM,MAAM,GAAG,QAAQ,UAAU;GAC/B,MAAM;GACN,SAAS;IACP,GAAG;IACH,gBAAgB;IAChB,eAAe,OAAO;IACvB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,eAAe,MAAM;IACjC;;CAGJ,MAAM,kBAAkB,OACtB,KACA,YAAY,UACM;EAClB,MAAM,OAAO,GAAG,KAAK,UAAU,IAAI,CAAC;AACpC,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,iBAAiB,MAAM;IACnC;;CAGJ,MAAM,oBAAoB,OACxB,MACA,YAAY,UACM;AAClB,MAAI,KAAK,WAAW,EAClB;EAEF,MAAM,OAAO,GAAG,KAAK,KAAK,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;AAClE,QAAM,MAAM,GAAG,QAAQ,iBAAiB;GACtC;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,eAAe,MAAM;IACjC;;CAGJ,MAAM,kBAAkB,OACtB,MACA,SACA,YAAY,UACM;AAClB,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C,MAAM;GACN,SAAS;IACP,GAAG;IACH,gBAAgB;IAChB,mBAAmB,OAAO,KAAK,SAAS;IACxC,qBAAqB,OAAO,KAAK,WAAW;IAC5C,qBAAqB,OAAO,KAAK,WAAW;IAC5C,uBAAuB,KAAK,eAAe,MAAM;IACjD,oBAAoB,KAAK;IAC1B;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,aAAa,MAAM;IAC/B;;AAGJ,QAAO;EACL;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAgB,8BAA8B,MAK1B;AAClB,QAAO,sBAAsB;EAC3B,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,SAASA,wCAAAA,UAAU,KAAK,OAAO;EAC/B,kBAAkBC,wCAAAA,oBAAoB,KAAK,iBAAiB;EAC7D,CAAC"}
@@ -5,7 +5,7 @@ import { gzip } from "./gzip.cjs";
5
5
  declare function createReplayTransport(config: ReplayTransportConfig): ReplayTransport;
6
6
  declare function createReplayTransportFromInit(args: {
7
7
  appID: string;
8
- userID: string;
8
+ apiKey?: string;
9
9
  apiURL?: string;
10
10
  otelCollectorURL?: string;
11
11
  }): ReplayTransport;
@@ -1 +1 @@
1
- {"version":3,"file":"replayTransport.d.cts","names":[],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"mappings":";;;;iBA8BgB,qBAAA,CACd,MAAA,EAAQ,qBAAA,GACP,eAAA;AAAA,iBAqFa,6BAAA,CAA8B,IAAA;EAC5C,KAAA;EACA,MAAA;EACA,MAAA;EACA,gBAAA;AAAA,IACE,eAAA"}
1
+ {"version":3,"file":"replayTransport.d.cts","names":[],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"mappings":";;;;iBAiCgB,qBAAA,CACd,MAAA,EAAQ,qBAAA,GACP,eAAA;AAAA,iBAyGa,6BAAA,CAA8B,IAAA;EAC5C,KAAA;EACA,MAAA;EACA,MAAA;EACA,gBAAA;AAAA,IACE,eAAA"}
@@ -5,7 +5,7 @@ import { gzip } from "./gzip.js";
5
5
  declare function createReplayTransport(config: ReplayTransportConfig): ReplayTransport;
6
6
  declare function createReplayTransportFromInit(args: {
7
7
  appID: string;
8
- userID: string;
8
+ apiKey?: string;
9
9
  apiURL?: string;
10
10
  otelCollectorURL?: string;
11
11
  }): ReplayTransport;
@@ -1 +1 @@
1
- {"version":3,"file":"replayTransport.d.ts","names":[],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"mappings":";;;;iBA8BgB,qBAAA,CACd,MAAA,EAAQ,qBAAA,GACP,eAAA;AAAA,iBAqFa,6BAAA,CAA8B,IAAA;EAC5C,KAAA;EACA,MAAA;EACA,MAAA;EACA,gBAAA;AAAA,IACE,eAAA"}
1
+ {"version":3,"file":"replayTransport.d.ts","names":[],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"mappings":";;;;iBAiCgB,qBAAA,CACd,MAAA,EAAQ,qBAAA,GACP,eAAA;AAAA,iBAyGa,6BAAA,CAA8B,IAAA;EAC5C,KAAA;EACA,MAAA;EACA,MAAA;EACA,gBAAA;AAAA,IACE,eAAA"}
@@ -1,11 +1,11 @@
1
1
  import { gzip } from "./gzip.js";
2
- import { getMXLHeaders } from "../../../exporters/utils.js";
3
2
  import { getApiURL, getOtelCollectorURL } from "../../../managers/MXLConfigManager/utils.js";
4
3
  //#region src/instrumentations/replay/transport/replayTransport.ts
5
4
  /**
6
5
  * Ported from Maple browser SDK (MIT), adapted for MXL auth and endpoints.
7
6
  * @see https://github.com/Makisuo/maple/tree/main/packages/browser/src/replay/transport.ts
8
7
  */
8
+ const getReplayHeaders = (appID) => ({ "mxl-client-id": appID });
9
9
  let lastWarnAt = 0;
10
10
  function warnDropped(what, error) {
11
11
  const now = Date.now();
@@ -14,16 +14,32 @@ function warnDropped(what, error) {
14
14
  console.warn(`[mxl] session replay ${what} failed (dropping; will retry on next chunk):`, error);
15
15
  }
16
16
  function createReplayTransport(config) {
17
- const headers = getMXLHeaders(config.appID, config.userID);
17
+ const headers = getReplayHeaders(config.appID);
18
18
  const baseUrl = config.baseUrl.replace(/\/$/, "");
19
19
  const otelCollectorURL = config.otelCollectorURL.replace(/\/$/, "");
20
20
  const ignoredPathPrefixes = [
21
21
  `${baseUrl}/sessionReplays/`,
22
22
  `${baseUrl}/sessionEvents`,
23
+ `${baseUrl}/replay`,
23
24
  `${otelCollectorURL}/v1/logs`,
24
25
  `${otelCollectorURL}/v1/traces`
25
26
  ];
26
27
  const shouldIgnoreUrl = (url) => ignoredPathPrefixes.some((prefix) => url.startsWith(prefix));
28
+ const postReplay = async (keepalive = false) => {
29
+ if (!config.apiKey) return;
30
+ await fetch(`${baseUrl}/replay`, {
31
+ body: "null",
32
+ headers: {
33
+ ...headers,
34
+ "content-type": "application/json",
35
+ "mxl-api-key": config.apiKey
36
+ },
37
+ keepalive,
38
+ method: "POST"
39
+ }).catch((error) => {
40
+ warnDropped("replay POST", error);
41
+ });
42
+ };
27
43
  const postSessionMeta = async (row, keepalive = false) => {
28
44
  const body = `${JSON.stringify(row)}\n`;
29
45
  await fetch(`${baseUrl}/sessionReplays/meta`, {
@@ -72,6 +88,7 @@ function createReplayTransport(config) {
72
88
  });
73
89
  };
74
90
  return {
91
+ postReplay,
75
92
  postSessionBlob,
76
93
  postSessionEvents,
77
94
  postSessionMeta,
@@ -80,10 +97,10 @@ function createReplayTransport(config) {
80
97
  }
81
98
  function createReplayTransportFromInit(args) {
82
99
  return createReplayTransport({
100
+ apiKey: args.apiKey,
83
101
  appID: args.appID,
84
102
  baseUrl: getApiURL(args.apiURL),
85
- otelCollectorURL: getOtelCollectorURL(args.otelCollectorURL),
86
- userID: args.userID
103
+ otelCollectorURL: getOtelCollectorURL(args.otelCollectorURL)
87
104
  });
88
105
  }
89
106
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"replayTransport.js","names":[],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"sourcesContent":["/**\n * Ported from Maple browser SDK (MIT), adapted for MXL auth and endpoints.\n * @see https://github.com/Makisuo/maple/tree/main/packages/browser/src/replay/transport.ts\n */\n\nimport { getMXLHeaders } from '../../../exporters/utils';\nimport {\n getApiURL,\n getOtelCollectorURL\n} from '../../../managers/MXLConfigManager/utils';\nimport { gzip } from './gzip';\nimport type { ChunkMeta, ReplayTransport, ReplayTransportConfig } from './types';\n\nexport { gzip };\n\nlet lastWarnAt = 0;\n\nfunction warnDropped(what: string, error: unknown): void {\n const now = Date.now();\n if (now - lastWarnAt < 30_000) {\n return;\n }\n lastWarnAt = now;\n // oxlint-disable-next-line no-console -- best-effort replay diagnostics\n console.warn(\n `[mxl] session replay ${what} failed (dropping; will retry on next chunk):`,\n error,\n );\n}\n\nexport function createReplayTransport(\n config: ReplayTransportConfig,\n): ReplayTransport {\n const headers = getMXLHeaders(config.appID, config.userID);\n const baseUrl = config.baseUrl.replace(/\\/$/, '');\n const otelCollectorURL = config.otelCollectorURL.replace(/\\/$/, '');\n\n const ignoredPathPrefixes = [\n `${baseUrl}/sessionReplays/`,\n `${baseUrl}/sessionEvents`,\n `${otelCollectorURL}/v1/logs`,\n `${otelCollectorURL}/v1/traces`,\n ];\n\n const shouldIgnoreUrl = (url: string): boolean =>\n ignoredPathPrefixes.some((prefix) => url.startsWith(prefix));\n\n const postSessionMeta = async (\n row: Record<string, unknown>,\n keepalive = false,\n ): Promise<void> => {\n const body = `${JSON.stringify(row)}\\n`;\n await fetch(`${baseUrl}/sessionReplays/meta`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('metadata POST', error);\n });\n };\n\n const postSessionEvents = async (\n rows: ReadonlyArray<Record<string, unknown>>,\n keepalive = false,\n ): Promise<void> => {\n if (rows.length === 0) {\n return;\n }\n const body = `${rows.map((row) => JSON.stringify(row)).join('\\n')}\\n`;\n await fetch(`${baseUrl}/sessionEvents`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('events POST', error);\n });\n };\n\n const postSessionBlob = async (\n meta: ChunkMeta,\n gzipped: Uint8Array,\n keepalive = false,\n ): Promise<void> => {\n await fetch(`${baseUrl}/sessionReplays/blob`, {\n body: gzipped as unknown as BodyInit,\n headers: {\n ...headers,\n 'content-type': 'application/octet-stream',\n 'x-mxl-chunk-seq': String(meta.chunkSeq),\n 'x-mxl-duration-ms': String(meta.durationMs),\n 'x-mxl-event-count': String(meta.eventCount),\n 'x-mxl-is-checkpoint': meta.isCheckpoint ? '1' : '0',\n 'x-mxl-session-id': meta.sessionId,\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('blob POST', error);\n });\n };\n\n return {\n postSessionBlob,\n postSessionEvents,\n postSessionMeta,\n shouldIgnoreUrl,\n };\n}\n\nexport function createReplayTransportFromInit(args: {\n appID: string;\n userID: string;\n apiURL?: string;\n otelCollectorURL?: string;\n}): ReplayTransport {\n return createReplayTransport({\n appID: args.appID,\n baseUrl: getApiURL(args.apiURL),\n otelCollectorURL: getOtelCollectorURL(args.otelCollectorURL),\n userID: args.userID,\n });\n}\n"],"mappings":";;;;;;;;AAeA,IAAI,aAAa;AAEjB,SAAS,YAAY,MAAc,OAAsB;CACvD,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,MAAM,aAAa,IACrB;AAEF,cAAa;AAEb,SAAQ,KACN,wBAAwB,KAAK,gDAC7B,MACD;;AAGH,SAAgB,sBACd,QACiB;CACjB,MAAM,UAAU,cAAc,OAAO,OAAO,OAAO,OAAO;CAC1D,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GAAG;CACjD,MAAM,mBAAmB,OAAO,iBAAiB,QAAQ,OAAO,GAAG;CAEnE,MAAM,sBAAsB;EAC1B,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,iBAAiB;EACpB,GAAG,iBAAiB;EACrB;CAED,MAAM,mBAAmB,QACvB,oBAAoB,MAAM,WAAW,IAAI,WAAW,OAAO,CAAC;CAE9D,MAAM,kBAAkB,OACtB,KACA,YAAY,UACM;EAClB,MAAM,OAAO,GAAG,KAAK,UAAU,IAAI,CAAC;AACpC,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,iBAAiB,MAAM;IACnC;;CAGJ,MAAM,oBAAoB,OACxB,MACA,YAAY,UACM;AAClB,MAAI,KAAK,WAAW,EAClB;EAEF,MAAM,OAAO,GAAG,KAAK,KAAK,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;AAClE,QAAM,MAAM,GAAG,QAAQ,iBAAiB;GACtC;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,eAAe,MAAM;IACjC;;CAGJ,MAAM,kBAAkB,OACtB,MACA,SACA,YAAY,UACM;AAClB,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C,MAAM;GACN,SAAS;IACP,GAAG;IACH,gBAAgB;IAChB,mBAAmB,OAAO,KAAK,SAAS;IACxC,qBAAqB,OAAO,KAAK,WAAW;IAC5C,qBAAqB,OAAO,KAAK,WAAW;IAC5C,uBAAuB,KAAK,eAAe,MAAM;IACjD,oBAAoB,KAAK;IAC1B;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,aAAa,MAAM;IAC/B;;AAGJ,QAAO;EACL;EACA;EACA;EACA;EACD;;AAGH,SAAgB,8BAA8B,MAK1B;AAClB,QAAO,sBAAsB;EAC3B,OAAO,KAAK;EACZ,SAAS,UAAU,KAAK,OAAO;EAC/B,kBAAkB,oBAAoB,KAAK,iBAAiB;EAC5D,QAAQ,KAAK;EACd,CAAC"}
1
+ {"version":3,"file":"replayTransport.js","names":[],"sources":["../../../../src/instrumentations/replay/transport/replayTransport.ts"],"sourcesContent":["/**\n * Ported from Maple browser SDK (MIT), adapted for MXL auth and endpoints.\n * @see https://github.com/Makisuo/maple/tree/main/packages/browser/src/replay/transport.ts\n */\n\nimport {\n getApiURL,\n getOtelCollectorURL,\n} from '../../../managers/MXLConfigManager/utils';\nimport { gzip } from './gzip';\nimport type { ChunkMeta, ReplayTransport, ReplayTransportConfig } from './types';\n\nexport { gzip };\n\nconst getReplayHeaders = (appID: string): Record<string, string> => ({\n 'mxl-client-id': appID,\n});\n\nlet lastWarnAt = 0;\n\nfunction warnDropped(what: string, error: unknown): void {\n const now = Date.now();\n if (now - lastWarnAt < 30_000) {\n return;\n }\n lastWarnAt = now;\n // oxlint-disable-next-line no-console -- best-effort replay diagnostics\n console.warn(\n `[mxl] session replay ${what} failed (dropping; will retry on next chunk):`,\n error,\n );\n}\n\nexport function createReplayTransport(\n config: ReplayTransportConfig,\n): ReplayTransport {\n const headers = getReplayHeaders(config.appID);\n const baseUrl = config.baseUrl.replace(/\\/$/, '');\n const otelCollectorURL = config.otelCollectorURL.replace(/\\/$/, '');\n\n const ignoredPathPrefixes = [\n `${baseUrl}/sessionReplays/`,\n `${baseUrl}/sessionEvents`,\n `${baseUrl}/replay`,\n `${otelCollectorURL}/v1/logs`,\n `${otelCollectorURL}/v1/traces`,\n ];\n\n const shouldIgnoreUrl = (url: string): boolean =>\n ignoredPathPrefixes.some((prefix) => url.startsWith(prefix));\n\n const postReplay = async (keepalive = false): Promise<void> => {\n if (!config.apiKey) {\n return;\n }\n await fetch(`${baseUrl}/replay`, {\n body: 'null',\n headers: {\n ...headers,\n 'content-type': 'application/json',\n 'mxl-api-key': config.apiKey,\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('replay POST', error);\n });\n };\n\n const postSessionMeta = async (\n row: Record<string, unknown>,\n keepalive = false,\n ): Promise<void> => {\n const body = `${JSON.stringify(row)}\\n`;\n await fetch(`${baseUrl}/sessionReplays/meta`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('metadata POST', error);\n });\n };\n\n const postSessionEvents = async (\n rows: ReadonlyArray<Record<string, unknown>>,\n keepalive = false,\n ): Promise<void> => {\n if (rows.length === 0) {\n return;\n }\n const body = `${rows.map((row) => JSON.stringify(row)).join('\\n')}\\n`;\n await fetch(`${baseUrl}/sessionEvents`, {\n body,\n headers: {\n ...headers,\n 'content-type': 'application/x-ndjson',\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('events POST', error);\n });\n };\n\n const postSessionBlob = async (\n meta: ChunkMeta,\n gzipped: Uint8Array,\n keepalive = false,\n ): Promise<void> => {\n await fetch(`${baseUrl}/sessionReplays/blob`, {\n body: gzipped as unknown as BodyInit,\n headers: {\n ...headers,\n 'content-type': 'application/octet-stream',\n 'x-mxl-chunk-seq': String(meta.chunkSeq),\n 'x-mxl-duration-ms': String(meta.durationMs),\n 'x-mxl-event-count': String(meta.eventCount),\n 'x-mxl-is-checkpoint': meta.isCheckpoint ? '1' : '0',\n 'x-mxl-session-id': meta.sessionId,\n },\n keepalive,\n method: 'POST',\n }).catch((error) => {\n warnDropped('blob POST', error);\n });\n };\n\n return {\n postReplay,\n postSessionBlob,\n postSessionEvents,\n postSessionMeta,\n shouldIgnoreUrl,\n };\n}\n\nexport function createReplayTransportFromInit(args: {\n appID: string;\n apiKey?: string;\n apiURL?: string;\n otelCollectorURL?: string;\n}): ReplayTransport {\n return createReplayTransport({\n apiKey: args.apiKey,\n appID: args.appID,\n baseUrl: getApiURL(args.apiURL),\n otelCollectorURL: getOtelCollectorURL(args.otelCollectorURL),\n });\n}\n"],"mappings":";;;;;;;AAcA,MAAM,oBAAoB,WAA2C,EACnE,iBAAiB,OAClB;AAED,IAAI,aAAa;AAEjB,SAAS,YAAY,MAAc,OAAsB;CACvD,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,MAAM,aAAa,IACrB;AAEF,cAAa;AAEb,SAAQ,KACN,wBAAwB,KAAK,gDAC7B,MACD;;AAGH,SAAgB,sBACd,QACiB;CACjB,MAAM,UAAU,iBAAiB,OAAO,MAAM;CAC9C,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GAAG;CACjD,MAAM,mBAAmB,OAAO,iBAAiB,QAAQ,OAAO,GAAG;CAEnE,MAAM,sBAAsB;EAC1B,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,iBAAiB;EACpB,GAAG,iBAAiB;EACrB;CAED,MAAM,mBAAmB,QACvB,oBAAoB,MAAM,WAAW,IAAI,WAAW,OAAO,CAAC;CAE9D,MAAM,aAAa,OAAO,YAAY,UAAyB;AAC7D,MAAI,CAAC,OAAO,OACV;AAEF,QAAM,MAAM,GAAG,QAAQ,UAAU;GAC/B,MAAM;GACN,SAAS;IACP,GAAG;IACH,gBAAgB;IAChB,eAAe,OAAO;IACvB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,eAAe,MAAM;IACjC;;CAGJ,MAAM,kBAAkB,OACtB,KACA,YAAY,UACM;EAClB,MAAM,OAAO,GAAG,KAAK,UAAU,IAAI,CAAC;AACpC,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,iBAAiB,MAAM;IACnC;;CAGJ,MAAM,oBAAoB,OACxB,MACA,YAAY,UACM;AAClB,MAAI,KAAK,WAAW,EAClB;EAEF,MAAM,OAAO,GAAG,KAAK,KAAK,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;AAClE,QAAM,MAAM,GAAG,QAAQ,iBAAiB;GACtC;GACA,SAAS;IACP,GAAG;IACH,gBAAgB;IACjB;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,eAAe,MAAM;IACjC;;CAGJ,MAAM,kBAAkB,OACtB,MACA,SACA,YAAY,UACM;AAClB,QAAM,MAAM,GAAG,QAAQ,uBAAuB;GAC5C,MAAM;GACN,SAAS;IACP,GAAG;IACH,gBAAgB;IAChB,mBAAmB,OAAO,KAAK,SAAS;IACxC,qBAAqB,OAAO,KAAK,WAAW;IAC5C,qBAAqB,OAAO,KAAK,WAAW;IAC5C,uBAAuB,KAAK,eAAe,MAAM;IACjD,oBAAoB,KAAK;IAC1B;GACD;GACA,QAAQ;GACT,CAAC,CAAC,OAAO,UAAU;AAClB,eAAY,aAAa,MAAM;IAC/B;;AAGJ,QAAO;EACL;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAgB,8BAA8B,MAK1B;AAClB,QAAO,sBAAsB;EAC3B,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,SAAS,UAAU,KAAK,OAAO;EAC/B,kBAAkB,oBAAoB,KAAK,iBAAiB;EAC7D,CAAC"}
@@ -1,7 +1,7 @@
1
1
  //#region src/instrumentations/replay/transport/types.d.ts
2
2
  interface ReplayTransportConfig {
3
3
  readonly appID: string;
4
- readonly userID: string;
4
+ readonly apiKey?: string;
5
5
  readonly baseUrl: string;
6
6
  readonly otelCollectorURL: string;
7
7
  }
@@ -13,6 +13,7 @@ interface ChunkMeta {
13
13
  readonly durationMs: number;
14
14
  }
15
15
  interface ReplayTransport {
16
+ postReplay: (keepalive?: boolean) => Promise<void>;
16
17
  postSessionMeta: (row: Record<string, unknown>, keepalive?: boolean) => Promise<void>;
17
18
  postSessionEvents: (rows: ReadonlyArray<Record<string, unknown>>, keepalive?: boolean) => Promise<void>;
18
19
  postSessionBlob: (meta: ChunkMeta, gzipped: Uint8Array, keepalive?: boolean) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../../../../src/instrumentations/replay/transport/types.ts"],"mappings":";UAAiB,qBAAA;EAAA,SACN,KAAA;EAAA,SACA,MAAA;EAAA,SACA,OAAA;EAAA,SACA,gBAAA;AAAA;AAAA,UAGM,SAAA;EAAA,SACN,SAAA;EAAA,SACA,QAAA;EAAA,SACA,YAAA;EAAA,SACA,UAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGM,eAAA;EACf,eAAA,GACE,GAAA,EAAK,MAAA,mBACL,SAAA,eACG,OAAA;EACL,iBAAA,GACE,IAAA,EAAM,aAAA,CAAc,MAAA,oBACpB,SAAA,eACG,OAAA;EACL,eAAA,GACE,IAAA,EAAM,SAAA,EACN,OAAA,EAAS,UAAA,EACT,SAAA,eACG,OAAA;EACL,eAAA,GAAkB,GAAA;AAAA"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../../../../src/instrumentations/replay/transport/types.ts"],"mappings":";UAAiB,qBAAA;EAAA,SACN,KAAA;EAAA,SACA,MAAA;EAAA,SACA,OAAA;EAAA,SACA,gBAAA;AAAA;AAAA,UAGM,SAAA;EAAA,SACN,SAAA;EAAA,SACA,QAAA;EAAA,SACA,YAAA;EAAA,SACA,UAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA,GAAa,SAAA,eAAwB,OAAA;EACrC,eAAA,GACE,GAAA,EAAK,MAAA,mBACL,SAAA,eACG,OAAA;EACL,iBAAA,GACE,IAAA,EAAM,aAAA,CAAc,MAAA,oBACpB,SAAA,eACG,OAAA;EACL,eAAA,GACE,IAAA,EAAM,SAAA,EACN,OAAA,EAAS,UAAA,EACT,SAAA,eACG,OAAA;EACL,eAAA,GAAkB,GAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  //#region src/instrumentations/replay/transport/types.d.ts
2
2
  interface ReplayTransportConfig {
3
3
  readonly appID: string;
4
- readonly userID: string;
4
+ readonly apiKey?: string;
5
5
  readonly baseUrl: string;
6
6
  readonly otelCollectorURL: string;
7
7
  }
@@ -13,6 +13,7 @@ interface ChunkMeta {
13
13
  readonly durationMs: number;
14
14
  }
15
15
  interface ReplayTransport {
16
+ postReplay: (keepalive?: boolean) => Promise<void>;
16
17
  postSessionMeta: (row: Record<string, unknown>, keepalive?: boolean) => Promise<void>;
17
18
  postSessionEvents: (rows: ReadonlyArray<Record<string, unknown>>, keepalive?: boolean) => Promise<void>;
18
19
  postSessionBlob: (meta: ChunkMeta, gzipped: Uint8Array, keepalive?: boolean) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/instrumentations/replay/transport/types.ts"],"mappings":";UAAiB,qBAAA;EAAA,SACN,KAAA;EAAA,SACA,MAAA;EAAA,SACA,OAAA;EAAA,SACA,gBAAA;AAAA;AAAA,UAGM,SAAA;EAAA,SACN,SAAA;EAAA,SACA,QAAA;EAAA,SACA,YAAA;EAAA,SACA,UAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGM,eAAA;EACf,eAAA,GACE,GAAA,EAAK,MAAA,mBACL,SAAA,eACG,OAAA;EACL,iBAAA,GACE,IAAA,EAAM,aAAA,CAAc,MAAA,oBACpB,SAAA,eACG,OAAA;EACL,eAAA,GACE,IAAA,EAAM,SAAA,EACN,OAAA,EAAS,UAAA,EACT,SAAA,eACG,OAAA;EACL,eAAA,GAAkB,GAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/instrumentations/replay/transport/types.ts"],"mappings":";UAAiB,qBAAA;EAAA,SACN,KAAA;EAAA,SACA,MAAA;EAAA,SACA,OAAA;EAAA,SACA,gBAAA;AAAA;AAAA,UAGM,SAAA;EAAA,SACN,SAAA;EAAA,SACA,QAAA;EAAA,SACA,YAAA;EAAA,SACA,UAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA,GAAa,SAAA,eAAwB,OAAA;EACrC,eAAA,GACE,GAAA,EAAK,MAAA,mBACL,SAAA,eACG,OAAA;EACL,iBAAA,GACE,IAAA,EAAM,aAAA,CAAc,MAAA,oBACpB,SAAA,eACG,OAAA;EACL,eAAA,GACE,IAAA,EAAM,SAAA,EACN,OAAA,EAAS,UAAA,EACT,SAAA,eACG,OAAA;EACL,eAAA,GAAkB,GAAA;AAAA"}