swetrix 4.2.0 → 4.3.0

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.
@@ -90,11 +90,29 @@ const getPath = (options) => {
90
90
  return result;
91
91
  };
92
92
 
93
+ const SESSION_REPLAY_PRIVACY_VALUES = ['total', 'normal', 'none'];
93
94
  const defaultActions = {
94
95
  stop() { },
95
96
  };
97
+ const defaultSessionReplayActions = {
98
+ async stop() { },
99
+ async flush() { },
100
+ };
96
101
  const DEFAULT_API_HOST = 'https://api.swetrix.com/log';
97
102
  const DEFAULT_API_BASE = 'https://api.swetrix.com';
103
+ const DEFAULT_RRWEB_FILE = 'replaylibrary.min.js';
104
+ const DEFAULT_RRWEB_URL = `https://cdn.jsdelivr.net/npm/swetrix@latest/dist/${DEFAULT_RRWEB_FILE}`;
105
+ const DEFAULT_SESSION_REPLAY_FLUSH_INTERVAL = 5000;
106
+ const DEFAULT_SESSION_REPLAY_MAX_EVENTS = 100;
107
+ const DEFAULT_SESSION_REPLAY_PRIVACY = 'total';
108
+ const SESSION_REPLAY_ACTIVITY_EVENTS = [
109
+ 'click',
110
+ 'keydown',
111
+ 'mousedown',
112
+ 'mousemove',
113
+ 'scroll',
114
+ 'touchstart',
115
+ ];
98
116
  // Default cache duration: 5 minutes
99
117
  const DEFAULT_CACHE_DURATION = 5 * 60 * 1000;
