posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
1,692 lines • 88.2 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
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.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostHogFeatureFlags = exports.QuotaLimitedResource = exports.parseFlagsResponse = exports.filterActiveFeatureFlags = exports.FeatureFlagError = void 0;
var general_utils_1 = require("@posthog/browser-common/utils/general-utils");
var feature_flags_config_1 = require("./feature-flags-config");
var extension_tokens_1 = require("./extension-tokens");
var constants_1 = require("./constants");
var core_1 = require("@posthog/core");
var logger_1 = require("@posthog/browser-common/utils/logger");
var event_utils_1 = require("@posthog/browser-common/utils/event-utils");
var globals_1 = require("@posthog/browser-common/utils/globals");
var promise_utils_1 = require("@posthog/browser-common/utils/promise-utils");
var request_utils_1 = require("@posthog/browser-common/utils/request-utils");
var logger = (0, logger_1.createLogger)('[FeatureFlags]');
var forceDebugLogger = (0, logger_1.createLogger)('[FeatureFlags]', { debugEnabled: true });
var FLAG_TIMEOUT_MSG = '" failed. Feature flags didn\'t load in time.';
// Mirrors the event retry queue's status-0 budget: repeated requests that die
// before any HTTP response while the browser reports itself online are usually
// deterministically blocked (ad blocker, CORS, extension). Stop periodic /flags
// refreshes after this many consecutive failures until connectivity changes.
var MAX_CONSECUTIVE_FLAGS_STATUS_ZERO_FAILURES = 3;
/**
* Error type constants for the $feature_flag_error property.
*
* These values are sent in analytics events to track flag evaluation failures.
* They should not be changed without considering impact on existing dashboards
* and queries that filter on these values.
*/
exports.FeatureFlagError = {
ERRORS_WHILE_COMPUTING: 'errors_while_computing_flags',
FLAG_MISSING: 'flag_missing',
QUOTA_LIMITED: 'quota_limited',
TIMEOUT: 'timeout',
CONNECTION_ERROR: 'connection_error',
UNKNOWN_ERROR: 'unknown_error',
apiError: function (status) { return "api_error_".concat(status); },
};
/** Converts an array of flag names to a Record where each flag is set to true. */
var arrayToFlagsRecord = function (flags) {
var flagsObj = {};
for (var i = 0; i < flags.length; i++) {
flagsObj[flags[i]] = true;
}
return flagsObj;
};
var filterActiveFeatureFlags = function (featureFlags) {
var e_1, _a;
var activeFeatureFlags = {};
try {
for (var _b = __values((0, general_utils_1.entries)(featureFlags || {})), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
if (value) {
activeFeatureFlags[key] = value;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
return activeFeatureFlags;
};
exports.filterActiveFeatureFlags = filterActiveFeatureFlags;
var parseFlagsResponse = function (response, currentFlags, currentFlagPayloads, currentFlagDetails, options, responseLogger) {
var _a, _b, _c, _d;
if (currentFlags === void 0) { currentFlags = {}; }
if (currentFlagPayloads === void 0) { currentFlagPayloads = {}; }
if (currentFlagDetails === void 0) { currentFlagDetails = {}; }
if (responseLogger === void 0) { responseLogger = logger; }
var normalizedResponse = normalizeFlagsResponse(response, responseLogger);
var flagDetails = normalizedResponse.flags;
var featureFlags = normalizedResponse.featureFlags;
var flagPayloads = normalizedResponse.featureFlagPayloads;
if (!featureFlags) {
return; // <-- This early return means we don't update anything, which is good.
}
var requestId = response['requestId'];
var evaluatedAt = response['evaluatedAt'];
// using the v1 api
if ((0, core_1.isArray)(featureFlags)) {
responseLogger.warn('v1 of the feature flags endpoint is deprecated. Please use the latest version.');
var $enabled_feature_flags = {};
if (featureFlags) {
for (var i = 0; i < featureFlags.length; i++) {
$enabled_feature_flags[featureFlags[i]] = true;
}
}
return _a = {},
_a[constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS] = featureFlags,
_a[constants_1.ENABLED_FEATURE_FLAGS] = $enabled_feature_flags,
// Legacy responses never carry the gate — fail safe to full events.
_a[constants_1.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS] = false,
_a;
}
// using the v2+ api
var newFeatureFlags = featureFlags;
var newFeatureFlagPayloads = flagPayloads;
var newFeatureFlagDetails = flagDetails;
if (options === null || options === void 0 ? void 0 : options.partialResponse) {
// The response is intentionally partial (e.g., only survey flags were requested via
// advanced_only_evaluate_survey_feature_flags). Merge with existing flags so that
// bootstrapped or previously loaded non-survey flags are preserved.
var evaluatedFlagKeys = Object.keys(newFeatureFlags);
var evaluatedPayloads_1 = newFeatureFlagPayloads || {};
newFeatureFlags = __assign(__assign({}, currentFlags), newFeatureFlags);
newFeatureFlagPayloads = __assign(__assign({}, currentFlagPayloads), evaluatedPayloads_1);
evaluatedFlagKeys.forEach(function (key) {
if (!(key in evaluatedPayloads_1)) {
newFeatureFlagPayloads === null || newFeatureFlagPayloads === void 0 ? true : delete newFeatureFlagPayloads[key];
}
});
newFeatureFlagDetails = __assign(__assign({}, currentFlagDetails), newFeatureFlagDetails);
}
else if (response.errorsWhileComputingFlags) {
// if not all flags were computed, we upsert flags instead of replacing them
// but filter out flags that failed to evaluate so they don't overwrite cached values
if (flagDetails) {
var successfulKeys_1 = new Set(Object.keys(flagDetails).filter(function (key) { var _a; return !((_a = flagDetails[key]) === null || _a === void 0 ? void 0 : _a.failed); }));
newFeatureFlags = __assign(__assign({}, currentFlags), Object.fromEntries(Object.entries(newFeatureFlags).filter(function (_a) {
var _b = __read(_a, 1), key = _b[0];
return successfulKeys_1.has(key);
})));
var successfulPayloads_1 = Object.fromEntries(Object.entries(newFeatureFlagPayloads || {}).filter(function (_a) {
var _b = __read(_a, 1), key = _b[0];
return successfulKeys_1.has(key);
}));
newFeatureFlagPayloads = __assign(__assign({}, currentFlagPayloads), successfulPayloads_1);
successfulKeys_1.forEach(function (key) {
if (!(key in successfulPayloads_1)) {
newFeatureFlagPayloads === null || newFeatureFlagPayloads === void 0 ? true : delete newFeatureFlagPayloads[key];
}
});
newFeatureFlagDetails = __assign(__assign({}, currentFlagDetails), Object.fromEntries(Object.entries(newFeatureFlagDetails || {}).filter(function (_a) {
var _b = __read(_a, 1), key = _b[0];
return successfulKeys_1.has(key);
})));
}
else {
// v1 responses don't have flagDetails, so we can't filter by failed field
// Fall back to the original merge behavior
newFeatureFlags = __assign(__assign({}, currentFlags), newFeatureFlags);
newFeatureFlagPayloads = __assign(__assign({}, currentFlagPayloads), newFeatureFlagPayloads);
newFeatureFlagDetails = __assign(__assign({}, currentFlagDetails), newFeatureFlagDetails);
}
}
return __assign(__assign((_b = {}, _b[constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS] = Object.keys((0, exports.filterActiveFeatureFlags)(newFeatureFlags)), _b[constants_1.ENABLED_FEATURE_FLAGS] = newFeatureFlags || {}, _b[constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS] = newFeatureFlagPayloads || {}, _b[constants_1.PERSISTENCE_FEATURE_FLAG_DETAILS] = newFeatureFlagDetails || {}, _b[constants_1.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS] = response.minimalFlagCalledEvents === true, _b), (requestId ? (_c = {}, _c[constants_1.PERSISTENCE_FEATURE_FLAG_REQUEST_ID] = requestId, _c) : {})), (evaluatedAt ? (_d = {}, _d[constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT] = evaluatedAt, _d) : {}));
};
exports.parseFlagsResponse = parseFlagsResponse;
var normalizeFlagsResponse = function (response, responseLogger) {
var flagDetails = response['flags'];
if (flagDetails) {
// This is a /flags?v=2 request.
// Map of flag keys to flag values: Record<string, string | boolean>
var featureFlags = Object.fromEntries(Object.keys(flagDetails).map(function (flag) { var _a; return [flag, (_a = flagDetails[flag].variant) !== null && _a !== void 0 ? _a : flagDetails[flag].enabled]; }));
// Map of flag keys to flag payloads: Record<string, JsonType>
var featureFlagPayloads = Object.fromEntries(Object.keys(flagDetails)
.filter(function (flag) { return flagDetails[flag].enabled; })
.filter(function (flag) { var _a; return !(0, core_1.isUndefined)((_a = flagDetails[flag].metadata) === null || _a === void 0 ? void 0 : _a.payload); })
.map(function (flag) { var _a; return [flag, (_a = flagDetails[flag].metadata) === null || _a === void 0 ? void 0 : _a.payload]; }));
return __assign(__assign({}, response), { featureFlags: featureFlags, featureFlagPayloads: featureFlagPayloads });
}
else if (response['featureFlags']) {
// The response has no `flags` key but does carry top-level `featureFlags`, which is the
// shape returned by older servers that don't understand `?v=2`. A valid v2 response with no
// flags (e.g. a project without any feature flags) legitimately omits `flags`, so we must
// not warn in that case.
responseLogger.warn('Using an older version of the feature flags endpoint. Please upgrade your PostHog server to the latest version');
}
return response;
};
exports.QuotaLimitedResource = {
FeatureFlags: 'feature_flags',
Recordings: 'recordings',
};
var PostHogFeatureFlags = /** @class */ (function () {
function PostHogFeatureFlags(instanceOrConfigSource) {
var _this = this;
this.name = extension_tokens_1.FeatureFlagsExtension;
this._override_warning = false;
this.featureFlagEventHandlers = [];
this._logger = logger;
this._baseEventProperties = {};
this._eventPropertiesWithFlagValues = {};
this._reloadingHandlers = [];
this._hasLoadedFlags = false;
this._requestInFlight = false;
this._requestGeneration = 0;
this._reloadingDisabled = false;
this._additionalReloadRequested = false;
this._flagsLoadedFromRemote = false;
this._staleCacheRefreshTriggered = false;
this._consecutiveStatusZeroFailures = 0;
this._onOnline = function () {
var wasTripped = _this._hasStatusZeroCircuitBreakerTripped();
_this._consecutiveStatusZeroFailures = 0;
if (wasTripped) {
_this.reloadFeatureFlags();
}
};
this._refreshIfDue = function () {
var _a;
var refreshIntervalMs = _this._refreshIntervalMs;
if ((0, core_1.isUndefined)(refreshIntervalMs) ||
_this._config.remoteRequestsDisabled ||
!globals_1.document ||
globals_1.document.visibilityState === 'hidden' ||
Date.now() - ((_a = _this._lastRefreshAt) !== null && _a !== void 0 ? _a : 0) < refreshIntervalMs) {
return;
}
_this.reloadFeatureFlags();
_this._scheduleNextRefresh();
};
this._onVisibilityChange = function () {
if ((globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.visibilityState) === 'visible') {
_this._refreshIfDue();
}
};
if ('get' in instanceOrConfigSource) {
this._configSource = instanceOrConfigSource;
}
else {
this._instance = instanceOrConfigSource;
this._mutableConfigSource = new feature_flags_config_1.MutableFeatureFlagsConfigSource(instanceOrConfigSource.config, instanceOrConfigSource._shouldDisableFlags());
this._configSource = this._mutableConfigSource;
}
}
PostHogFeatureFlags.prototype.updateConfig = function (config, remoteRequestsDisabled) {
var _a;
(_a = this._mutableConfigSource) === null || _a === void 0 ? void 0 : _a.update(config, remoteRequestsDisabled);
if (this._client) {
this._syncAutomaticRefresh();
}
};
PostHogFeatureFlags.prototype.setup = function (client) {
var _this = this;
this._initializingClient = client;
this._logger = client.logger.createLogger('[FeatureFlags]');
return (0, promise_utils_1.continueWith)(client.kv.initialize(), function () {
if (_this._initializingClient !== client) {
return;
}
_this._initializingClient = undefined;
_this._client = client;
_this._finishSetup(client);
});
};
PostHogFeatureFlags.prototype._finishSetup = function (client) {
var _this = this;
var _a, _b;
if (this._client !== client) {
return;
}
if (globals_1.window) {
(0, general_utils_1.addEventListener)(globals_1.window, 'online', this._onOnline);
}
this._syncAutomaticRefresh();
this._dynamicProperties = client.registerDynamicEventProperties(function () {
return _this._isCacheStale() ? _this._baseEventProperties : _this._eventPropertiesWithFlagValues;
});
this._crossTabPersistenceUnsubscribe = (_b = (_a = this._instance) === null || _a === void 0 ? void 0 : _a.persistence) === null || _b === void 0 ? void 0 : _b.onCrossTabFeatureFlagChange(function () {
_this._rebuildEventProperties();
_this._fireFeatureFlagsCallbacks();
});
this._rebuildEventProperties();
return this.initialize();
};
PostHogFeatureFlags.prototype._syncAutomaticRefresh = function () {
var configuredIntervalMs = this._config.refreshIntervalMs;
var refreshIntervalMs = !this._config.remoteRequestsDisabled &&
globals_1.document &&
!(0, core_1.isUndefined)(configuredIntervalMs) &&
configuredIntervalMs > 0
? configuredIntervalMs
: undefined;
if (refreshIntervalMs === this._refreshIntervalMs) {
return;
}
this._stopAutomaticRefresh();
if ((0, core_1.isUndefined)(refreshIntervalMs)) {
return;
}
this._refreshIntervalMs = refreshIntervalMs;
this._scheduleNextRefresh();
if (globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.addEventListener) {
(0, general_utils_1.addEventListener)(globals_1.document, constants_1.DOM_EVENT_VISIBILITYCHANGE, this._onVisibilityChange);
}
};
PostHogFeatureFlags.prototype._scheduleNextRefresh = function () {
if ((0, core_1.isUndefined)(this._refreshIntervalMs)) {
return;
}
if (!(0, core_1.isUndefined)(this._refreshInterval)) {
clearInterval(this._refreshInterval);
}
this._lastRefreshAt = Date.now();
this._refreshInterval = setInterval(this._refreshIfDue, this._refreshIntervalMs);
};
PostHogFeatureFlags.prototype._stopAutomaticRefresh = function () {
var _a;
if (!(0, core_1.isUndefined)(this._refreshInterval)) {
clearInterval(this._refreshInterval);
this._refreshInterval = undefined;
(_a = globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(globals_1.document, constants_1.DOM_EVENT_VISIBILITYCHANGE, this._onVisibilityChange);
}
this._refreshIntervalMs = undefined;
this._lastRefreshAt = undefined;
};
PostHogFeatureFlags.prototype.destroy = function () {
this._dispose();
};
PostHogFeatureFlags.prototype.dispose = function () {
this._dispose();
};
PostHogFeatureFlags.prototype._dispose = function () {
var _a, _b;
this._stopAutomaticRefresh();
this._requestGeneration++;
this._additionalReloadRequested = false;
this._initializingClient = undefined;
if (!this._client) {
return;
}
this._clearDebouncer();
(_a = this._dynamicProperties) === null || _a === void 0 ? void 0 : _a.dispose();
this._dynamicProperties = undefined;
(_b = this._crossTabPersistenceUnsubscribe) === null || _b === void 0 ? void 0 : _b.call(this);
this._crossTabPersistenceUnsubscribe = undefined;
this._reloadingHandlers = [];
globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.removeEventListener('online', this._onOnline);
this._client = undefined;
};
Object.defineProperty(PostHogFeatureFlags.prototype, "_config", {
get: function () {
return this._configSource.get();
},
enumerable: false,
configurable: true
});
PostHogFeatureFlags.prototype._prop = function (key) {
var _a;
return (_a = this._client) === null || _a === void 0 ? void 0 : _a.kv.get(key);
};
PostHogFeatureFlags.prototype._set = function (properties) {
var _this = this;
this._persist(function () { var _a; return (_a = _this._client) === null || _a === void 0 ? void 0 : _a.kv.set(properties); });
};
PostHogFeatureFlags.prototype._markCrossTabFeatureFlagSnapshot = function (statePatch, response, partialResponse) {
var _a, e_2, _b;
var _c;
if (!((_c = this._instance) === null || _c === void 0 ? void 0 : _c.persistence) || !statePatch[constants_1.ENABLED_FEATURE_FLAGS]) {
return;
}
var previousFlags = this._prop(constants_1.ENABLED_FEATURE_FLAGS) || {};
var nextFlags = statePatch[constants_1.ENABLED_FEATURE_FLAGS] || {};
var responseFlags = response.flags || response.featureFlags;
var successfulResponseFlagKeys = response.flags
? Object.entries(response.flags)
.filter(function (_a) {
var _b = __read(_a, 2), detail = _b[1];
return !(detail === null || detail === void 0 ? void 0 : detail.failed);
})
.map(function (_a) {
var _b = __read(_a, 1), key = _b[0];
return key;
})
: [];
var isIncompleteResponse = !!response.errorsWhileComputingFlags && !!response.flags;
var ownedFlagKeys = isIncompleteResponse
? successfulResponseFlagKeys
: partialResponse && !(0, core_1.isArray)(responseFlags)
? Object.keys(responseFlags || {})
: Array.from(new Set(__spreadArray(__spreadArray([], __read(Object.keys(previousFlags)), false), __read(Object.keys(nextFlags)), false)));
var previousPayloads = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS) || {};
var nextPayloads = statePatch[constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS] || {};
var ownedPayloadKeys = isIncompleteResponse || partialResponse
? ownedFlagKeys
: Array.from(new Set(__spreadArray(__spreadArray([], __read(Object.keys(previousPayloads)), false), __read(Object.keys(nextPayloads)), false)));
var ownsCompleteSnapshot = !isIncompleteResponse && !partialResponse;
var flagOwnership = ownsCompleteSnapshot ? true : ownedFlagKeys;
var payloadOwnership = ownsCompleteSnapshot ? true : ownedPayloadKeys;
var changes = (_a = {},
_a[constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS] = flagOwnership,
_a[constants_1.ENABLED_FEATURE_FLAGS] = flagOwnership,
_a[constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS] = payloadOwnership,
_a);
if (statePatch[constants_1.PERSISTENCE_FEATURE_FLAG_DETAILS]) {
changes[constants_1.PERSISTENCE_FEATURE_FLAG_DETAILS] = flagOwnership;
}
try {
for (var _d = __values([
constants_1.PERSISTENCE_FEATURE_FLAG_REQUEST_ID,
constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT,
constants_1.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS,
]), _e = _d.next(); !_e.done; _e = _d.next()) {
var key = _e.value;
if (!(0, core_1.isUndefined)(statePatch[key])) {
changes[key] = true;
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
}
finally { if (e_2) throw e_2.error; }
}
this._instance.persistence.markCrossTabFeatureFlagChanges(changes);
};
PostHogFeatureFlags.prototype._remove = function (keys) {
var _this = this;
this._persist(function () { var _a; return (_a = _this._client) === null || _a === void 0 ? void 0 : _a.kv.remove(keys); });
};
PostHogFeatureFlags.prototype._persist = function (operation) {
try {
operation();
}
catch (error) {
this._logger.error('Failed to update feature flag persistence', error);
}
};
PostHogFeatureFlags.prototype._rebuildEventProperties = function () {
var e_3, _a, e_4, _b;
var common = {};
try {
for (var _c = __values([
constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS,
constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS,
constants_1.PERSISTENCE_FEATURE_FLAG_REQUEST_ID,
constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS,
]), _d = _c.next(); !_d.done; _d = _c.next()) {
var key = _d.value;
var value = this._prop(key);
if (!(0, core_1.isUndefined)(value)) {
common[key] = value;
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_3) throw e_3.error; }
}
this._baseEventProperties = common;
var withFlagValues = __assign({}, common);
var flags = this._prop(constants_1.ENABLED_FEATURE_FLAGS);
if (flags) {
try {
for (var _e = __values(Object.entries(flags)), _f = _e.next(); !_f.done; _f = _e.next()) {
var _g = __read(_f.value, 2), key = _g[0], value = _g[1];
withFlagValues["$feature/".concat(key)] = value;
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
}
finally { if (e_4) throw e_4.error; }
}
}
this._eventPropertiesWithFlagValues = withFlagValues;
};
/**
* Check if the feature flag cache is stale based on the configured TTL.
*/
PostHogFeatureFlags.prototype._isCacheStale = function () {
var ttl = this._config.cacheTtlMs;
if (!ttl || ttl <= 0) {
return false;
}
var evaluatedAt = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT);
return typeof evaluatedAt !== 'number' || Date.now() - evaluatedAt > ttl;
};
/**
* Triggers a debounced reload when cache staleness is first detected.
* Returns true if cache is stale, false otherwise.
*/
PostHogFeatureFlags.prototype._checkAndTriggerStaleRefresh = function () {
if (!this._isCacheStale()) {
return false;
}
// Only trigger refresh once per stale period
if (!this._staleCacheRefreshTriggered && !this._requestInFlight) {
this._staleCacheRefreshTriggered = true;
this._logger.warn('Feature flag cache is stale, triggering refresh...');
this.reloadFeatureFlags();
}
return true;
};
PostHogFeatureFlags.prototype._getValidEvaluationEnvironments = function () {
var _this = this;
var envs = this._config.evaluationContexts;
if (!(envs === null || envs === void 0 ? void 0 : envs.length)) {
return [];
}
return envs.filter(function (env) {
var isValid = env && typeof env === 'string' && env.trim().length > 0;
if (!isValid) {
_this._logger.error('Invalid evaluation context found:', env, 'Expected non-empty string');
}
return isValid;
});
};
PostHogFeatureFlags.prototype._getValidFlagKeys = function () {
var _this = this;
var flagKeys = this._config.flagKeys;
if ((0, core_1.isUndefined)(flagKeys)) {
return undefined;
}
return flagKeys.filter(function (flagKey) {
var isValid = flagKey && typeof flagKey === 'string' && flagKey.trim().length > 0;
if (!isValid) {
_this._logger.error('Invalid flag key found:', flagKey, 'Expected non-empty string');
}
return isValid;
});
};
PostHogFeatureFlags.prototype.initialize = function () {
var _a, _b, _c, _d;
var config = this._config;
var bootstrapFlags = (_b = (_a = config.bootstrap) === null || _a === void 0 ? void 0 : _a.featureFlags) !== null && _b !== void 0 ? _b : {};
var hasBootstrappedFlags = Object.keys(bootstrapFlags).length;
if (hasBootstrappedFlags) {
var bootstrapPayloads_1 = (_d = (_c = config.bootstrap) === null || _c === void 0 ? void 0 : _c.featureFlagPayloads) !== null && _d !== void 0 ? _d : {};
var activeFlags_1 = Object.keys(bootstrapFlags)
.filter(function (flag) { return !!bootstrapFlags[flag]; })
.reduce(function (res, key) {
res[key] = bootstrapFlags[key] || false;
return res;
}, {});
var featureFlagPayloads = Object.keys(bootstrapPayloads_1)
.filter(function (key) { return activeFlags_1[key]; })
.reduce(function (res, key) {
res[key] = bootstrapPayloads_1[key];
return res;
}, {});
return this._receivedFeatureFlags({ featureFlags: activeFlags_1, featureFlagPayloads: featureFlagPayloads });
}
return undefined;
};
PostHogFeatureFlags.prototype.updateFlags = function (flags, payloads, options) {
var e_5, _a;
var _b, _c;
// Merge against the raw stored flags/payloads, not the override-applied getters.
var existingFlags = (options === null || options === void 0 ? void 0 : options.merge)
? ((_b = this._prop(constants_1.ENABLED_FEATURE_FLAGS)) !== null && _b !== void 0 ? _b : {})
: {};
var existingPayloads = (options === null || options === void 0 ? void 0 : options.merge)
? ((_c = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS)) !== null && _c !== void 0 ? _c : {})
: {};
var finalFlags = __assign(__assign({}, existingFlags), flags);
var finalPayloads = __assign(__assign({}, existingPayloads), payloads);
// Convert simple flags to v4 format to avoid deprecation warning
var flagDetails = {};
try {
for (var _d = __values(Object.entries(finalFlags)), _e = _d.next(); !_e.done; _e = _d.next()) {
var _f = __read(_e.value, 2), key = _f[0], value = _f[1];
flagDetails[key] = {
key: key,
enabled: (0, core_1.getEnabledFromValue)(value),
variant: (0, core_1.getVariantFromValue)(value),
reason: undefined,
// id: 0 indicates manually injected flags (not from server evaluation)
metadata: !(0, core_1.isUndefined)(finalPayloads === null || finalPayloads === void 0 ? void 0 : finalPayloads[key])
? { id: 0, version: undefined, description: undefined, payload: finalPayloads[key] }
: undefined,
};
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
}
finally { if (e_5) throw e_5.error; }
}
void this._receivedFeatureFlags({
flags: flagDetails,
});
};
Object.defineProperty(PostHogFeatureFlags.prototype, "hasLoadedFlags", {
get: function () {
return this._hasLoadedFlags;
},
enumerable: false,
configurable: true
});
PostHogFeatureFlags.prototype.getFlags = function () {
return Object.keys(this.getFlagVariants());
};
PostHogFeatureFlags.prototype.getFlagsWithDetails = function () {
var e_6, _a;
var _b, _c;
var flagDetails = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_DETAILS);
var overridenFlags = this._prop(constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS);
var overriddenPayloads = this._prop(constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS);
if (!overriddenPayloads && !overridenFlags) {
return flagDetails || {};
}
var finalDetails = (0, general_utils_1.extend)({}, flagDetails || {});
var overriddenKeys = __spreadArray([], __read(new Set(__spreadArray(__spreadArray([], __read(Object.keys(overriddenPayloads || {})), false), __read(Object.keys(overridenFlags || {})), false))), false);
try {
for (var overriddenKeys_1 = __values(overriddenKeys), overriddenKeys_1_1 = overriddenKeys_1.next(); !overriddenKeys_1_1.done; overriddenKeys_1_1 = overriddenKeys_1.next()) {
var key = overriddenKeys_1_1.value;
var originalDetail = finalDetails[key];
var overrideFlagValue = overridenFlags === null || overridenFlags === void 0 ? void 0 : overridenFlags[key];
var finalEnabled = (0, core_1.isUndefined)(overrideFlagValue)
? ((_b = originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.enabled) !== null && _b !== void 0 ? _b : false)
: !!overrideFlagValue;
var overrideVariant = (0, core_1.isUndefined)(overrideFlagValue)
? originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.variant
: typeof overrideFlagValue === 'string'
? overrideFlagValue
: undefined;
var overridePayload = overriddenPayloads === null || overriddenPayloads === void 0 ? void 0 : overriddenPayloads[key];
var overridenDetail = __assign(__assign({}, originalDetail), { enabled: finalEnabled,
// If the flag is not enabled, the variant should be undefined, even if the original has a variant value.
variant: finalEnabled ? (overrideVariant !== null && overrideVariant !== void 0 ? overrideVariant : originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.variant) : undefined });
// Keep track of the original enabled and variant values so we can send them in the $feature_flag_called event.
// This will be helpful for debugging and for understanding the impact of overrides.
if (finalEnabled !== (originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.enabled)) {
overridenDetail.original_enabled = originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.enabled;
}
if (overrideVariant !== (originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.variant)) {
overridenDetail.original_variant = originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.variant;
}
if (overridePayload) {
overridenDetail.metadata = __assign(__assign({}, originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.metadata), { payload: overridePayload, original_payload: (_c = originalDetail === null || originalDetail === void 0 ? void 0 : originalDetail.metadata) === null || _c === void 0 ? void 0 : _c.payload });
}
finalDetails[key] = overridenDetail;
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (overriddenKeys_1_1 && !overriddenKeys_1_1.done && (_a = overriddenKeys_1.return)) _a.call(overriddenKeys_1);
}
finally { if (e_6) throw e_6.error; }
}
if (!this._override_warning) {
this._logger.warn(' Overriding feature flag details!', {
flagDetails: flagDetails,
overriddenPayloads: overriddenPayloads,
finalDetails: finalDetails,
});
this._override_warning = true;
}
return finalDetails;
};
PostHogFeatureFlags.prototype.getAllFeatureFlags = function () {
var flagVariants = this.getFlagVariants();
var payloads = this.getFlagPayloads();
return Object.keys(flagVariants).map(function (key) {
var flagValue = flagVariants[key];
return {
key: key,
enabled: (0, core_1.getEnabledFromValue)(flagValue),
variant: (0, core_1.getVariantFromValue)(flagValue),
payload: (0, core_1.parsePayload)(payloads[key]),
};
});
};
PostHogFeatureFlags.prototype.getFlagVariants = function () {
var enabledFlags = this._prop(constants_1.ENABLED_FEATURE_FLAGS);
var overriddenFlags = this._prop(constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS);
if (!overriddenFlags) {
return enabledFlags || {};
}
var finalFlags = (0, general_utils_1.extend)({}, enabledFlags || {});
var overriddenKeys = Object.keys(overriddenFlags);
for (var i = 0; i < overriddenKeys.length; i++) {
finalFlags[overriddenKeys[i]] = overriddenFlags[overriddenKeys[i]];
}
if (!this._override_warning) {
this._logger.warn(' Overriding feature flags!', {
enabledFlags: enabledFlags,
overriddenFlags: overriddenFlags,
finalFlags: finalFlags,
});
this._override_warning = true;
}
return finalFlags;
};
PostHogFeatureFlags.prototype.getFlagPayloads = function () {
var flagPayloads = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS);
var overriddenPayloads = this._prop(constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS);
if (!overriddenPayloads) {
return flagPayloads || {};
}
var finalPayloads = (0, general_utils_1.extend)({}, flagPayloads || {});
var overriddenKeys = Object.keys(overriddenPayloads);
for (var i = 0; i < overriddenKeys.length; i++) {
finalPayloads[overriddenKeys[i]] = overriddenPayloads[overriddenKeys[i]];
}
if (!this._override_warning) {
this._logger.warn(' Overriding feature flag payloads!', {
flagPayloads: flagPayloads,
overriddenPayloads: overriddenPayloads,
finalPayloads: finalPayloads,
});
this._override_warning = true;
}
return finalPayloads;
};
/**
* Reloads feature flags asynchronously.
*
* Constraints:
*
* 1. Avoid parallel requests
* 2. Delay a few milliseconds after each reloadFeatureFlags call to batch subsequent changes together
*/
PostHogFeatureFlags.prototype.reloadFeatureFlags = function () {
var _this = this;
if (this._reloadingDisabled || this._config.featureFlagsDisabled) {
// If reloading has been explicitly disabled then we don't want to do anything
// Or if feature flags are disabled
return;
}
if (this._hasStatusZeroCircuitBreakerTripped()) {
return;
}
if (this._reloadDebouncer) {
// If we're already in a debounce then we don't want to do anything
return;
}
// Notify browser-v1 facade listeners before the debounced request starts.
this._reloadingHandlers.slice().forEach(function (handler) {
try {
handler();
}
catch (error) {
_this._logger.error('Error while running feature flags reloading callback', error);
}
});
// Debounce multiple calls on the same tick
this._reloadDebouncer = setTimeout(function () {
_this._callFlagsEndpoint();
}, 5);
};
PostHogFeatureFlags.prototype._clearDebouncer = function () {
clearTimeout(this._reloadDebouncer);
this._reloadDebouncer = undefined;
};
PostHogFeatureFlags.prototype.onReloading = function (handler) {
var _this = this;
this._reloadingHandlers.push(handler);
return function () {
_this._reloadingHandlers = _this._reloadingHandlers.filter(function (entry) { return entry !== handler; });
};
};
PostHogFeatureFlags.prototype.ensureFlagsLoaded = function () {
if (this._hasLoadedFlags || this._requestInFlight || this._reloadDebouncer) {
// If we are or have already loaded the flags then we don't want to do anything
return;
}
this.reloadFeatureFlags();
};
PostHogFeatureFlags.prototype.setAnonymousDistinctId = function (anon_distinct_id) {
this.$anon_distinct_id = anon_distinct_id;
};
PostHogFeatureFlags.prototype.setReloadingPaused = function (isPaused) {
this._reloadingDisabled = isPaused;
};
PostHogFeatureFlags.prototype.resetFlagCallReported = function () {
this._remove(constants_1.FLAG_CALL_REPORTED);
};
PostHogFeatureFlags.prototype._callFlagsEndpoint = function (options) {
var _this = this;
this._clearDebouncer();
var client = this._client;
if (!client || this._config.remoteRequestsDisabled || this._hasStatusZeroCircuitBreakerTripped()) {
return;
}
if (this._requestInFlight) {
this._additionalReloadRequested = true;
return;
}
var data = {
token: client.projectToken,
distinct_id: client.distinctId,
groups: client.groups,
$anon_distinct_id: this.$anon_distinct_id,
person_properties: __assign(__assign(__assign({}, client.initialPersonProperties), (this._prop(constants_1.STORED_PERSON_PROPERTIES_KEY) || {})), { $lib: client.library.name, $lib_version: client.library.version }),
group_properties: this._prop(constants_1.STORED_GROUP_PROPERTIES_KEY),
timezone: (0, event_utils_1.getTimezone)(),
};
if (!(0, core_1.isUndefined)(client.deviceId)) {
data.$device_id = client.deviceId;
}
if ((options === null || options === void 0 ? void 0 : options.disableFlags) || this._config.featureFlagsDisabled) {
data.disable_flags = true;
}
var evaluationContexts = this._getValidEvaluationEnvironments();
if (evaluationContexts.length) {
data.evaluation_contexts = evaluationContexts;
}
var flagKeys = this._getValidFlagKeys();
if (!(0, core_1.isUndefined)(flagKeys)) {
data.flag_keys = flagKeys;
}
var isPartialFlagsResponse = this._config.onlyEvaluateSurveyFeatureFlags;
var path = "/flags/?v=2".concat(isPartialFlagsResponse ? '&only_evaluate_survey_feature_flags=true' : '');
var requestGeneration = this._requestGeneration;
this._requestInFlight = true;
var requestAdditionalReload = function () {
if (_this._additionalReloadRequested) {
_this._additionalReloadRequested = false;
_this._callFlagsEndpoint();
}
};
var handleResponse = function (response) {
var _a;
var _b, _c;
var json = ((_b = response.json) !== null && _b !== void 0 ? _b : {});
var errorsLoading = response.statusCode !== 200;
_this._requestInFlight = false;
if (requestGeneration !== _this._requestGeneration) {
requestAdditionalReload();
return;
}
_this._trackStatusZeroReachability(response.statusCode);
if (!errorsLoading && !_this._additionalReloadRequested) {
_this.$anon_distinct_id = undefined;
}
if (data.disable_flags && !_this._additionalReloadRequested) {
return;
}
_this._flagsLoadedFromRemote = !errorsLoading;
var flagErrors = [];
if (response.error) {
flagErrors.push(response.error instanceof Error && response.error.name === 'AbortError'
? exports.FeatureFlagError.TIMEOUT
: response.error instanceof Error
? exports.FeatureFlagError.CONNECTION_ERROR
: exports.FeatureFlagError.UNKNOWN_ERROR);
}
else if (response.statusCode !== 200) {
flagErrors.push(exports.FeatureFlagError.apiError(response.statusCode));
}
if (json.errorsWhileComputingFlags) {
flagErrors.push(exports.FeatureFlagError.ERRORS_WHILE_COMPUTING);
}
var isQuotaLimited = !!((_c = json.quotaLimited) === null || _c === void 0 ? void 0 : _c.includes(exports.QuotaLimitedResource.FeatureFlags));
if (isQuotaLimited) {
flagErrors.push(exports.FeatureFlagError.QUOTA_LIMITED);
}
_this._set((_a = {}, _a[constants_1.PERSISTENCE_FEATURE_FLAG_ERRORS] = flagErrors, _a));
if (isQuotaLimited) {
_this._logger.warn('You have hit your feature flags quota limit, and will not be able to load feature flags until the quota is reset. Please visit https://posthog.com/docs/billing/limits-alerts to learn more.');
}
else if (!data.disable_flags) {
_this._receivedFeatureFlags(json, errorsLoading, { partialResponse: isPartialFlagsResponse });
}
requestAdditionalReload();
};
var handleError = function (error) {
var _a;
_this._requestInFlight = false;
if (requestGeneration !== _this._requestGeneration) {
requestAdditionalReload();
return;
}
_this._set((_a = {}, _a[constants_1.PERSISTENCE_FEATURE_FLAG_ERRORS] = [exports.FeatureFlagError.CONNECTION_ERROR], _a));
_this._logger.error('Feature flag request failed', error);
requestAdditionalReload();
};
try {
void client
.sendRequest(path, {
target: 'flags',
method: 'POST',
body: data,
compression: this._config.compression,
sentAt: 'body',
timeoutMs: this._config.requestTimeoutMs,
})
.then(handleResponse)
.catch(handleError);
}
catch (error) {
handleError(error);
}
};
PostHogFeatureFlags.prototype._hasStatusZeroCircuitBreakerTripped = function () {
return (0, request_utils_1.isStatusZeroFailureCircuitBreakerTripped)(this._consecutiveStatusZeroFailures, MAX_CONSECUTIVE_FLAGS_STATUS_ZERO_FAILURES);
};
PostHogFeatureFlags.prototype._trackStatusZeroReachability = function (statusCode) {
var _this = this;
this._consecutiveStatusZeroFailures = (0, request_utils_1.updateStatusZeroFailureCount)(statusCode, this._consecutiveStatusZeroFailures, MAX_CONSECUTIVE_FLAGS_STATUS_ZERO_FAILURES, function () {
return _this._logger.warn('Feature flag requests are failing before receiving an HTTP response; this can happen due to network issues, CORS, browser blocking, or ad blockers. Stopped refreshing feature flags; will try again when connectivity changes.');
});
};
/**
* Get feature flag's value for user.
*
* By default, this method may return cached values from localStorage if the `/flags` endpoint
* hasn't responded yet. This reduces flicker but means you might briefly see stale values
* (e.g., a flag that was disabled on the server).
*
* ### Usage:
*
* if(posthog.getFeatureFlag('my-flag') === 'some-variant') { // do something }
*
* // Only use fresh values from the server (returns undefined until /flags responds)
* if(posthog.getFeatureFlag('my-flag', { fresh: true }) === 'some-variant') { // do something }
*
* @param {String} key Key of the feature flag.
* @param {Object} options Optional settings.
* @param {boolean} [options.send_event=true] If false, won't send a $feature_flag_called event to PostHog.
* @param {boolean} [options.fresh=false] If true, only returns values loaded from the server, not cached localStorage values.
* Use this when you need to ensure the flag value reflects the current server state,
* such as after disabling a flag. Returns undefined until the /flags endpoint responds.
* @returns {boolean | string | undefined} The flag value, or undefined if not found or not yet loaded.
*/
PostHogFeatureFlags.prototype.getFeatureFlag = function (key, options) {
var _a;
if (options === void 0) { options = {}; }
if (options.fresh && !this._flagsLoadedFromRemote) {
return undefined;
}
if (!this._hasLoadedFlags && !(this.getFlags() && this.getFlags().length > 0)) {
this._logger.warn('getFeatureFlag for key "' + key + FLAG_TIMEOUT_MSG);
return undefined;
}
// Check if cache is stale and trigger refresh if needed
if (this._checkAndTriggerStaleRefresh()) {
return undefined;
}
var result = this.getFeatureFlagResult(key, options);
return (_a = result === null || result === void 0 ? void 0 : result.variant) !== null && _a !== void 0 ? _a : result === null || result === void 0 ? void 0 : result.enabled;
};
/*
* Retrieves the details for a feature flag.
*
* ### Usage:
*
* const details = getFeatureFlagDetails("my-flag")
* console.log(details.metadata.version)
* console.log(details.reason)
*
* @param {String} key Key of the feature flag.
*/
PostHogFeatureFlags.prototype.getFeatureFlagDetails = function (key) {
var details = this.getFlagsWithDetails();
return details[key];
};
/**
* @deprecated Use `getFeatureFlagResult()` instead which properly tracks the feature flag call.
* `getFeatureFlagPayload()` does not emit the `$feature_flag_called` event which may result in
* missing analytics. This method will be removed in a future version.
*/
PostHogFeatureFlags.prototype.getFeatureFlagPayload = function (key) {
// Don't send event to maintain backwards compatibility - this method never tracked calls
var result = this.getFeatureFlagResult(key, { send_event: false });
return result === null || result === void 0 ? void 0 : result.payload;
};
/**
* Get a feature flag result including both the flag value and payload, while properly tracking the call.
* This method emits the `$feature_flag_called` event by default.
*
* By default, this method may return cached values from localStorage if the `/flags` endpoint
* hasn't responded yet. This reduces flicker but means you might briefly see stale values
* (e.g., a flag that was disabled on the server).
*
* ### Usage:
*
* const result = posthog.getFeatureFlagResult('my-flag')
* if (result?.enabled) {
* console.log('Flag is enabled with payload:', result.payload)
* }
*
* // Only use fresh values from the server
* const freshResult = posthog.getFeatureFlagResult('my-flag', { fresh: true })
*
* @param {String} key Key of the feature flag.
* @param {Object} [options] Options for the feature flag lookup.
* @param {boolean} [options.send_event=true] If false, won't send the $feature_flag_called event.
* @param {boolean} [options.fresh=false] If true, only returns values loaded from the server, not cached localStorage values.
* Use this when you need to ensure the flag value reflects the current server state.
* Returns undefined until the /flags endpoint responds.
* @returns {FeatureFlagResult | undefined} The feature flag result including key, enabled, variant, and payload.
*/
PostHogFeatureFlags.prototype.getFeatureFlagResult = function (key, options) {
var _a, _b, _c, _d;
var _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
if (options === void 0) { options = {}; }
if (options.fresh && !this._flagsLoadedFromRemote) {
return undefined;
}
if (!this._hasLoadedFlags && !(this.getFlags() && this.getFlags().length > 0)) {
this._logger.warn('getFeatureFlagResult for key "' + key + FLAG_TIMEOUT_MSG);
return undefined;
}
// Check if cache is stale and trigger refresh if needed
if (this._checkAndTriggerStaleRefresh()) {
return undefined;
}
var flagVariants = this.getFlagVariants();
var flagExists = key in flagVariants;
var flagValue = flagVariants[key];
var payloads = this.getFlagPayloads();
var payload = payloads[key];
var flagReportValue = String(flagValue);
var requestId = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_REQUEST_ID) || undefined;
var evaluatedAt = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT) || undefined;
var flagCallReported = this._prop(constants_1.FLAG_CALL_REPORTED) || {};
var sessionIdToPersist;
// When session-scoped dedup is enabled, reset the reported flags whenever the session changes.
if (this._config.deduplicateCallsPerSession) {
var currentSessionId = (_e = this._client) === null || _e === void 0 ? void 0 : _e.session.sessionId;
var storedSessionId = this._prop(constants_1.FLAG_CALL_REPORTED_SESSION_ID);
if (currentSessionId && currentSessionId !== storedSessionId) {
flagCallReported = {};
sessionIdToPersist = currentSessionId;
}
}
if (options.send_event || !('send_event' in options)) {
if (!(key in flagCallReported) || !flagCallReported[key].includes(flagReportValue)) {
if ((0, core_1.isArray)(flagCallReported[key])) {
flagCallReported[key].push(flagReportValue);
}
else {
flagCallReported[key] = [flagReportValue];
}
this._set(__assign((_a = {}, _a[constants_1.FLAG_CALL_REPORTED] = flagCallReported, _a), (sessionIdToPersist ? (_b = {}, _b[constants_1.FLAG_CALL_REPORTED_SESSION_ID] = sessionIdToPersist, _b) : {})));
var flagDetails = this.getFeatureFlagDetails(key);
var errors = __spreadArray([], __read(((_f = this._prop(constants_1.PERSISTENCE_FEATURE_FLAG_ERRORS)) !== null && _f !== void 0 ? _f : [])), false);
if ((0, core_1.isUndefined)(flagValue)) {
errors.push(exports.FeatureFlagError.FLAG_MISSING);
}
var properties = {
$feature_flag: key,
$feature_flag_response: flagValue,
$feature_flag_payload: payload !== null && payload !== void 0 ? payload : null,
$feature_flag_request_id: requestId,
$feature_flag_evaluated_at: evaluatedAt,
$feature_flag_bootstrapped_response: (_j = (_h = (_g = this._config.bootstrap) === null || _g === void 0 ? void 0 : _g.featureFlags) === null || _h === void 0 ? void 0 : _h[key]) !== null && _j !== void 0 ? _j : null,
$feature_flag_bootstrapped_payload: (_m = (_l = (_k = this._config.bootstrap) === null || _k === void 0 ? void 0 : _k.featureFlagPayloads) === null || _l === void 0 ? void 0 : _l[key]) !== null && _m !== void 0 ? _m : null,
// If we haven't yet received a response from the /flags endpoint, we must have used the bootstrapped value
$used_bootstrap_value: !this._flagsLoadedFromRemote,
};
if (!(0, core_1.isUndefined)((_o = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.metadata) === null || _o === void 0 ? void 0 : _o.has_experiment)) {
properties.$feature_flag_has_experiment = flagDetails.metadata.has_experiment;
}
if (!(0, core_1.isUndefined)((_p = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.metadata) === null || _p === void 0 ? void 0 : _p.version)) {
properties.$feature_flag_version = flagDetails.metadata.version;
}
var reason = (_r = (_q = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.reason) === null || _q === void 0 ? void 0 : _q.description) !== null && _r !== void 0 ? _r : (_s = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.reason) === null || _s === void 0 ? void 0 : _s.code;
if (reason) {
properties.$feature_flag_reason = reason;
}
if ((_t = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.metadata) === null || _t === void 0 ? void 0 : _t.id) {
properties.$feature_flag_id = flagDetails.metadata.id;
}
// It's possible that flag values were overridden by calling overrideFeatureFlags.
// We want to capture the original values in case someone forgets they were using overrides
// and is wondering why their app is acting weird.
if (!(0, core_1.isUndefined)(flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.original_variant) || !(0, core_1.isUndefined)(flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.original_enabled)) {
properties.$feature_flag_original_response = !(0, core_1.isUndefined)(flagDetails.original_variant)
? flagDetails.original_variant
: flagDetails.original_enabled;
}
if ((_u = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.metadata) === null || _u === void 0 ? void 0 : _u.original_payload) {
properties.$feature_flag_original_payload = (_v = flagDetails === null || flagDetails === void 0 ? void 0 : flagDetails.metadata) === null || _v === void 0 ? void 0 : _v.original_payload;
}
if (errors.length) {
properties.$feature_flag_error = errors.join(',');
}
this._captureFeatureFlagCalled(properties);
}
else if (sessionIdToPersist) {
this._set((_c = {},
_c[constants_1.FLAG_CALL_REPORTED] = flagCallReported,
_c[constants_1.FLAG_CALL_REPORTED_SESSION_ID] = sessionIdToPersist,
_c));
}
}
else if (sessionIdToPersist) {
this._set((_d = {},
_d[constants_1.FLAG_CALL_REPORTED] = flagCallReported,
_d[constants_1.FLAG_CALL_REPORTED_SESSION_ID] = sessionIdToPersist,
_d));
}
if (!flagExists) {
return undefined;
}
return {
key: key,
enabled: !!flagValue,
variant: typeof flagValue === 'string' ? flagValue : undefined,
payload: (0, core_1.parsePayload)(payload),
};
};
PostHogFeatureFlags.prototype._captureFeatureFlagCalled = function (properties) {
var _this = this;
var _a;
try {
void ((_a = this._client) === null || _a === void 0 ? void 0 : _a.capture('$feature_flag_called', properties).catch(function (error) {
_this._logger.error('Failed to capture feature flag call', error);
}));
}
catch (error) {
this._logger.error('Failed to capture feature flag call', error);
}
};
/*
* Fetches the payload for a remote config feature flag. This method will bypass any cached values and fetch the latest
* value from the PostHog API.
*
* Note: Because the posthog-js SDK is primarily used with public project API keys, encrypted remote config payloads will
* be redacted, never decrypted in the response.
*
* ### Usage:
*
* getRemoteConfigPayload("home-page-welcome-message", (payload) => console.log(`Fetched remote config: ${payload}`))
*
* @param {String} key Key of the feature flag.
* @param {Function} [callback] The callback function will be called once the remote config feature flag payload has been fetched.
*/
PostHogFeatureFlags.prototype.getRemoteConfigPayload = function (key, callback) {
void this._getRemoteConfigPayload(key, callback);
};
PostHogFeatureFlags.prototype._getRemoteConfigPayload = function (key, callback) {
return __awaiter(this, void 0, void 0, function () {
var client, data, evaluationContexts, flagKeys, payload, response, payloads, error_1;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
client = this._client;
if (!client) {
return [2 /*return*/];
}
data = {
distinct_id: client.distinctId,
token: client.projectToken,
person_properties: { $lib: client.library.name, $lib_version: client.library.version },
};
evaluationContexts = this._getValidEvaluationEnvironments();
if (evaluationContexts.length) {
data.evaluation_contexts = evaluationContexts;
}
flagKeys = this._getValidFlagKeys();
if (!(0, core_1.isUndefined)(flagKeys)) {
data.flag_keys = flagKeys;
}
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, client.sendRequest('/flags/?v=2', {
target: 'flags',
method: 'POST',
body: data,
compression: this._config.compression,
sentAt: 'body',
timeoutMs: this._config.requestTimeoutMs,
})];
case 2:
response = _b.sent();
payloads = (_a = response.json) === null || _a === void 0 ? void 0 : _a.featureFlagPayloads;
payload = (payloads === null || payloads === void 0 ? void 0 : payloads[key]) || undefined;
return [3 /*break*/, 4];
case 3:
error_1 = _b.sent();
this._logger.error('Remote config feature flag request failed', error_1);
return [2 /*return*/];
case 4:
try {
callback(payload);
}
catch (error) {
this._logger.error('Remote config feature flag callback failed', error);
}
return [2 /*return*/];
}
});
});
};
PostHogFeatureFlags.prototype.isFeatureEnabled = function (key, options) {
if (options === void 0) { options = {}; }
if (options.fresh && !this._flagsLoadedFromRemote) {
return options.defaultValue;
}
if (!this._hasLoadedFlags && !(this.getFlags() && this.getFlags().length > 0)) {
this._logger.warn('isFeatureEnabled for key "' + key + FLAG_TIMEOUT_MSG);
return options.defaultValue;
}
var flagValue = this.getFeatureFlag(key, options);
return (0, core_1.isUndefined)(flagValue) ? options.defaultValue : !!flagValue;
};
PostHogFeatureFlags.prototype.addFeatureFlagsHandler = function (handler) {
this.featureFlagEventHandlers.push(handler);
};
PostHogFeatureFlags.prototype.removeFeatureFlagsHandler = function (handler) {
this.featureFlagEventHandlers = this.featureFlagEventHandlers.filter(function (h) { return h !== handler; });
};
PostHogFeatureFlags.prototype.receivedFeatureFlags = function (response, errorsLoading, options) {
void this._receivedFeatureFlags(response, errorsLoading, options);
};
PostHogFeatureFlags.prototype._receivedFeatureFlags = function (response, errorsLoading, options) {
if (!this._client) {
return;
}
this._hasLoadedFlags = true;
var currentFlags = this.getFlagVariants();
var currentFlagPayloads = this.getFlagPayloads();
var currentFlagDetails = this.getFlagsWithDetails();
var statePatch = (0, exports.parseFlagsResponse)(response, currentFlags, currentFlagPayloads, currentFlagDetails, options, this._logger);
if (statePatch) {
this._markCrossTabFeatureFlagSnapshot(statePatch, response, !!(options === null || options === void 0 ? void 0 : options.partialResponse));
this._set(statePatch);
}
// Reset stale refresh flag when we successfully receive fresh flags
if (!errorsLoading) {
this._staleCacheRefreshTriggered = false;
}
this._fireFeatureFlagsCallbacks(errorsLoading);
};
/**
* @deprecated Use overrideFeatureFlags instead. This will be removed in a future version.
*/
PostHogFeatureFlags.prototype.override = function (flags, suppressWarning) {
if (suppressWarning === void 0) { suppressWarning = false; }
this._logger.warn('override is deprecated. Please use overrideFeatureFlags instead.');
this.overrideFeatureFlags({
flags: flags,
suppressWarning: suppressWarning,
});
};
/**
* Override feature flags on the client-side. Useful for setting non-persistent feature flags,
* or for testing/debugging feature flags in the PostHog app.
*
* ### Usage:
*
* - posthog.featureFlags.overrideFeatureFlags(false) // clear all overrides
* - posthog.featureFlags.overrideFeatureFlags(['beta-feature']) // enable flags
* - posthog.featureFlags.overrideFeatureFlags({'beta-feature': 'variant'}) // set variants
* - posthog.featureFlags.overrideFeatureFlags({ flags: ['beta-feature'] }) // enable flags
* - posthog.featureFlags.overrideFeatureFlags({ flags: {'beta-feature': 'variant'} }) // set variants
* - posthog.featureFlags.overrideFeatureFlags({ // set both flags and payloads
* flags: {'beta-feature': 'variant'},
* payloads: { 'beta-feature': { someData: true } }
* })
* - posthog.featureFlags.overrideFeatureFlags({ // only override payloads
* payloads: { 'beta-feature': { someData: true } }
* })
*/
PostHogFeatureFlags.prototype.overrideFeatureFlags = function (overrideOptions) {
this._overrideFeatureFlags(overrideOptions);
};
PostHogFeatureFlags.prototype._overrideFeatureFlags = function (overrideOptions) {
var _a, _b;
var _c;
if (!this._client) {
this._logger.warn('posthog.featureFlags.overrideFeatureFlags called before feature flags were ready');
return;
}
// Clear all overrides if false, lets you do something like posthog.featureFlags.overrideFeatureFlags(false)
if (overrideOptions === false) {
this._remove([constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS, constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS]);
this._fireFeatureFlagsCallbacks();
forceDebugLogger.info('All overrides cleared');
return;
}
// Array syntax: ['flag-a', 'flag-b'] -> { 'flag-a': true, 'flag-b': true }
if ((0, core_1.isArray)(overrideOptions)) {
this._set((_a = {}, _a[constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS] = arrayToFlagsRecord(overrideOptions), _a));
this._fireFeatureFlagsCallbacks();
forceDebugLogger.info('Flag overrides set', { flags: overrideOptions });
return;
}
if (overrideOptions &&
typeof overrideOptions === 'object' &&
('flags' in overrideOptions || 'payloads' in overrideOptions)) {
var options = overrideOptions;
this._override_warning = Boolean((_c = options.suppressWarning) !== null && _c !== void 0 ? _c : false);
var statePatch = {};
var flags = options.flags;
var payloads = options.payloads;
// Handle flags if provided, lets you do something like posthog.featureFlags.overrideFeatureFlags({flags: ['beta-feature']})
if (flags) {
statePatch[constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS] = (0, core_1.isArray)(flags) ? arrayToFlagsRecord(flags) : flags;
}
// Handle payloads independently, lets you do something like posthog.featureFlags.overrideFeatureFlags({payloads: { 'beta-feature': { someData: true } }})
if (payloads) {
statePatch[constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS] = payloads;
}
if (Object.keys(statePatch).length) {
this._set(statePatch);
}
if (flags === false && payloads === false) {
this._remove([constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS, constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS]);
}
else if (flags === false) {
this._remove(constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS);
}
else if (payloads === false) {
this._remove(constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS);
}
this._fireFeatureFlagsCallbacks();
if (flags === false) {
forceDebugLogger.info('Flag overrides cleared');
}
else if (flags) {
forceDebugLogger.info('Flag overrides set', { flags: flags });
}
if (payloads === false) {
forceDebugLogger.info('Payload overrides cleared');
}
else if (payloads) {
forceDebugLogger.info('Payload overrides set', { payloads: payloads });
}
return;
}
// Fallback: treat as Record<string, string | boolean>, e.g. {'beta-feature': 'variant'}
if (overrideOptions && typeof overrideOptions === 'object') {
this._set((_b = {},
_b[constants_1.PERSISTENCE_OVERRIDE_FEATURE_FLAGS] = overrideOptions,
_b));
this._fireFeatureFlagsCallbacks();
forceDebugLogger.info('Flag overrides set', { flags: overrideOptions });
return;
}
this._logger.warn('Invalid overrideOptions provided to overrideFeatureFlags', { overrideOptions: overrideOptions });
};
/*
* Register an event listener that runs when feature flags become available or when they change.
* If there are flags, the listener is called immediately in addition to being called on future changes.
*
* ### Usage:
*
* posthog.onFeatureFlags(function(featureFlags, featureFlagsVariants, { errorsLoading }) { // do something })
*
* @param {Function} [callback] The callback function will be called once the feature flags are ready or when they are updated.
* It'll return a list of feature flags enabled for the user, the variants,
* and also a context object indicating whether we succeeded to fetch the flags or not.
* @returns {Function} A function that can be called to unsubscribe the listener. Used by useEffect when the component unmounts.
*/
PostHogFeatureFlags.prototype.onFeatureFlags = function (callback) {
var _this = this;
this.addFeatureFlagsHandler(callback);
if (this._hasLoadedFlags) {
var _a = this._prepareFeatureFlagsForCallbacks(), flags = _a.flags, flagVariants = _a.flagVariants;
// Isolate the callback so a user-provided handler that throws surfaces as a logged
// error rather than propagating out of onFeatureFlags as a posthog-js SDK error.
try {
callback(flags, flagVariants);
}
catch (error) {
this._logger.error('Error while running feature flags callback', error);
}
}
return function () { return _this.removeFeatureFlagsHandler(callback); };
};
PostHogFeatureFlags.prototype.updateEarlyAccessFeatureEnrollment = function (key, isEnrolled, stage) {
var _a, _b, _c, _d;
var _this = this;
var _e, _f, _g;
var existing_early_access_features = this._prop(constants_1.PERSISTENCE_EARLY_ACCESS_FEATURES) || [];
var feature = existing_early_access_features.find(function (f) { return f.flagKey === key; });
var enrollmentPersonProp = (_a = {},
_a["$feature_enrollment/".concat(key)] = isEnrolled,
_a);
var properties = {
$feature_flag: key,
$feature_enrollment: isEnrolled,
$set: enrollmentPersonProp,
};
if (feature) {
properties['$early_access_feature_name'] = feature.name;
}
if (stage) {
properties['$feature_enrollment_stage'] = stage;
}
var newFlags = __assign(__assign({}, this.getFlagVariants()), (_b = {}, _b[key] = isEnrolled, _b));
(_f = (_e = this._instance) === null || _e === void 0 ? void 0 : _e.persistence) === null || _f === void 0 ? void 0 : _f.markCrossTabFeatureFlagChanges((_c = {},
_c[constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS] = [key],
_c[constants_1.ENABLED_FEATURE_FLAGS] = [key],
_c[constants_1.STORED_PERSON_PROPERTIES_KEY] = Object.keys(enrollmentPersonProp),
_c));
this._set((_d = {},
_d[constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS] = Object.keys((0, exports.filterActiveFeatureFlags)(newFlags)),
_d[constants_1.ENABLED_FEATURE_FLAGS] = newFlags,
_d[constants_1.STORED_PERSON_PROPERTIES_KEY] = __assign(__assign({}, (this._prop(constants_1.STORED_PERSON_PROPERTIES_KEY) || {})), enrollmentPersonProp),
_d));
this._fireFeatureFlagsCallbacks();
try {
void ((_g = this._client) === null || _g === void 0 ? void 0 : _g.capture('$feature_enrollment_update', properties).catch(function (error) {
_this._logger.error('Failed to capture early access feature enrollment', error);
}));
}
catch (error) {
this._logger.error('Failed to capture early access feature enrollment', error);
}
};
PostHogFeatureFlags.prototype.getEarlyAccessFeatures = function (callback, force_reload, stages) {
if (force_reload === void 0) { force_reload = false; }
var existing_early_access_features = this._prop(constants_1.PERSISTENCE_EARLY_ACCESS_FEATURES);
if (existing_early_access_features && !force_reload) {
callback(existing_early_access_features);
return;
}
void this._getEarlyAccessFeatures(callback, stages);
};
// TODO: Consider moving early access features to their own extension.
PostHogFeatureFlags.prototype._getEarlyAccessFeatures = function (callback, stages) {
return __awaiter(this, void 0, void 0, function () {
var client, stageParams, earlyAccessFeatures, response, error_2;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
client = this._client;
if (!client) {
return [2 /*return*/];
}
stageParams = stages ? "&".concat(stages.map(function (s) { return "stage=".concat(s); }).join('&')) : '';
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, client.sendRequest("/api/early_access_features/?token=".concat(client.projectToken).concat(stageParams), {
target: 'api',
method: 'GET',
sentAt: 'query',
})];
case 2:
response = _b.sent();
if (!response.json) {
return [2 /*return*/];
}
earlyAccessFeatures = response.json.earlyAccessFeatures;
this._set((_a = {}, _a[constants_1.PERSISTENCE_EARLY_ACCESS_FEATURES] = earlyAccessFeatures, _a));
return [3 /*break*/, 4];
case 3:
error_2 = _b.sent();
this._logger.error('Early access feature request failed', error_2);
return [2 /*return*/];
case 4:
try {
callback(earlyAccessFeatures);
}
catch (error) {
this._logger.error('Early access feature callback failed', error);
}
return [2 /*return*/];
}
});
});
};
PostHogFeatureFlags.prototype._prepareFeatureFlagsForCallbacks = function () {
var flags = this.getFlags();
var flagVariants = this.getFlagVariants();
// Return truthy
var truthyFlags = flags.filter(function (flag) { return flagVariants[flag]; });
var truthyFlagVariants = Object.keys(flagVariants)
.filter(function (variantKey) { return flagVariants[variantKey]; })
.reduce(function (res, key) {
res[key] = flagVariants[key];
return res;
}, {});
return {
flags: truthyFlags,
flagVariants: truthyFlagVariants,
};
};
PostHogFeatureFlags.prototype._fireFeatureFlagsCallbacks = function (errorsLoading) {
var _this = this;
this._rebuildEventProperties();
var _a = this._prepareFeatureFlagsForCallbacks(), flags = _a.flags, flagVariants = _a.flagVariants;
this.featureFlagEventHandlers.forEach(function (handler) {
// Isolate each handler: a user-provided onFeatureFlags callback that throws must not
// break the callback chain (preventing later handlers from firing) or surface as a
// posthog-js SDK error in error tracking.
try {
handler(flags, flagVariants, { errorsLoading: errorsLoading });
}
catch (error) {
_this._logger.error('Error while running feature flags callback', error);
}
});
};
/**
* Set override person properties for feature flags.
* This is used when dealing with new persons / where you don't want to wait for ingestion
* to update user properties.
*/
PostHogFeatureFlags.prototype.setPersonPropertiesForFlags = function (properties, reloadFeatureFlags) {
if (reloadFeatureFlags === void 0) { reloadFeatureFlags = true; }
this._setPersonPropertiesForFlags(properties, reloadFeatureFlags);
};
PostHogFeatureFlags.prototype._setPersonPropertiesForFlags = function (properties, reloadFeatureFlags) {
var _a;
if (reloadFeatureFlags === void 0) { reloadFeatureFlags = true; }
var existingProperties = this._prop(constants_1.STORED_PERSON_PROPERTIES_KEY) || {};
// If the caller passes { $set, $set_once }, split them apart so we can apply $set_once
// semantics (skip keys that already exist). Otherwise treat all properties as $set for
// backward compatibility with the public API.
var propsToSet = (properties === null || properties === void 0 ? void 0 : properties['$set']) || (!(properties === null || properties === void 0 ? void 0 : properties['$set_once']) ? properties : {});
var propsToSetOnce = properties === null || properties === void 0 ? void 0 : properties['$set_once'];
var setOnceProps = {};
if (propsToSetOnce) {
for (var key in propsToSetOnce) {
if (Object.prototype.hasOwnProperty.call(propsToSetOnce, key)) {
if (!(key in existingProperties)) {
setOnceProps[key] = propsToSetOnce[key];
}
}
}
}
this._set((_a = {},
_a[constants_1.STORED_PERSON_PROPERTIES_KEY] = __assign(__assign(__assign({}, existingProperties), setOnceProps), propsToSet),
_a));
if (reloadFeatureFlags) {
this.reloadFeatureFlags();
}
};
/**
* Remove override person properties used for feature flags.
* This is the counterpart to setPersonPropertiesForFlags, used when person properties
* are unset so flags re-evaluate without the removed values.
*/
PostHogFeatureFlags.prototype.unsetPersonPropertiesForFlags = function (propertyNames, reloadFeatureFlags) {
var _a;
if (reloadFeatureFlags === void 0) { reloadFeatureFlags = true; }
var existingProperties = this._prop(constants_1.STORED_PERSON_PROPERTIES_KEY) || {};
var nextProperties = __assign({}, existingProperties);
propertyNames.forEach(function (name) {
delete nextProperties[name];
});
this._set((_a = {}, _a[constants_1.STORED_PERSON_PROPERTIES_KEY] = nextProperties, _a));
if (reloadFeatureFlags) {
this.reloadFeatureFlags();
}
};
PostHogFeatureFlags.prototype.resetPersonPropertiesForFlags = function (reloadFeatureFlags) {
if (reloadFeatureFlags === void 0) { reloadFeatureFlags = true; }
this._remove(constants_1.STORED_PERSON_PROPERTIES_KEY);
if (reloadFeatureFlags) {
this.reloadFeatureFlags();
}
};
/**
* Set override group properties for feature flags.
* This is used when dealing with new groups / where you don't want to wait for ingestion
* to update properties.
* Takes in an object, the key of which is the group type.
* For example:
* setGroupPropertiesForFlags({'organization': { name: 'CYZ', employees: '11' } })
*/
PostHogFeatureFlags.prototype.setGroupPropertiesForFlags = function (properties, reloadFeatureFlags) {
var e_7, _a, _b;
if (reloadFeatureFlags === void 0) { reloadFeatureFlags = true; }
var existingProperties = (this._prop(constants_1.STORED_GROUP_PROPERTIES_KEY) || {});
var nextProperties = __assign({}, existingProperties);
try {
for (var _c = __values(Object.keys(properties)), _d = _c.next(); !_d.done; _d = _c.next()) {
var groupType = _d.value;
nextProperties[groupType] = __assign(__assign({}, existingProperties[groupType]), properties[groupType]);
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_7) throw e_7.error; }
}
this._set((_b = {}, _b[constants_1.STORED_GROUP_PROPERTIES_KEY] = nextProperties, _b));
if (reloadFeatureFlags) {
this.reloadFeatureFlags();
}
};
PostHogFeatureFlags.prototype.resetGroupPropertiesForFlags = function (group_type) {
var _a, _b;
if (group_type) {
var existingProperties = this._prop(constants_1.STORED_GROUP_PROPERTIES_KEY) || {};
this._set((_a = {},
_a[constants_1.STORED_GROUP_PROPERTIES_KEY] = __assign(__assign({}, existingProperties), (_b = {}, _b[group_type] = {}, _b)),
_a));
}
else {
this._remove(constants_1.STORED_GROUP_PROPERTIES_KEY);
}
};
PostHogFeatureFlags.prototype.reset = function () {
this._requestGeneration++;
this._additionalReloadRequested = false;
this._baseEventProperties = {};
this._eventPropertiesWithFlagValues = {};
this._hasLoadedFlags = false;
this._reloadingDisabled = false;
this._flagsLoadedFromRemote = false;
this.$anon_distinct_id = undefined;
this._clearDebouncer();
this._override_warning = false;
this._consecutiveStatusZeroFailures = 0;
};
return PostHogFeatureFlags;
}());
exports.PostHogFeatureFlags = PostHogFeatureFlags;
//# sourceMappingURL=posthog-featureflags.js.map