posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
734 lines • 32.6 kB
JavaScript
"use strict";
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TriggerGroupMatching = exports.EventTriggerMatching = exports.LinkedFlagMatching = exports.URLTriggerMatching = exports.AlwaysActivatedTriggerMatching = exports.PendingTriggerMatching = exports.AndTriggerMatching = exports.OrTriggerMatching = exports.TRIGGER_DISABLED = exports.TRIGGER_PENDING = exports.TRIGGER_ACTIVATED = exports.RRWEB_ERROR = exports.MISSING_CONFIG = exports.AWAITING_CONFIG = exports.LAZY_LOADING = exports.PAUSED = exports.BUFFERING = exports.ACTIVE = exports.SAMPLED = exports.DISABLED = void 0;
exports.nullMatchSessionRecordingStatus = nullMatchSessionRecordingStatus;
exports.anyMatchSessionRecordingStatus = anyMatchSessionRecordingStatus;
exports.allMatchSessionRecordingStatus = allMatchSessionRecordingStatus;
exports.triggerGroupsMatchSessionRecordingStatus = triggerGroupsMatchSessionRecordingStatus;
var constants_1 = require("../../../constants");
var core_1 = require("@posthog/core");
var logger_1 = require("@posthog/browser-common/utils/logger");
var url_targeting_utils_1 = require("@posthog/browser-common/utils/url-targeting-utils");
exports.DISABLED = 'disabled';
exports.SAMPLED = 'sampled';
exports.ACTIVE = 'active';
exports.BUFFERING = 'buffering';
exports.PAUSED = 'paused';
exports.LAZY_LOADING = 'lazy_loading';
exports.AWAITING_CONFIG = 'awaiting_config';
exports.MISSING_CONFIG = 'missing_config';
exports.RRWEB_ERROR = 'rrweb_error';
var TRIGGER = 'trigger';
exports.TRIGGER_ACTIVATED = TRIGGER + '_activated';
exports.TRIGGER_PENDING = TRIGGER + '_pending';
exports.TRIGGER_DISABLED = TRIGGER + '_' + exports.DISABLED;
/*
triggers can have one of three statuses:
* - trigger_activated: the trigger met conditions to start recording
* - trigger_pending: the trigger is present, but the conditions are not yet met
* - trigger_disabled: the trigger is not present
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
var triggerStatuses = [exports.TRIGGER_ACTIVATED, exports.TRIGGER_PENDING, exports.TRIGGER_DISABLED];
function persistedTriggerStatus(instance, triggerCount, groupId, groupPrefix, fallbackPersistenceKey, sessionId) {
if (triggerCount === 0) {
return exports.TRIGGER_DISABLED;
}
// V2: Use per-group persistence key if groupId is provided
var persistenceKey = groupId ? groupPrefix + groupId : fallbackPersistenceKey;
var currentTriggerSession = instance === null || instance === void 0 ? void 0 : instance.get_property(persistenceKey);
return currentTriggerSession === sessionId ? exports.TRIGGER_ACTIVATED : exports.TRIGGER_PENDING;
}
/**
* Session recording starts in buffering mode while waiting for "flags response".
* Once the response is received, it might be disabled, active or sampled.
* When "sampled" that means a sample rate is set, and the last time the session ID rotated
* the sample rate determined this session should be sent to the server.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
var sessionRecordingStatuses = [
exports.DISABLED,
exports.SAMPLED,
exports.ACTIVE,
exports.BUFFERING,
exports.PAUSED,
exports.LAZY_LOADING,
exports.AWAITING_CONFIG,
exports.MISSING_CONFIG,
exports.RRWEB_ERROR,
];
function sessionRecordingUrlTriggerMatches(url, triggers, compiledRegexCache) {
return triggers.some(function (trigger) {
var _a;
switch (trigger.matching) {
case 'regex': {
var regex = (_a = compiledRegexCache === null || compiledRegexCache === void 0 ? void 0 : compiledRegexCache.get(trigger.url)) !== null && _a !== void 0 ? _a : new RegExp(trigger.url);
return regex.test(url);
}
default:
return false;
}
});
}
function orTriggerStatus(statuses) {
if (statuses.includes(exports.TRIGGER_ACTIVATED)) {
return exports.TRIGGER_ACTIVATED;
}
if (statuses.includes(exports.TRIGGER_PENDING)) {
return exports.TRIGGER_PENDING;
}
return exports.TRIGGER_DISABLED;
}
function andTriggerStatus(statuses) {
var enabledStatuses = new Set(statuses);
// trigger_disabled means no config
enabledStatuses.delete(exports.TRIGGER_DISABLED);
switch (enabledStatuses.size) {
case 0:
return exports.TRIGGER_DISABLED;
case 1:
return Array.from(enabledStatuses)[0];
default:
return exports.TRIGGER_PENDING;
}
}
var OrTriggerMatching = /** @class */ (function () {
function OrTriggerMatching(_matchers) {
this._matchers = _matchers;
}
OrTriggerMatching.prototype.triggerStatus = function (sessionId) {
return orTriggerStatus(this._matchers.map(function (matcher) { return matcher.triggerStatus(sessionId); }));
};
OrTriggerMatching.prototype.triggerStatusNoSideEffects = function (sessionId) {
return orTriggerStatus(this._matchers.map(function (matcher) { return matcher.triggerStatusNoSideEffects(sessionId); }));
};
OrTriggerMatching.prototype.stop = function () {
this._matchers.forEach(function (m) { return m.stop(); });
};
return OrTriggerMatching;
}());
exports.OrTriggerMatching = OrTriggerMatching;
var AndTriggerMatching = /** @class */ (function () {
function AndTriggerMatching(_matchers) {
this._matchers = _matchers;
}
AndTriggerMatching.prototype.triggerStatus = function (sessionId) {
return andTriggerStatus(this._matchers.map(function (matcher) { return matcher.triggerStatus(sessionId); }));
};
AndTriggerMatching.prototype.triggerStatusNoSideEffects = function (sessionId) {
return andTriggerStatus(this._matchers.map(function (matcher) { return matcher.triggerStatusNoSideEffects(sessionId); }));
};
AndTriggerMatching.prototype.stop = function () {
this._matchers.forEach(function (m) { return m.stop(); });
};
return AndTriggerMatching;
}());
exports.AndTriggerMatching = AndTriggerMatching;
var PendingTriggerMatching = /** @class */ (function () {
function PendingTriggerMatching() {
}
PendingTriggerMatching.prototype.triggerStatus = function () {
return this.triggerStatusNoSideEffects();
};
PendingTriggerMatching.prototype.triggerStatusNoSideEffects = function () {
return exports.TRIGGER_PENDING;
};
PendingTriggerMatching.prototype.stop = function () {
// no-op
};
return PendingTriggerMatching;
}());
exports.PendingTriggerMatching = PendingTriggerMatching;
var AlwaysActivatedTriggerMatching = /** @class */ (function () {
function AlwaysActivatedTriggerMatching() {
}
AlwaysActivatedTriggerMatching.prototype.triggerStatus = function () {
return this.triggerStatusNoSideEffects();
};
AlwaysActivatedTriggerMatching.prototype.triggerStatusNoSideEffects = function () {
return exports.TRIGGER_ACTIVATED;
};
AlwaysActivatedTriggerMatching.prototype.stop = function () {
// no-op
};
return AlwaysActivatedTriggerMatching;
}());
exports.AlwaysActivatedTriggerMatching = AlwaysActivatedTriggerMatching;
var isEagerLoadedConfig = function (x) {
return 'sessionRecording' in x;
};
var URLTriggerMatching = /** @class */ (function () {
function URLTriggerMatching(_instance, groupId) {
this._instance = _instance;
this._urlTriggers = [];
this._urlBlocklist = [];
this._compiledTriggerRegexes = new Map();
this._compiledBlocklistRegexes = new Map();
this._lastCheckedUrl = '';
this.urlBlocked = false;
this._groupId = groupId;
}
URLTriggerMatching.prototype.onConfig = function (config) {
var _a, _b;
this._urlTriggers =
(isEagerLoadedConfig(config)
? (0, core_1.isObject)(config.sessionRecording)
? (_a = config.sessionRecording) === null || _a === void 0 ? void 0 : _a.urlTriggers
: []
: config === null || config === void 0 ? void 0 : config.urlTriggers) || [];
this._urlBlocklist =
(isEagerLoadedConfig(config)
? (0, core_1.isObject)(config.sessionRecording)
? (_b = config.sessionRecording) === null || _b === void 0 ? void 0 : _b.urlBlocklist
: []
: config === null || config === void 0 ? void 0 : config.urlBlocklist) || [];
this._compileRegexCache();
};
/**
* Compiles and caches RegExp objects from URL triggers and blocklist.
* This prevents recreating RegExp objects on every rrweb event
*/
URLTriggerMatching.prototype._compileRegexCache = function () {
var e_1, _a, e_2, _b;
this._compiledTriggerRegexes.clear();
this._compiledBlocklistRegexes.clear();
try {
for (var _c = __values(this._urlTriggers), _d = _c.next(); !_d.done; _d = _c.next()) {
var trigger = _d.value;
if (trigger.matching === 'regex' && !this._compiledTriggerRegexes.has(trigger.url)) {
try {
this._compiledTriggerRegexes.set(trigger.url, new RegExp(trigger.url));
}
catch (e) {
logger_1.logger.error('Invalid URL trigger regex pattern:', trigger.url, e);
}
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
try {
for (var _e = __values(this._urlBlocklist), _f = _e.next(); !_f.done; _f = _e.next()) {
var trigger = _f.value;
if (trigger.matching === 'regex' && !this._compiledBlocklistRegexes.has(trigger.url)) {
try {
this._compiledBlocklistRegexes.set(trigger.url, new RegExp(trigger.url));
}
catch (e) {
logger_1.logger.error('Invalid URL blocklist regex pattern:', trigger.url, e);
}
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
}
finally { if (e_2) throw e_2.error; }
}
};
/**
* @deprecated Use onConfig instead
*/
URLTriggerMatching.prototype.onRemoteConfig = function (response) {
this.onConfig(response);
};
URLTriggerMatching.prototype._urlTriggerStatus = function (sessionId) {
return persistedTriggerStatus(this._instance, this._urlTriggers.length, this._groupId, constants_1.SESSION_RECORDING_TRIGGER_V2_GROUP_URL_PREFIX, constants_1.SESSION_RECORDING_URL_TRIGGER_ACTIVATED_SESSION, sessionId);
};
URLTriggerMatching.prototype.triggerStatus = function (sessionId) {
var _a;
var result = this.triggerStatusNoSideEffects(sessionId);
this._instance.register_for_session((_a = {},
_a[constants_1.SDK_DEBUG_REPLAY_URL_TRIGGER_STATUS] = result,
_a));
return result;
};
/**
* Pure read of the trigger status. Unlike {@link triggerStatus} it does not write the debug
* session property, so a diagnostic that polls every leg (e.g. while buffering) can read it
* without adding a `register_for_session` persistence write per call.
*/
URLTriggerMatching.prototype.triggerStatusNoSideEffects = function (sessionId) {
return this._urlTriggerStatus(sessionId);
};
URLTriggerMatching.prototype.isCurrentUrlBlocked = function () {
var url = (0, url_targeting_utils_1.getTargetingUrl)(this._instance);
if (!url) {
return false;
}
try {
return sessionRecordingUrlTriggerMatches(url, this._urlBlocklist, this._compiledBlocklistRegexes);
}
catch (_a) {
return true;
}
};
/**
* Check URL blocklist and pause/resume recording accordingly
* This is separate from trigger checking and is used by both V1 and V2
*
* Performance optimization: Only checks when URL changes to avoid redundant regex matching
*/
URLTriggerMatching.prototype.checkUrlBlocklist = function (onPause, onResume) {
var url = (0, url_targeting_utils_1.getTargetingUrl)(this._instance);
if (!url) {
return;
}
// Performance optimization: Skip if URL hasn't changed since last check
if (url === this._lastCheckedUrl) {
return;
}
this._lastCheckedUrl = url;
// Check blocklist and call onPause/onResume
// Note: DON'T set this.urlBlocked here - let the callbacks (_pauseRecording/_resumeRecording) set it
var wasBlocked = this.urlBlocked;
var isNowBlocked = sessionRecordingUrlTriggerMatches(url, this._urlBlocklist, this._compiledBlocklistRegexes);
if (wasBlocked && isNowBlocked) {
return;
}
if (isNowBlocked && !wasBlocked) {
onPause();
}
else if (!isNowBlocked && wasBlocked) {
onResume();
}
};
URLTriggerMatching.prototype.checkUrlTriggerConditions = function (onPause, onResume, onActivate, sessionId) {
var url = (0, url_targeting_utils_1.getTargetingUrl)(this._instance);
if (!url) {
return;
}
// Performance optimization: Skip if URL hasn't changed since last check
// This prevents redundant checks on every rrweb event
if (url === this._lastCheckedUrl) {
return;
}
this._lastCheckedUrl = url;
// Check blocklist and call onPause/onResume
// Note: DON'T set this.urlBlocked here - let the callbacks (_pauseRecording/_resumeRecording) set it
var wasBlocked = this.urlBlocked;
var isNowBlocked = sessionRecordingUrlTriggerMatches(url, this._urlBlocklist, this._compiledBlocklistRegexes);
if (isNowBlocked && !wasBlocked) {
onPause();
}
else if (!isNowBlocked && wasBlocked) {
onResume();
}
// Check URL triggers
var isActivated = this._urlTriggerStatus(sessionId) === exports.TRIGGER_ACTIVATED;
var urlMatches = sessionRecordingUrlTriggerMatches(url, this._urlTriggers, this._compiledTriggerRegexes);
if (!isActivated && urlMatches) {
onActivate('url', url);
}
};
URLTriggerMatching.prototype.stop = function () {
this._lastCheckedUrl = '';
};
return URLTriggerMatching;
}());
exports.URLTriggerMatching = URLTriggerMatching;
var LinkedFlagMatching = /** @class */ (function () {
function LinkedFlagMatching(_instance) {
this._instance = _instance;
this.linkedFlag = null;
this.linkedFlagSeen = false;
this._flagListenerCleanup = function () { };
}
LinkedFlagMatching.prototype.triggerStatus = function () {
var _a;
var result = this.triggerStatusNoSideEffects();
this._instance.register_for_session((_a = {},
_a[constants_1.SDK_DEBUG_REPLAY_LINKED_FLAG_TRIGGER_STATUS] = result,
_a));
return result;
};
// Pure read of the trigger status — see URLTriggerMatching.triggerStatusNoSideEffects.
LinkedFlagMatching.prototype.triggerStatusNoSideEffects = function () {
var result = exports.TRIGGER_PENDING;
if ((0, core_1.isNullish)(this.linkedFlag)) {
result = exports.TRIGGER_DISABLED;
}
if (this.linkedFlagSeen) {
result = exports.TRIGGER_ACTIVATED;
}
return result;
};
LinkedFlagMatching.prototype.onConfig = function (config, onStarted) {
var _this = this;
var _a;
this.linkedFlag =
(isEagerLoadedConfig(config)
? (0, core_1.isObject)(config.sessionRecording)
? (_a = config.sessionRecording) === null || _a === void 0 ? void 0 : _a.linkedFlag
: null
: config === null || config === void 0 ? void 0 : config.linkedFlag) || null;
if (!(0, core_1.isNullish)(this.linkedFlag) && !this.linkedFlagSeen) {
var linkedFlag_1 = (0, core_1.isString)(this.linkedFlag) ? this.linkedFlag : this.linkedFlag.flag;
var linkedVariant_1 = (0, core_1.isString)(this.linkedFlag) ? null : this.linkedFlag.variant;
this._flagListenerCleanup = this._instance.onFeatureFlags(function (_flags, variants) {
var flagIsPresent = (0, core_1.isObject)(variants) && linkedFlag_1 in variants;
var linkedFlagMatches = false;
if (flagIsPresent) {
var variantForFlagKey = variants[linkedFlag_1];
if ((0, core_1.isBoolean)(variantForFlagKey)) {
linkedFlagMatches = variantForFlagKey === true;
}
else if (linkedVariant_1) {
linkedFlagMatches = variantForFlagKey === linkedVariant_1;
}
else {
// then this is a variant flag and we want to match any string
linkedFlagMatches = !!variantForFlagKey;
}
}
// onFeatureFlags fires on every flag reload, so only report activation on the
// inactive->active transition. Firing on every reload while the flag stays truthy
// re-reports linked_flag_matched and (via onStarted -> _onTriggerActivated) restarts
// the full-snapshot interval each time, which can postpone periodic full snapshots
// indefinitely when flags reload more often than full_snapshot_interval_millis.
var isNewActivation = linkedFlagMatches && !_this.linkedFlagSeen;
_this.linkedFlagSeen = linkedFlagMatches;
if (isNewActivation) {
onStarted(linkedFlag_1, linkedVariant_1);
}
});
}
};
/**
* @deprecated Use onConfig instead
*/
LinkedFlagMatching.prototype.onRemoteConfig = function (response, onStarted) {
this.onConfig(response, onStarted);
};
LinkedFlagMatching.prototype.stop = function () {
this._flagListenerCleanup();
};
return LinkedFlagMatching;
}());
exports.LinkedFlagMatching = LinkedFlagMatching;
var EventTriggerMatching = /** @class */ (function () {
function EventTriggerMatching(_instance, groupId) {
this._instance = _instance;
this._eventTriggers = [];
this._groupId = groupId;
}
EventTriggerMatching.prototype.onConfig = function (config) {
var _a;
// Handle both RemoteConfig (nested) and SessionRecordingPersistedConfig (flattened) structures
this._eventTriggers =
(isEagerLoadedConfig(config)
? (0, core_1.isObject)(config.sessionRecording)
? (_a = config.sessionRecording) === null || _a === void 0 ? void 0 : _a.eventTriggers
: []
: config === null || config === void 0 ? void 0 : config.eventTriggers) || [];
};
/**
* @deprecated Use onConfig instead
*/
EventTriggerMatching.prototype.onRemoteConfig = function (response) {
this.onConfig(response);
};
EventTriggerMatching.prototype._eventTriggerStatus = function (sessionId) {
var triggerCount = this._eventTriggers.length;
return persistedTriggerStatus(this._instance, triggerCount, this._groupId, constants_1.SESSION_RECORDING_TRIGGER_V2_GROUP_EVENT_PREFIX, constants_1.SESSION_RECORDING_EVENT_TRIGGER_ACTIVATED_SESSION, sessionId);
};
EventTriggerMatching.prototype.triggerStatus = function (sessionId) {
var _a;
var result = this.triggerStatusNoSideEffects(sessionId);
this._instance.register_for_session((_a = {},
_a[constants_1.SDK_DEBUG_REPLAY_EVENT_TRIGGER_STATUS] = result,
_a));
return result;
};
// Pure read of the trigger status — see URLTriggerMatching.triggerStatusNoSideEffects.
EventTriggerMatching.prototype.triggerStatusNoSideEffects = function (sessionId) {
return this._eventTriggerStatus(sessionId);
};
EventTriggerMatching.prototype.checkEventTriggerConditions = function (eventName, onActivate, sessionId) {
if (this._eventTriggers.length === 0) {
return;
}
var isActivated = this._eventTriggerStatus(sessionId) === exports.TRIGGER_ACTIVATED;
var includes = this._eventTriggers.includes(eventName);
if (!isActivated && includes) {
onActivate('event', eventName);
}
};
EventTriggerMatching.prototype.stop = function () {
// no-op
};
return EventTriggerMatching;
}());
exports.EventTriggerMatching = EventTriggerMatching;
/**
* V2 Trigger Group Matching - manages a single trigger group with its own conditions
*/
var TriggerGroupMatching = /** @class */ (function () {
function TriggerGroupMatching(_instance, group, onFlagStarted) {
this._instance = _instance;
this.group = group;
// V2: Pass groupId to child matchers for per-group persistence
this._urlTriggerMatching = new URLTriggerMatching(_instance, group.id);
this._eventTriggerMatching = new EventTriggerMatching(_instance, group.id);
this._linkedFlagMatching = new LinkedFlagMatching(_instance);
// Check if all conditions are empty (no events, urls, or flags)
var hasEvents = group.conditions.events && group.conditions.events.length > 0;
var hasUrls = group.conditions.urls && group.conditions.urls.length > 0;
var hasFlag = !!group.conditions.flag;
if (!hasEvents && !hasUrls && !hasFlag) {
// Empty conditions = trigger immediately on session start
this._combinedMatching = new AlwaysActivatedTriggerMatching();
}
else {
// V2: Events arrive as objects {name, properties?} from the server.
// Extract just the names — property filter evaluation is handled by the strategy.
var eventNames = (group.conditions.events || []).map(function (e) { return e.name; });
// Convert group config to the format expected by the individual matchers
var config = {
urlTriggers: group.conditions.urls || [],
eventTriggers: eventNames,
linkedFlag: group.conditions.flag || null,
urlBlocklist: [],
};
this._urlTriggerMatching.onConfig(config);
this._eventTriggerMatching.onConfig(config);
this._linkedFlagMatching.onConfig(config, onFlagStarted);
// Combine matchers based on the group's matchType
var matchers = [this._eventTriggerMatching, this._urlTriggerMatching, this._linkedFlagMatching];
this._combinedMatching =
group.conditions.matchType === 'any'
? new OrTriggerMatching(matchers)
: new AndTriggerMatching(matchers);
}
}
TriggerGroupMatching.prototype.triggerStatus = function (sessionId) {
return this._combinedMatching.triggerStatus(sessionId);
};
TriggerGroupMatching.prototype.triggerStatusNoSideEffects = function (sessionId) {
return this._combinedMatching.triggerStatusNoSideEffects(sessionId);
};
TriggerGroupMatching.prototype.getPendingTriggerConditions = function (sessionId) {
if (this.triggerStatusNoSideEffects(sessionId) !== exports.TRIGGER_PENDING) {
return [];
}
var legs = [
{
label: 'URL condition not matched',
status: this._urlTriggerMatching.triggerStatusNoSideEffects(sessionId),
},
{
label: 'event condition not matched',
status: this._eventTriggerMatching.triggerStatusNoSideEffects(sessionId),
},
{
label: 'linked flag condition not matched',
status: this._linkedFlagMatching.triggerStatusNoSideEffects(),
},
];
return legs.filter(function (_a) {
var status = _a.status;
return status === exports.TRIGGER_PENDING;
}).map(function (_a) {
var label = _a.label;
return label;
});
};
TriggerGroupMatching.prototype.checkEventTriggerConditions = function (eventName, onActivate, sessionId) {
this._eventTriggerMatching.checkEventTriggerConditions(eventName, onActivate, sessionId);
};
TriggerGroupMatching.prototype.checkUrlTriggerConditions = function (onPause, onResume, onActivate, sessionId) {
this._urlTriggerMatching.checkUrlTriggerConditions(onPause, onResume, onActivate, sessionId);
};
/**
* V2: Activate this group's trigger and persist to group-specific key
* This prevents cross-group contamination and survives page reloads
*/
TriggerGroupMatching.prototype.activateTrigger = function (triggerType, sessionId) {
var _a;
var _b;
var persistenceKey = triggerType === 'url'
? constants_1.SESSION_RECORDING_TRIGGER_V2_GROUP_URL_PREFIX + this.group.id
: constants_1.SESSION_RECORDING_TRIGGER_V2_GROUP_EVENT_PREFIX + this.group.id;
(_b = this._instance.persistence) === null || _b === void 0 ? void 0 : _b.register((_a = {},
_a[persistenceKey] = sessionId,
_a));
};
TriggerGroupMatching.prototype.stop = function () {
this._urlTriggerMatching.stop();
this._eventTriggerMatching.stop();
this._linkedFlagMatching.stop();
};
return TriggerGroupMatching;
}());
exports.TriggerGroupMatching = TriggerGroupMatching;
// we need a no-op matcher before we can lazy-load the other matches, since all matchers wait on remote config anyway
function nullMatchSessionRecordingStatus(triggersStatus) {
if (triggersStatus.rrwebError) {
return exports.RRWEB_ERROR;
}
if (!triggersStatus.isRecordingEnabled) {
return exports.DISABLED;
}
return exports.BUFFERING;
}
function anyMatchSessionRecordingStatus(triggersStatus) {
if (triggersStatus.rrwebError) {
return exports.RRWEB_ERROR;
}
if (!triggersStatus.receivedFlags) {
return exports.BUFFERING;
}
if (!triggersStatus.isRecordingEnabled) {
return exports.DISABLED;
}
if (triggersStatus.urlTriggerMatching.urlBlocked) {
return exports.PAUSED;
}
var sampledActive = triggersStatus.isSampled === true;
var triggerMatches = new OrTriggerMatching([
triggersStatus.eventTriggerMatching,
triggersStatus.urlTriggerMatching,
triggersStatus.linkedFlagMatching,
]).triggerStatus(triggersStatus.sessionId);
if (sampledActive) {
return exports.SAMPLED;
}
if (triggerMatches === exports.TRIGGER_ACTIVATED) {
return exports.ACTIVE;
}
if (triggerMatches === exports.TRIGGER_PENDING) {
// even if sampled active is false, we should still be buffering
// since a pending trigger could override it
return exports.BUFFERING;
}
// if sampling is set and the session is already decided to not be sampled
// then we should never be active
if (triggersStatus.isSampled === false) {
return exports.DISABLED;
}
return exports.ACTIVE;
}
function allMatchSessionRecordingStatus(triggersStatus) {
if (triggersStatus.rrwebError) {
return exports.RRWEB_ERROR;
}
if (!triggersStatus.receivedFlags) {
return exports.BUFFERING;
}
if (!triggersStatus.isRecordingEnabled) {
return exports.DISABLED;
}
if (triggersStatus.urlTriggerMatching.urlBlocked) {
return exports.PAUSED;
}
var andTriggerMatch = new AndTriggerMatching([
triggersStatus.eventTriggerMatching,
triggersStatus.urlTriggerMatching,
triggersStatus.linkedFlagMatching,
]);
var currentTriggerStatus = andTriggerMatch.triggerStatus(triggersStatus.sessionId);
var hasTriggersConfigured = currentTriggerStatus !== exports.TRIGGER_DISABLED;
var hasSamplingConfigured = (0, core_1.isBoolean)(triggersStatus.isSampled);
if (hasTriggersConfigured && currentTriggerStatus === exports.TRIGGER_PENDING) {
return exports.BUFFERING;
}
if (hasTriggersConfigured && currentTriggerStatus === exports.TRIGGER_DISABLED) {
return exports.DISABLED;
}
// sampling can't ever cause buffering, it's always determined right away or not configured
if (hasSamplingConfigured && !triggersStatus.isSampled) {
return exports.DISABLED;
}
// If sampling is configured and set to true, return sampled
if (triggersStatus.isSampled === true) {
return exports.SAMPLED;
}
return exports.ACTIVE;
}
/**
* V2 Trigger Groups Status Matcher - implements union behavior:
* 1. Evaluate ALL trigger groups
* 2. For each matching group, check if its sample rate hits
* 3. If ANY group's sample rate hits → record session
*/
function triggerGroupsMatchSessionRecordingStatus(triggersStatus) {
var e_3, _a;
if (triggersStatus.rrwebError) {
return exports.RRWEB_ERROR;
}
if (!triggersStatus.receivedFlags) {
return exports.BUFFERING;
}
if (!triggersStatus.isRecordingEnabled) {
return exports.DISABLED;
}
// Check if any URL is blocked (url blocklist is global, not per-group)
if (triggersStatus.urlTriggerMatching.urlBlocked) {
return exports.PAUSED;
}
var groupMatchers = triggersStatus.triggerGroupMatchers;
var samplingResults = triggersStatus.triggerGroupSamplingResults;
if (groupMatchers.length === 0) {
// No V2 groups configured - should not happen, but treat as disabled
return exports.DISABLED;
}
// Evaluate all groups to determine overall status
var anyGroupPending = false;
var anyGroupSampled = false;
try {
for (var groupMatchers_1 = __values(groupMatchers), groupMatchers_1_1 = groupMatchers_1.next(); !groupMatchers_1_1.done; groupMatchers_1_1 = groupMatchers_1.next()) {
var matcher = groupMatchers_1_1.value;
var groupStatus = matcher.triggerStatus(triggersStatus.sessionId);
if (groupStatus === exports.TRIGGER_ACTIVATED) {
// Check if this group's sample rate hit
var groupId = matcher.group.id;
var samplingResult = samplingResults.get(groupId);
if ((0, core_1.isUndefined)(samplingResult)) {
logger_1.logger.warn('[V2 Triggers] Group activated but no sampling decision found', { groupId: groupId });
}
else if (samplingResult === true) {
anyGroupSampled = true;
}
}
else if (groupStatus === exports.TRIGGER_PENDING) {
anyGroupPending = true;
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (groupMatchers_1_1 && !groupMatchers_1_1.done && (_a = groupMatchers_1.return)) _a.call(groupMatchers_1);
}
finally { if (e_3) throw e_3.error; }
}
// Union behavior: if ANY group hit its sample rate, record
if (anyGroupSampled) {
return exports.SAMPLED;
}
// If any group is pending, keep buffering
if (anyGroupPending) {
return exports.BUFFERING;
}
// All groups are either disabled, conditions not met, or sampled out
return exports.DISABLED;
}
//# sourceMappingURL=triggerMatching.js.map