100
118
  class Lib {
@@ -108,9 +126,15 @@ class Lib {
108
126
  this.activePage = null;
109
127
  this.errorListenerExists = false;
110
128
  this.cachedData = null;
129
+ this.rrwebLoader = null;
130
+ this.sessionReplayActions = null;
131
+ this.sessionReplayInitPromise = null;
111
132
  this.trackPathChange = this.trackPathChange.bind(this);
112
133
  this.heartbeat = this.heartbeat.bind(this);
113
134
  this.captureError = this.captureError.bind(this);
135
+ if (this.getSessionReplayPreloadOption()) {
136
+ void this.preloadSessionReplay().catch(() => undefined);
137
+ }
114
138
  }
115
139
  captureError(event) {
116
140
  var _a, _b, _c, _d;
@@ -258,10 +282,10 @@ class Lib {
258
282
  };
259
283
  }
260
284
  /**
261
- * Fetches all feature flags and experiments for the project.
262
- * Results are cached for 5 minutes by default.
285
+ * Fetches all feature flags for the project.
286
+ * Results are cached for 5 minutes by default and share a cache with experiments.
263
287
  *
264
- * @param options - Options for evaluating feature flags.
288
+ * @param options - Options for evaluating feature flags (`profileId` only).
265
289
  * @param forceRefresh - If true, bypasses the cache and fetches fresh data.
266
290
  * @returns A promise that resolves to a record of flag keys to boolean values.
267
291
  */
@@ -328,8 +352,8 @@ class Lib {
328
352
  * Gets the value of a single feature flag.
329
353
  *
330
354
  * @param key - The feature flag key.
331
- * @param options - Options for evaluating the feature flag.
332
- * @param defaultValue - Default value to return if the flag is not found. Defaults to false.
355
+ * @param options - Options for evaluating the feature flag (`profileId` only).
356
+ * @param defaultValue - Optional default value to return if the flag is not found. Defaults to false.
333
357
  * @returns A promise that resolves to the boolean value of the flag.
334
358
  */
335
359
  async getFeatureFlag(key, options, defaultValue = false) {
@@ -344,16 +368,16 @@ class Lib {
344
368
  this.cachedData = null;
345
369
  }
346
370
  /**
347
- * Fetches all A/B test experiments for the project.
371
+ * Fetches variant assignments for running A/B test experiments returned by feature flag evaluation.
348
372
  * Results are cached for 5 minutes by default (shared cache with feature flags).
349
373
  *
350
- * @param options - Options for evaluating experiments.
374
+ * @param options - Options for evaluating experiments (`profileId` only).
351
375
  * @param forceRefresh - If true, bypasses the cache and fetches fresh data.
352
376
  * @returns A promise that resolves to a record of experiment IDs to variant keys.
353
377
  *
354
378
  * @example
355
379
  * ```typescript
356
- * const experiments = await getExperiments()
380
+ * const experiments = await getExperiments({ profileId: 'user-123' })
357
381
  * // experiments = { 'exp-123': 'variant-a', 'exp-456': 'control' }
358
382
  * ```
359
383
  */
@@ -384,13 +408,16 @@ class Lib {
384
408
  * Gets the variant key for a specific A/B test experiment.
385
409
  *
386
410
  * @param experimentId - The experiment ID.
387
- * @param options - Options for evaluating the experiment.
388
- * @param defaultVariant - Default variant key to return if the experiment is not found. Defaults to null.
411
+ * @param options - Options for evaluating the experiment (`profileId` only).
412
+ * @param defaultVariant - Optional default variant key to return if the experiment is not found. Defaults to null.
389
413
  * @returns A promise that resolves to the variant key assigned to this user, or defaultVariant if not found.
390
414
  *
391
415
  * @example
392
416
  * ```typescript
393
- * const variant = await getExperiment('checkout-redesign')
417
+ * const variant = await getExperiment('checkout-redesign', { profileId: 'user-123' })
418
+ *
419
+ * // Optional fallback variant:
420
+ * const variantWithFallback = await getExperiment('checkout-redesign', undefined, 'control')
394
421
  *
395
422
  * if (variant === 'new-checkout') {
396
423
  * // Show new checkout flow
@@ -506,6 +533,167 @@ class Lib {
506
533
  return null;
507
534
  }
508
535
  }
536
+ async startSessionReplay(options = {}) {
537
+ if (this.sessionReplayActions) {
538
+ return this.sessionReplayActions;
539
+ }
540
+ if (this.sessionReplayInitPromise) {
541
+ return this.sessionReplayInitPromise;
542
+ }
543
+ const initPromise = this.initialiseSessionReplay(options);
544
+ this.sessionReplayInitPromise = initPromise;
545
+ try {
546
+ return await initPromise;
547
+ }
548
+ finally {
549
+ if (this.sessionReplayInitPromise === initPromise) {
550
+ this.sessionReplayInitPromise = null;
551
+ }
552
+ }
553
+ }
554
+ async initialiseSessionReplay(options) {
555
+ var _a;
556
+ if (this.sessionReplayActions) {
557
+ return this.sessionReplayActions;
558
+ }
559
+ if (!this.canTrack()) {
560
+ return defaultSessionReplayActions;
561
+ }
562
+ if (!this.shouldSampleSessionReplay(options.sampleRate)) {
563
+ return defaultSessionReplayActions;
564
+ }
565
+ try {
566
+ await this.preloadSessionReplay();
567
+ }
568
+ catch (_b) {
569
+ return defaultSessionReplayActions;
570
+ }
571
+ const rrweb = window.rrweb;
572
+ if (!(rrweb === null || rrweb === void 0 ? void 0 : rrweb.record)) {
573
+ return defaultSessionReplayActions;
574
+ }
575
+ const privacy = this.getSessionReplayPrivacy(options.privacy);
576
+ const replayId = this.createReplayId();
577
+ const started = await this.sendSessionReplayStart(replayId, privacy);
578
+ if (!started) {
579
+ return defaultSessionReplayActions;
580
+ }
581
+ const flushIntervalMs = typeof options.flushIntervalMs === 'number' && options.flushIntervalMs > 0
582
+ ? options.flushIntervalMs
583
+ : DEFAULT_SESSION_REPLAY_FLUSH_INTERVAL;
584
+ const maxEventsPerChunk = typeof options.maxEventsPerChunk === 'number' &&
585
+ options.maxEventsPerChunk > 0
586
+ ? Math.floor(options.maxEventsPerChunk)
587
+ : DEFAULT_SESSION_REPLAY_MAX_EVENTS;
588
+ const maxDurationMs = typeof options.maxDurationMs === 'number' && options.maxDurationMs > 0
589
+ ? options.maxDurationMs
590
+ : null;
591
+ const idleTimeoutMs = typeof options.idleTimeoutMs === 'number' && options.idleTimeoutMs > 0
592
+ ? options.idleTimeoutMs
593
+ : null;
594
+ let chunkIndex = 0;
595
+ let stopped = false;
596
+ let events = [];
597
+ let flushing = Promise.resolve();
598
+ let maxDurationTimer;
599
+ let idleTimer;
600
+ const flush = async (useBeacon = false) => {
601
+ if (!events.length)
602
+ return;
603
+ const chunk = events;
604
+ events = [];
605
+ const currentChunkIndex = chunkIndex++;
606
+ flushing = flushing
607
+ .catch(() => undefined)
608
+ .then(() => this.sendSessionReplayChunk(replayId, privacy, currentChunkIndex, chunk, useBeacon));
609
+ await flushing;
610
+ };
611
+ const userEmit = (_a = options.rrweb) === null || _a === void 0 ? void 0 : _a.emit;
612
+ const recordOptions = this.getSessionReplayRecordOptions(privacy, options.rrweb, (event) => {
613
+ try {
614
+ userEmit === null || userEmit === void 0 ? void 0 : userEmit(event);
615
+ }
616
+ catch (_a) { }
617
+ events.push(event);
618
+ if (events.length >= maxEventsPerChunk) {
619
+ void flush();
620
+ }
621
+ });
622
+ const stopRecording = rrweb.record(recordOptions);
623
+ const timer = setInterval(() => void flush(), flushIntervalMs);
624
+ const flushOnPageExit = () => void flush(true);
625
+ const flushOnHidden = () => {
626
+ if (document.visibilityState === 'hidden') {
627
+ void flush(true);
628
+ }
629
+ };
630
+ const clearIdleTimer = () => {
631
+ if (idleTimer) {
632
+ clearTimeout(idleTimer);
633
+ idleTimer = undefined;
634
+ }
635
+ };
636
+ const stopSessionReplay = async () => {
637
+ if (stopped)
638
+ return;
639
+ stopped = true;
640
+ clearInterval(timer);
641
+ if (maxDurationTimer) {
642
+ clearTimeout(maxDurationTimer);
643
+ }
644
+ clearIdleTimer();
645
+ window.removeEventListener('pagehide', flushOnPageExit);
646
+ document.removeEventListener('visibilitychange', flushOnHidden);
647
+ SESSION_REPLAY_ACTIVITY_EVENTS.forEach((eventName) => {
648
+ window.removeEventListener(eventName, resetIdleTimer);
649
+ });
650
+ stopRecording === null || stopRecording === void 0 ? void 0 : stopRecording();
651
+ await flush();
652
+ this.sessionReplayActions = null;
653
+ this.sessionReplayInitPromise = null;
654
+ };
655
+ const resetIdleTimer = () => {
656
+ if (!idleTimeoutMs || stopped)
657
+ return;
658
+ clearIdleTimer();
659
+ idleTimer = setTimeout(() => void stopSessionReplay(), idleTimeoutMs);
660
+ };
661
+ window.addEventListener('pagehide', flushOnPageExit);
662
+ document.addEventListener('visibilitychange', flushOnHidden);
663
+ if (maxDurationMs) {
664
+ maxDurationTimer = setTimeout(() => void stopSessionReplay(), maxDurationMs);
665
+ }
666
+ if (idleTimeoutMs) {
667
+ SESSION_REPLAY_ACTIVITY_EVENTS.forEach((eventName) => {
668
+ window.addEventListener(eventName, resetIdleTimer, { passive: true });
669
+ });
670
+ resetIdleTimer();
671
+ }
672
+ this.sessionReplayActions = {
673
+ stop: stopSessionReplay,
674
+ flush: async () => {
675
+ await flush();
676
+ },
677
+ };
678
+ return this.sessionReplayActions;
679
+ }
680
+ shouldSampleSessionReplay(sampleRate) {
681
+ if (typeof sampleRate !== 'number') {
682
+ return true;
683
+ }
684
+ if (sampleRate <= 0) {
685
+ return false;
686
+ }
687
+ if (sampleRate >= 1) {
688
+ return true;
689
+ }
690
+ return Math.random() < sampleRate;
691
+ }
692
+ getSessionReplayPrivacy(privacy) {
693
+ return SESSION_REPLAY_PRIVACY_VALUES.includes(privacy)
694
+ ? privacy
695
+ : DEFAULT_SESSION_REPLAY_PRIVACY;
696
+ }
509
697
  /**
510
698
  * Gets the API base URL (without /log suffix).
511
699
  */
@@ -595,6 +783,225 @@ class Lib {
595
783
  }
596
784
  return true;
597
785
  }
786
+ getSessionReplayUrl() {
787
+ const replayOption = this.getSessionReplayPreloadOption();
788
+ if (replayOption &&
789
+ typeof replayOption === 'object' &&
790
+ replayOption.rrwebUrl) {
791
+ return replayOption.rrwebUrl;
792
+ }
793
+ return this.getDefaultSessionReplayUrl();
794
+ }
795
+ getSessionReplayPreloadOption() {
796
+ var _a;
797
+ return (_a = this.options) === null || _a === void 0 ? void 0 : _a.preloadSessionReplay;
798
+ }
799
+ getDefaultSessionReplayUrl() {
800
+ if (!isInBrowser()) {
801
+ return DEFAULT_RRWEB_URL;
802
+ }
803
+ const trackerScript = this.getTrackerScript();
804
+ if (trackerScript === null || trackerScript === void 0 ? void 0 : trackerScript.src) {
805
+ const { hostname, pathname } = new URL(trackerScript.src);
806
+ if (hostname === 'swetrix.org' &&
807
+ /^\/swetrix(\.min)?\.js$/i.test(pathname)) {
808
+ return DEFAULT_RRWEB_URL;
809
+ }
810
+ return new URL(DEFAULT_RRWEB_FILE, trackerScript.src).toString();
811
+ }
812
+ return DEFAULT_RRWEB_URL;
813
+ }
814
+ getTrackerScript() {
815
+ const trackerScript = Array.from(document.scripts).find((script) => {
816
+ if (!script.src) {
817
+ return false;
818
+ }
819
+ try {
820
+ const { pathname } = new URL(script.src);
821
+ return /(^|\/)swetrix(\.min)?\.js$/i.test(pathname);
822
+ }
823
+ catch (_a) {
824
+ return false;
825
+ }
826
+ });
827
+ return trackerScript;
828
+ }
829
+ preloadSessionReplay() {
830
+ var _a;
831
+ if (!isInBrowser()) {
832
+ return Promise.resolve();
833
+ }
834
+ if ((_a = window.rrweb) === null || _a === void 0 ? void 0 : _a.record) {
835
+ return Promise.resolve();
836
+ }
837
+ if (this.rrwebLoader) {
838
+ return this.rrwebLoader;
839
+ }
840
+ if (window.__SWETRIX_RRWEB_LOADING__) {
841
+ this.rrwebLoader = window.__SWETRIX_RRWEB_LOADING__;
842
+ void this.rrwebLoader.catch(() => {
843
+ if (window.__SWETRIX_RRWEB_LOADING__ === this.rrwebLoader) {
844
+ delete window.__SWETRIX_RRWEB_LOADING__;
845
+ }
846
+ this.rrwebLoader = null;
847
+ });
848
+ return this.rrwebLoader;
849
+ }
850
+ this.rrwebLoader = this.loadSessionReplayRecorder();
851
+ window.__SWETRIX_RRWEB_LOADING__ = this.rrwebLoader;
852
+ const loader = this.rrwebLoader;
853
+ void loader.catch(() => {
854
+ if (window.__SWETRIX_RRWEB_LOADING__ === loader) {
855
+ delete window.__SWETRIX_RRWEB_LOADING__;
856
+ }
857
+ if (this.rrwebLoader === loader) {
858
+ this.rrwebLoader = null;
859
+ }
860
+ });
861
+ return this.rrwebLoader;
862
+ }
863
+ async loadSessionReplayRecorder() {
864
+ const replayOption = this.getSessionReplayPreloadOption();
865
+ const hasCustomReplayUrl = replayOption && typeof replayOption === 'object' && replayOption.rrwebUrl;
866
+ if (hasCustomReplayUrl || this.getTrackerScript()) {
867
+ await this.loadSessionReplayScript(this.getSessionReplayUrl());
868
+ return;
869
+ }
870
+ if (await this.loadSessionReplayPackage()) {
871
+ return;
872
+ }
873
+ await this.loadSessionReplayScript(this.getDefaultSessionReplayUrl());
874
+ }
875
+ async loadSessionReplayPackage() {
876
+ try {
877
+ const rrwebModule = (await import('rrweb'));
878
+ const rrweb = rrwebModule.record ? rrwebModule : rrwebModule.default;
879
+ if (!(rrweb === null || rrweb === void 0 ? void 0 : rrweb.record)) {
880
+ return false;
881
+ }
882
+ window.rrweb = rrweb;
883
+ return true;
884
+ }
885
+ catch (_a) {
886
+ return false;
887
+ }
888
+ }
889
+ loadSessionReplayScript(url) {
890
+ return new Promise((resolve, reject) => {
891
+ const script = document.createElement('script');
892
+ script.async = true;
893
+ script.src = url;
894
+ script.crossOrigin = 'anonymous';
895
+ script.onload = () => resolve();
896
+ script.onerror = () => reject(new Error('Failed to load rrweb'));
897
+ document.head.appendChild(script);
898
+ });
899
+ }
900
+ getSessionReplayRecordOptions(privacy, userOptions, emit) {
901
+ const options = {
902
+ ...userOptions,
903
+ emit,
904
+ };
905
+ const maskInputOptions = typeof options.maskInputOptions === 'object' &&
906
+ options.maskInputOptions !== null
907
+ ? options.maskInputOptions
908
+ : {};
909
+ const resolvedPrivacy = this.getSessionReplayPrivacy(privacy);
910
+ if (resolvedPrivacy === 'total') {
911
+ return {
912
+ ...options,
913
+ maskAllInputs: true,
914
+ maskTextSelector: '*',
915
+ blockSelector: this.mergeSelectors(options.blockSelector, 'img, picture, video, audio, canvas, svg'),
916
+ recordCanvas: false,
917
+ inlineImages: false,
918
+ emit,
919
+ };
920
+ }
921
+ if (resolvedPrivacy === 'normal') {
922
+ return {
923
+ ...options,
924
+ maskAllInputs: true,
925
+ emit,
926
+ };
927
+ }
928
+ return {
929
+ ...options,
930
+ maskInputOptions: {
931
+ ...maskInputOptions,
932
+ password: true,
933
+ },
934
+ emit,
935
+ };
936
+ }
937
+ mergeSelectors(existing, required) {
938
+ if (typeof existing === 'string' && existing.trim()) {
939
+ return `${existing}, ${required}`;
940
+ }
941
+ return required;
942
+ }
943
+ createReplayId() {
944
+ if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
945
+ return crypto.randomUUID();
946
+ }
947
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
948
+ }
949
+ async sendSessionReplayStart(replayId, privacy) {
950
+ var _a, _b, _c;
951
+ try {
952
+ const apiBase = this.getApiBase();
953
+ const response = await fetch(`${apiBase}/log/session-replay/start`, {
954
+ method: 'POST',
955
+ headers: {
956
+ 'Content-Type': 'application/json',
957
+ },
958
+ body: JSON.stringify({
959
+ pid: this.projectID,
960
+ replayId,
961
+ privacy,
962
+ pg: this.activePage ||
963
+ getPath({
964
+ hash: (_a = this.pageViewsOptions) === null || _a === void 0 ? void 0 : _a.hash,
965
+ search: (_b = this.pageViewsOptions) === null || _b === void 0 ? void 0 : _b.search,
966
+ }),
967
+ lc: getLocale(),
968
+ tz: getTimezone(),
969
+ profileId: (_c = this.options) === null || _c === void 0 ? void 0 : _c.profileId,
970
+ }),
971
+ });
972
+ return response.ok;
973
+ }
974
+ catch (_d) {
975
+ return false;
976
+ }
977
+ }
978
+ async sendSessionReplayChunk(replayId, privacy, chunkIndex, events, useBeacon) {
979
+ const apiBase = this.getApiBase();
980
+ const url = `${apiBase}/log/session-replay/chunk`;
981
+ const payload = JSON.stringify({
982
+ pid: this.projectID,
983
+ replayId,
984
+ privacy,
985
+ chunkIndex,
986
+ events,
987
+ });
988
+ if (useBeacon && typeof navigator.sendBeacon === 'function') {
989
+ const sent = navigator.sendBeacon(url, new Blob([payload], { type: 'application/json' }));
990
+ if (sent)
991
+ return;
992
+ }
993
+ try {
994
+ await fetch(url, {
995
+ method: 'POST',
996
+ headers: {
997
+ 'Content-Type': 'application/json',
998
+ },
999
+ keepalive: useBeacon,
1000
+ body: payload,
1001
+ });
1002
+ }
1003
+ catch (_a) { }
1004
+ }
598
1005
  async sendRequest(path, body) {
599
1006
  var _a;
600
1007
  const host = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.apiURL) || DEFAULT_API_HOST;
@@ -670,6 +1077,12 @@ function trackErrors(options) {
670
1077
  }
671
1078
  return exports.LIB_INSTANCE.trackErrors(options);
672
1079
  }
1080
+ function startSessionReplay(options) {
1081
+ if (!exports.LIB_INSTANCE) {
1082
+ return Promise.resolve(defaultSessionReplayActions);
1083
+ }
1084
+ return exports.LIB_INSTANCE.startSessionReplay(options);
1085
+ }
673
1086
  /**
674
1087
  * This function is used to manually track an error event.
675
1088
  * It's useful if you want to track specific errors in your application.
@@ -704,17 +1117,16 @@ function pageview(options) {
704
1117
  }
705
1118
  /**
706
1119
  * Fetches all feature flags for the project.
707
- * Results are cached for 5 minutes by default.
1120
+ * Results are cached for 5 minutes by default and share a cache with experiments.
708
1121
  *
709
- * @param options - Options for evaluating feature flags (visitorId, attributes).
1122
+ * @param options - Options for evaluating feature flags (`profileId` only).
710
1123
  * @param forceRefresh - If true, bypasses the cache and fetches fresh flags.
711
1124
  * @returns A promise that resolves to a record of flag keys to boolean values.
712
1125
  *
713
1126
  * @example
714
1127
  * ```typescript
715
1128
  * const flags = await getFeatureFlags({
716
- * visitorId: 'user-123',
717
- * attributes: { cc: 'US', dv: 'desktop' }
1129
+ * profileId: 'user-123'
718
1130
  * })
719
1131
  *
720
1132
  * if (flags['new-checkout']) {
@@ -731,13 +1143,13 @@ async function getFeatureFlags(options, forceRefresh) {
731
1143
  * Gets the value of a single feature flag.
732
1144
  *
733
1145
  * @param key - The feature flag key.
734
- * @param options - Options for evaluating the feature flag (visitorId, attributes).
735
- * @param defaultValue - Default value to return if the flag is not found. Defaults to false.
1146
+ * @param options - Options for evaluating the feature flag (`profileId` only).
1147
+ * @param defaultValue - Optional default value to return if the flag is not found. Defaults to false.
736
1148
  * @returns A promise that resolves to the boolean value of the flag.
737
1149
  *
738
1150
  * @example
739
1151
  * ```typescript
740
- * const isEnabled = await getFeatureFlag('dark-mode', { visitorId: 'user-123' })
1152
+ * const isEnabled = await getFeatureFlag('dark-mode', { profileId: 'user-123' })
741
1153
  *
742
1154
  * if (isEnabled) {
743
1155
  * // Enable dark mode
@@ -750,8 +1162,8 @@ async function getFeatureFlag(key, options, defaultValue = false) {
750
1162
  return exports.LIB_INSTANCE.getFeatureFlag(key, options, defaultValue);
751
1163
  }
752
1164
  /**
753
- * Clears the cached feature flags, forcing a fresh fetch on the next call.
754
- * Useful when you know the user's context has changed significantly.
1165
+ * Clears the cached feature flags and experiments, forcing a fresh fetch on the next call.
1166
+ * Useful when you know the user's profile has changed.
755
1167
  */
756
1168
  function clearFeatureFlagsCache() {
757
1169
  if (!exports.LIB_INSTANCE)
@@ -759,10 +1171,10 @@ function clearFeatureFlagsCache() {
759
1171
  exports.LIB_INSTANCE.clearFeatureFlagsCache();
760
1172
  }
761
1173
  /**
762
- * Fetches all A/B test experiments for the project.
1174
+ * Fetches variant assignments for running A/B test experiments returned by feature flag evaluation.
763
1175
  * Results are cached for 5 minutes by default (shared cache with feature flags).
764
1176
  *
765
- * @param options - Options for evaluating experiments.
1177
+ * @param options - Options for evaluating experiments (`profileId` only).
766
1178
  * @param forceRefresh - If true, bypasses the cache and fetches fresh data.
767
1179
  * @returns A promise that resolves to a record of experiment IDs to variant keys.
768
1180
  *
@@ -789,13 +1201,16 @@ async function getExperiments(options, forceRefresh) {
789
1201
  * Gets the variant key for a specific A/B test experiment.
790
1202
  *
791
1203
  * @param experimentId - The experiment ID.
792
- * @param options - Options for evaluating the experiment.
793
- * @param defaultVariant - Default variant key to return if the experiment is not found. Defaults to null.
1204
+ * @param options - Options for evaluating the experiment (`profileId` only).
1205
+ * @param defaultVariant - Optional default variant key to return if the experiment is not found. Defaults to null.
794
1206
  * @returns A promise that resolves to the variant key assigned to this user, or defaultVariant if not found.
795
1207
  *
796
1208
  * @example
797
1209
  * ```typescript
798
- * const variant = await getExperiment('checkout-redesign-experiment-id')
1210
+ * const variant = await getExperiment('checkout-redesign-experiment-id', { profileId: 'user-123' })
1211
+ *
1212
+ * // Optional fallback variant:
1213
+ * const variantWithFallback = await getExperiment('checkout-redesign-experiment-id', undefined, 'control')
799
1214
  *
800
1215
  * if (variant === 'new-checkout') {
801
1216
  * // Show new checkout flow
@@ -889,6 +1304,7 @@ exports.getProfileId = getProfileId;
889
1304
  exports.getSessionId = getSessionId;
890
1305
  exports.init = init;
891
1306
  exports.pageview = pageview;
1307
+ exports.startSessionReplay = startSessionReplay;
892
1308
  exports.track = track;
893
1309
  exports.trackError = trackError;
894
1310
  exports.trackErrors = trackErrors;