@posthog/browser-common 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1222 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ __webpack_require__.d(__webpack_exports__, {
31
+ FeatureFlagError: ()=>FeatureFlagError,
32
+ PostHogFeatureFlags: ()=>PostHogFeatureFlags,
33
+ QuotaLimitedResource: ()=>QuotaLimitedResource,
34
+ filterActiveFeatureFlags: ()=>filterActiveFeatureFlags,
35
+ parseFlagsResponse: ()=>parseFlagsResponse
36
+ });
37
+ const general_utils_js_namespaceObject = require("./utils/general-utils.js");
38
+ const external_constants_js_namespaceObject = require("./constants.js");
39
+ const core_namespaceObject = require("@posthog/core");
40
+ const logger_js_namespaceObject = require("./utils/logger.js");
41
+ const timezone_js_namespaceObject = require("./utils/timezone.js");
42
+ const promise_utils_js_namespaceObject = require("./utils/promise-utils.js");
43
+ const request_reachability_js_namespaceObject = require("./utils/request-reachability.js");
44
+ const logger = (0, logger_js_namespaceObject.createLogger)('[FeatureFlags]');
45
+ const forceDebugLogger = (0, logger_js_namespaceObject.createLogger)('[FeatureFlags]', {
46
+ debugEnabled: true
47
+ });
48
+ const FLAG_TIMEOUT_MSG = '" failed. Feature flags didn\'t load in time.';
49
+ const MAX_CONSECUTIVE_FLAGS_STATUS_ZERO_FAILURES = 3;
50
+ const RETRYABLE_FLAGS_HTTP_STATUSES = [
51
+ 502,
52
+ 504
53
+ ];
54
+ const FLAGS_RETRY_DELAY_MS = 500;
55
+ const isFlagsTimeout = (response)=>0 === response.statusCode && response.error instanceof Error && 'AbortError' === response.error.name;
56
+ const isRetryableFlagsResponse = (response)=>RETRYABLE_FLAGS_HTTP_STATUSES.includes(response.statusCode) || isFlagsTimeout(response);
57
+ const MAX_IDLE_REFRESH_INTERVAL_MS = 3600000;
58
+ const USER_INTERACTION_EVENTS = [
59
+ 'click',
60
+ 'keydown',
61
+ 'wheel',
62
+ 'touchstart',
63
+ 'pointerdown'
64
+ ];
65
+ const FeatureFlagError = {
66
+ ERRORS_WHILE_COMPUTING: 'errors_while_computing_flags',
67
+ FLAG_MISSING: 'flag_missing',
68
+ QUOTA_LIMITED: 'quota_limited',
69
+ TIMEOUT: 'timeout',
70
+ CONNECTION_ERROR: 'connection_error',
71
+ UNKNOWN_ERROR: 'unknown_error',
72
+ apiError: (status)=>`api_error_${status}`
73
+ };
74
+ const arrayToFlagsRecord = (flags)=>{
75
+ const flagsObj = {};
76
+ for(let i = 0; i < flags.length; i++)flagsObj[flags[i]] = true;
77
+ return flagsObj;
78
+ };
79
+ const filterActiveFeatureFlags = (featureFlags)=>{
80
+ const activeFeatureFlags = {};
81
+ for (const [key, value] of (0, general_utils_js_namespaceObject.entries)(featureFlags || {}))if (value) activeFeatureFlags[key] = value;
82
+ return activeFeatureFlags;
83
+ };
84
+ const parseFlagsResponse = (response, currentFlags = {}, currentFlagPayloads = {}, currentFlagDetails = {}, options, responseLogger = logger)=>{
85
+ const normalizedResponse = normalizeFlagsResponse(response, responseLogger);
86
+ const flagDetails = normalizedResponse.flags;
87
+ const featureFlags = normalizedResponse.featureFlags;
88
+ const flagPayloads = normalizedResponse.featureFlagPayloads;
89
+ if (!featureFlags) return;
90
+ const requestId = response['requestId'];
91
+ const evaluatedAt = response['evaluatedAt'];
92
+ if ((0, core_namespaceObject.isArray)(featureFlags)) {
93
+ responseLogger.warn('v1 of the feature flags endpoint is deprecated. Please use the latest version.');
94
+ const $enabled_feature_flags = {};
95
+ if (featureFlags) for(let i = 0; i < featureFlags.length; i++)$enabled_feature_flags[featureFlags[i]] = true;
96
+ return {
97
+ [external_constants_js_namespaceObject.PERSISTENCE_ACTIVE_FEATURE_FLAGS]: featureFlags,
98
+ [external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS]: $enabled_feature_flags,
99
+ [external_constants_js_namespaceObject.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS]: false
100
+ };
101
+ }
102
+ let newFeatureFlags = featureFlags;
103
+ let newFeatureFlagPayloads = flagPayloads;
104
+ let newFeatureFlagDetails = flagDetails;
105
+ if (options?.partialResponse) {
106
+ const evaluatedFlagKeys = Object.keys(newFeatureFlags);
107
+ const evaluatedPayloads = newFeatureFlagPayloads || {};
108
+ newFeatureFlags = {
109
+ ...currentFlags,
110
+ ...newFeatureFlags
111
+ };
112
+ newFeatureFlagPayloads = {
113
+ ...currentFlagPayloads,
114
+ ...evaluatedPayloads
115
+ };
116
+ evaluatedFlagKeys.forEach((key)=>{
117
+ if (!(key in evaluatedPayloads)) delete newFeatureFlagPayloads?.[key];
118
+ });
119
+ newFeatureFlagDetails = {
120
+ ...currentFlagDetails,
121
+ ...newFeatureFlagDetails
122
+ };
123
+ } else if (response.errorsWhileComputingFlags) if (flagDetails) {
124
+ const successfulKeys = new Set(Object.keys(flagDetails).filter((key)=>!flagDetails[key]?.failed));
125
+ newFeatureFlags = {
126
+ ...currentFlags,
127
+ ...Object.fromEntries(Object.entries(newFeatureFlags).filter(([key])=>successfulKeys.has(key)))
128
+ };
129
+ const successfulPayloads = Object.fromEntries(Object.entries(newFeatureFlagPayloads || {}).filter(([key])=>successfulKeys.has(key)));
130
+ newFeatureFlagPayloads = {
131
+ ...currentFlagPayloads,
132
+ ...successfulPayloads
133
+ };
134
+ successfulKeys.forEach((key)=>{
135
+ if (!(key in successfulPayloads)) delete newFeatureFlagPayloads?.[key];
136
+ });
137
+ newFeatureFlagDetails = {
138
+ ...currentFlagDetails,
139
+ ...Object.fromEntries(Object.entries(newFeatureFlagDetails || {}).filter(([key])=>successfulKeys.has(key)))
140
+ };
141
+ } else {
142
+ newFeatureFlags = {
143
+ ...currentFlags,
144
+ ...newFeatureFlags
145
+ };
146
+ newFeatureFlagPayloads = {
147
+ ...currentFlagPayloads,
148
+ ...newFeatureFlagPayloads
149
+ };
150
+ newFeatureFlagDetails = {
151
+ ...currentFlagDetails,
152
+ ...newFeatureFlagDetails
153
+ };
154
+ }
155
+ return {
156
+ [external_constants_js_namespaceObject.PERSISTENCE_ACTIVE_FEATURE_FLAGS]: Object.keys(filterActiveFeatureFlags(newFeatureFlags)),
157
+ [external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS]: newFeatureFlags || {},
158
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS]: newFeatureFlagPayloads || {},
159
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_DETAILS]: newFeatureFlagDetails || {},
160
+ [external_constants_js_namespaceObject.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS]: true === response.minimalFlagCalledEvents,
161
+ ...requestId ? {
162
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_REQUEST_ID]: requestId
163
+ } : {},
164
+ ...evaluatedAt ? {
165
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT]: evaluatedAt
166
+ } : {}
167
+ };
168
+ };
169
+ const normalizeFlagsResponse = (response, responseLogger)=>{
170
+ const flagDetails = response['flags'];
171
+ if (flagDetails) {
172
+ const featureFlags = Object.fromEntries(Object.keys(flagDetails).map((flag)=>[
173
+ flag,
174
+ flagDetails[flag].variant ?? flagDetails[flag].enabled
175
+ ]));
176
+ const featureFlagPayloads = Object.fromEntries(Object.keys(flagDetails).filter((flag)=>flagDetails[flag].enabled).filter((flag)=>!(0, core_namespaceObject.isUndefined)(flagDetails[flag].metadata?.payload)).map((flag)=>[
177
+ flag,
178
+ flagDetails[flag].metadata?.payload
179
+ ]));
180
+ return {
181
+ ...response,
182
+ featureFlags,
183
+ featureFlagPayloads
184
+ };
185
+ }
186
+ if (response['featureFlags']) responseLogger.warn('Using an older version of the feature flags endpoint. Please upgrade your PostHog server to the latest version');
187
+ return response;
188
+ };
189
+ const QuotaLimitedResource = {
190
+ FeatureFlags: 'feature_flags',
191
+ Recordings: 'recordings'
192
+ };
193
+ class PostHogFeatureFlags {
194
+ constructor(configSource){
195
+ this.name = 'featureFlags';
196
+ this._override_warning = false;
197
+ this.featureFlagEventHandlers = [];
198
+ this._logger = logger;
199
+ this._baseEventProperties = {};
200
+ this._eventPropertiesWithFlagValues = {};
201
+ this._reloadingHandlers = [];
202
+ this._pendingReloads = new Set();
203
+ this._activeReloads = new Set();
204
+ this._hasLoadedFlags = false;
205
+ this._requestInFlight = false;
206
+ this._requestGeneration = 0;
207
+ this._reloadingDisabled = false;
208
+ this._additionalReloadRequested = false;
209
+ this._flagsLoadedFromRemote = false;
210
+ this._staleCacheRefreshTriggered = false;
211
+ this._consecutiveStatusZeroFailures = 0;
212
+ this._hadUserInteraction = false;
213
+ this._onOnline = ()=>{
214
+ const wasTripped = this._hasStatusZeroCircuitBreakerTripped();
215
+ this._consecutiveStatusZeroFailures = 0;
216
+ if (wasTripped) this.reloadFeatureFlags();
217
+ };
218
+ this._refreshIfDue = ()=>{
219
+ const refreshIntervalMs = this._refreshIntervalMs;
220
+ const dueIntervalMs = this._dueRefreshIntervalMs ?? refreshIntervalMs;
221
+ if ((0, core_namespaceObject.isUndefined)(refreshIntervalMs) || (0, core_namespaceObject.isUndefined)(dueIntervalMs) || this._config.remoteRequestsDisabled || !this._document || 'hidden' === this._document.visibilityState || Date.now() - (this._lastRefreshAt ?? 0) < dueIntervalMs) return;
222
+ this._dueRefreshIntervalMs = this._hadUserInteraction || !this._config.idleRefreshBackoff ? refreshIntervalMs : Math.min(2 * dueIntervalMs, MAX_IDLE_REFRESH_INTERVAL_MS);
223
+ this._hadUserInteraction = false;
224
+ this.reloadFeatureFlags();
225
+ this._scheduleNextRefresh();
226
+ };
227
+ this._onUserInteraction = ()=>{
228
+ if (this._hadUserInteraction && this._dueRefreshIntervalMs === this._refreshIntervalMs) return;
229
+ this._resumeConfiguredInterval();
230
+ };
231
+ this._onVisibilityChange = ()=>{
232
+ if (this._document?.visibilityState === 'visible') this._resumeConfiguredInterval();
233
+ };
234
+ this._configSource = configSource;
235
+ }
236
+ get _crossTabPersistence() {}
237
+ _onConfigUpdated() {
238
+ if (this._client) this._syncAutomaticRefresh();
239
+ }
240
+ setup(client) {
241
+ try {
242
+ this._window = "u" < typeof window ? void 0 : window;
243
+ this._document = "u" < typeof document ? void 0 : document;
244
+ } catch {
245
+ this._window = void 0;
246
+ this._document = void 0;
247
+ }
248
+ this._initializingClient = client;
249
+ this._logger = client.logger.createLogger('[FeatureFlags]');
250
+ return (0, promise_utils_js_namespaceObject.continueWith)(client.kv.initialize(), ()=>{
251
+ if (this._initializingClient !== client) return;
252
+ this._initializingClient = void 0;
253
+ this._client = client;
254
+ this._finishSetup(client);
255
+ });
256
+ }
257
+ _finishSetup(client) {
258
+ if (this._client !== client) return;
259
+ if (this._window) (0, general_utils_js_namespaceObject.addEventListener)(this._window, 'online', this._onOnline);
260
+ this._syncAutomaticRefresh();
261
+ this._dynamicProperties = client.registerDynamicEventProperties(()=>this._isCacheStale() ? this._baseEventProperties : this._eventPropertiesWithFlagValues);
262
+ this._crossTabPersistenceUnsubscribe = this._crossTabPersistence?.onCrossTabFeatureFlagChange(()=>{
263
+ this._clearBootstrapState();
264
+ this._rebuildEventProperties();
265
+ this._fireFeatureFlagsCallbacks();
266
+ });
267
+ this._rebuildEventProperties();
268
+ return this.initialize();
269
+ }
270
+ _resumeConfiguredInterval() {
271
+ this._hadUserInteraction = true;
272
+ this._dueRefreshIntervalMs = this._refreshIntervalMs;
273
+ this._refreshIfDue();
274
+ }
275
+ _syncAutomaticRefresh() {
276
+ const configuredIntervalMs = this._config.refreshIntervalMs;
277
+ const refreshIntervalMs = !this._config.remoteRequestsDisabled && this._document && !(0, core_namespaceObject.isUndefined)(configuredIntervalMs) && configuredIntervalMs > 0 ? configuredIntervalMs : void 0;
278
+ if (!this._config.idleRefreshBackoff) this._dueRefreshIntervalMs = refreshIntervalMs;
279
+ if (refreshIntervalMs === this._refreshIntervalMs) return;
280
+ this._stopAutomaticRefresh();
281
+ if ((0, core_namespaceObject.isUndefined)(refreshIntervalMs)) return;
282
+ this._refreshIntervalMs = refreshIntervalMs;
283
+ this._dueRefreshIntervalMs = refreshIntervalMs;
284
+ this._scheduleNextRefresh();
285
+ if (this._document?.addEventListener) {
286
+ (0, general_utils_js_namespaceObject.addEventListener)(this._document, external_constants_js_namespaceObject.DOM_EVENT_VISIBILITYCHANGE, this._onVisibilityChange);
287
+ (0, general_utils_js_namespaceObject.eachArray)(USER_INTERACTION_EVENTS, (eventName)=>{
288
+ (0, general_utils_js_namespaceObject.addEventListener)(this._document, eventName, this._onUserInteraction, {
289
+ capture: true
290
+ });
291
+ });
292
+ }
293
+ }
294
+ _scheduleNextRefresh() {
295
+ if ((0, core_namespaceObject.isUndefined)(this._refreshIntervalMs)) return;
296
+ if (!(0, core_namespaceObject.isUndefined)(this._refreshInterval)) clearInterval(this._refreshInterval);
297
+ this._lastRefreshAt = Date.now();
298
+ this._refreshInterval = setInterval(this._refreshIfDue, this._refreshIntervalMs);
299
+ }
300
+ _stopAutomaticRefresh() {
301
+ if (!(0, core_namespaceObject.isUndefined)(this._refreshInterval)) {
302
+ clearInterval(this._refreshInterval);
303
+ this._refreshInterval = void 0;
304
+ this._document?.removeEventListener?.(external_constants_js_namespaceObject.DOM_EVENT_VISIBILITYCHANGE, this._onVisibilityChange);
305
+ (0, general_utils_js_namespaceObject.eachArray)(USER_INTERACTION_EVENTS, (eventName)=>{
306
+ this._document?.removeEventListener?.(eventName, this._onUserInteraction, {
307
+ capture: true
308
+ });
309
+ });
310
+ }
311
+ this._refreshIntervalMs = void 0;
312
+ this._dueRefreshIntervalMs = void 0;
313
+ this._hadUserInteraction = false;
314
+ this._lastRefreshAt = void 0;
315
+ }
316
+ destroy() {
317
+ this._dispose();
318
+ }
319
+ dispose() {
320
+ this._dispose();
321
+ }
322
+ _dispose() {
323
+ this._stopAutomaticRefresh();
324
+ this._requestGeneration++;
325
+ this._additionalReloadRequested = false;
326
+ this._initializingClient = void 0;
327
+ this._settleReloads(this._pendingReloads, 'cancelled');
328
+ this._settleReloads(this._activeReloads, 'cancelled');
329
+ if (!this._client) return;
330
+ this._clearDebouncer();
331
+ this._dynamicProperties?.dispose();
332
+ this._dynamicProperties = void 0;
333
+ this._crossTabPersistenceUnsubscribe?.();
334
+ this._crossTabPersistenceUnsubscribe = void 0;
335
+ this._reloadingHandlers = [];
336
+ this._window?.removeEventListener('online', this._onOnline);
337
+ this._client = void 0;
338
+ }
339
+ get _config() {
340
+ return this._configSource.get();
341
+ }
342
+ _prop(key) {
343
+ if (this._bootstrapState && key in this._bootstrapState) return this._bootstrapState[key];
344
+ return this._client?.kv.get(key);
345
+ }
346
+ _clearBootstrapState() {
347
+ if (!this._bootstrapState) return false;
348
+ this._bootstrapState = void 0;
349
+ return true;
350
+ }
351
+ _fallBackToPersistedFlags() {
352
+ if ((0, core_namespaceObject.isUndefined)(this._client?.kv.get(external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS))) return false;
353
+ return this._clearBootstrapState();
354
+ }
355
+ _set(properties) {
356
+ this._persist(()=>this._client?.kv.set(properties));
357
+ }
358
+ _markCrossTabFeatureFlagSnapshot(statePatch, response, partialResponse) {
359
+ if (!this._crossTabPersistence || !statePatch[external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS]) return;
360
+ const previousFlags = this._prop(external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS) || {};
361
+ const nextFlags = statePatch[external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS] || {};
362
+ const responseFlags = response.flags || response.featureFlags;
363
+ const successfulResponseFlagKeys = response.flags ? Object.entries(response.flags).filter(([, detail])=>!detail?.failed).map(([key])=>key) : [];
364
+ const isIncompleteResponse = !!response.errorsWhileComputingFlags && !!response.flags;
365
+ const ownedFlagKeys = isIncompleteResponse ? successfulResponseFlagKeys : partialResponse && !(0, core_namespaceObject.isArray)(responseFlags) ? Object.keys(responseFlags || {}) : Array.from(new Set([
366
+ ...Object.keys(previousFlags),
367
+ ...Object.keys(nextFlags)
368
+ ]));
369
+ const previousPayloads = this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS) || {};
370
+ const nextPayloads = statePatch[external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS] || {};
371
+ const ownedPayloadKeys = isIncompleteResponse || partialResponse ? ownedFlagKeys : Array.from(new Set([
372
+ ...Object.keys(previousPayloads),
373
+ ...Object.keys(nextPayloads)
374
+ ]));
375
+ const ownsCompleteSnapshot = !isIncompleteResponse && !partialResponse;
376
+ const flagOwnership = ownsCompleteSnapshot ? true : ownedFlagKeys;
377
+ const payloadOwnership = ownsCompleteSnapshot ? true : ownedPayloadKeys;
378
+ const changes = {
379
+ [external_constants_js_namespaceObject.PERSISTENCE_ACTIVE_FEATURE_FLAGS]: flagOwnership,
380
+ [external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS]: flagOwnership,
381
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS]: payloadOwnership
382
+ };
383
+ if (statePatch[external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_DETAILS]) changes[external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_DETAILS] = flagOwnership;
384
+ for (const key of [
385
+ external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_REQUEST_ID,
386
+ external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT,
387
+ external_constants_js_namespaceObject.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS
388
+ ])if (!(0, core_namespaceObject.isUndefined)(statePatch[key])) changes[key] = true;
389
+ this._crossTabPersistence.markCrossTabFeatureFlagChanges(changes);
390
+ }
391
+ _remove(keys) {
392
+ this._persist(()=>this._client?.kv.remove(keys));
393
+ }
394
+ _persist(operation) {
395
+ try {
396
+ operation();
397
+ } catch (error) {
398
+ this._logger.error('Failed to update feature flag persistence', error);
399
+ }
400
+ }
401
+ _rebuildEventProperties() {
402
+ const common = {};
403
+ for (const key of [
404
+ external_constants_js_namespaceObject.PERSISTENCE_ACTIVE_FEATURE_FLAGS,
405
+ external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS,
406
+ external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_REQUEST_ID,
407
+ external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS
408
+ ]){
409
+ const value = this._prop(key);
410
+ if (!(0, core_namespaceObject.isUndefined)(value)) common[key] = value;
411
+ }
412
+ this._baseEventProperties = common;
413
+ const withFlagValues = {
414
+ ...common
415
+ };
416
+ const flags = this._prop(external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS);
417
+ if (flags) for (const [key, value] of Object.entries(flags))withFlagValues[`$feature/${key}`] = value;
418
+ this._eventPropertiesWithFlagValues = withFlagValues;
419
+ }
420
+ _isCacheStale() {
421
+ const ttl = this._config.cacheTtlMs;
422
+ if (!ttl || ttl <= 0) return false;
423
+ const evaluatedAt = this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT);
424
+ return 'number' != typeof evaluatedAt || Date.now() - evaluatedAt > ttl;
425
+ }
426
+ _checkAndTriggerStaleRefresh() {
427
+ if (!this._isCacheStale()) return false;
428
+ if (!this._staleCacheRefreshTriggered && !this._requestInFlight) {
429
+ this._staleCacheRefreshTriggered = true;
430
+ this._logger.warn('Feature flag cache is stale, triggering refresh...');
431
+ this.reloadFeatureFlags();
432
+ }
433
+ return true;
434
+ }
435
+ _getValidEvaluationEnvironments() {
436
+ const envs = this._config.evaluationContexts;
437
+ if (!envs?.length) return [];
438
+ return envs.filter((env)=>{
439
+ const isValid = env && 'string' == typeof env && env.trim().length > 0;
440
+ if (!isValid) this._logger.error('Invalid evaluation context found:', env, 'Expected non-empty string');
441
+ return isValid;
442
+ });
443
+ }
444
+ _getValidFlagKeys() {
445
+ const flagKeys = this._config.flagKeys;
446
+ if ((0, core_namespaceObject.isUndefined)(flagKeys)) return;
447
+ return flagKeys.filter((flagKey)=>{
448
+ const isValid = flagKey && 'string' == typeof flagKey && flagKey.trim().length > 0;
449
+ if (!isValid) this._logger.error('Invalid flag key found:', flagKey, 'Expected non-empty string');
450
+ return isValid;
451
+ });
452
+ }
453
+ initialize() {
454
+ const config = this._config;
455
+ const bootstrapFlags = config.bootstrap?.featureFlags ?? {};
456
+ const hasBootstrappedFlags = Object.keys(bootstrapFlags).length;
457
+ if (hasBootstrappedFlags) {
458
+ const bootstrapPayloads = config.bootstrap?.featureFlagPayloads ?? {};
459
+ const featureFlags = Object.keys(bootstrapFlags).filter((flag)=>!(0, core_namespaceObject.isUndefined)(bootstrapFlags[flag])).reduce((res, key)=>{
460
+ res[key] = bootstrapFlags[key];
461
+ return res;
462
+ }, {});
463
+ const featureFlagPayloads = Object.keys(bootstrapPayloads).filter((key)=>featureFlags[key]).reduce((res, key)=>{
464
+ res[key] = bootstrapPayloads[key];
465
+ return res;
466
+ }, {});
467
+ return this._receivedFeatureFlags({
468
+ featureFlags,
469
+ featureFlagPayloads
470
+ }, void 0, {
471
+ persist: false
472
+ });
473
+ }
474
+ }
475
+ updateFlags(flags, payloads, options) {
476
+ const existingFlags = options?.merge ? this._prop(external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS) ?? {} : {};
477
+ const existingPayloads = options?.merge ? this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS) ?? {} : {};
478
+ const finalFlags = {
479
+ ...existingFlags,
480
+ ...flags
481
+ };
482
+ const finalPayloads = {
483
+ ...existingPayloads,
484
+ ...payloads
485
+ };
486
+ const flagDetails = {};
487
+ for (const [key, value] of Object.entries(finalFlags))flagDetails[key] = {
488
+ key,
489
+ enabled: (0, core_namespaceObject.getEnabledFromValue)(value),
490
+ variant: (0, core_namespaceObject.getVariantFromValue)(value),
491
+ reason: void 0,
492
+ metadata: (0, core_namespaceObject.isUndefined)(finalPayloads?.[key]) ? void 0 : {
493
+ id: 0,
494
+ version: void 0,
495
+ description: void 0,
496
+ payload: finalPayloads[key]
497
+ }
498
+ };
499
+ this._receivedFeatureFlags({
500
+ flags: flagDetails
501
+ });
502
+ }
503
+ get hasLoadedFlags() {
504
+ return this._hasLoadedFlags;
505
+ }
506
+ getFlags() {
507
+ return Object.keys(this.getFlagVariants());
508
+ }
509
+ getFlagsWithDetails() {
510
+ const flagDetails = this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_DETAILS);
511
+ const overridenFlags = this._prop(external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS);
512
+ const overriddenPayloads = this._prop(external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS);
513
+ if (!overriddenPayloads && !overridenFlags) return flagDetails || {};
514
+ const finalDetails = (0, general_utils_js_namespaceObject.extend)({}, flagDetails || {});
515
+ const overriddenKeys = [
516
+ ...new Set([
517
+ ...Object.keys(overriddenPayloads || {}),
518
+ ...Object.keys(overridenFlags || {})
519
+ ])
520
+ ];
521
+ for (const key of overriddenKeys){
522
+ const originalDetail = finalDetails[key];
523
+ const overrideFlagValue = overridenFlags?.[key];
524
+ const finalEnabled = (0, core_namespaceObject.isUndefined)(overrideFlagValue) ? originalDetail?.enabled ?? false : !!overrideFlagValue;
525
+ const overrideVariant = (0, core_namespaceObject.isUndefined)(overrideFlagValue) ? originalDetail?.variant : 'string' == typeof overrideFlagValue ? overrideFlagValue : void 0;
526
+ const overridePayload = overriddenPayloads?.[key];
527
+ const overridenDetail = {
528
+ ...originalDetail,
529
+ enabled: finalEnabled,
530
+ variant: finalEnabled ? overrideVariant ?? originalDetail?.variant : void 0
531
+ };
532
+ if (finalEnabled !== originalDetail?.enabled) overridenDetail.original_enabled = originalDetail?.enabled;
533
+ if (overrideVariant !== originalDetail?.variant) overridenDetail.original_variant = originalDetail?.variant;
534
+ if (overridePayload) overridenDetail.metadata = {
535
+ ...originalDetail?.metadata,
536
+ payload: overridePayload,
537
+ original_payload: originalDetail?.metadata?.payload
538
+ };
539
+ finalDetails[key] = overridenDetail;
540
+ }
541
+ if (!this._override_warning) {
542
+ this._logger.warn(' Overriding feature flag details!', {
543
+ flagDetails,
544
+ overriddenPayloads,
545
+ finalDetails
546
+ });
547
+ this._override_warning = true;
548
+ }
549
+ return finalDetails;
550
+ }
551
+ getAllFeatureFlags() {
552
+ const flagVariants = this.getFlagVariants();
553
+ const payloads = this.getFlagPayloads();
554
+ return Object.keys(flagVariants).map((key)=>{
555
+ const flagValue = flagVariants[key];
556
+ return {
557
+ key,
558
+ enabled: (0, core_namespaceObject.getEnabledFromValue)(flagValue),
559
+ variant: (0, core_namespaceObject.getVariantFromValue)(flagValue),
560
+ payload: (0, core_namespaceObject.parsePayload)(payloads[key])
561
+ };
562
+ });
563
+ }
564
+ getFlagVariants() {
565
+ const enabledFlags = this._prop(external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS);
566
+ const overriddenFlags = this._prop(external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS);
567
+ if (!overriddenFlags) return enabledFlags || {};
568
+ const finalFlags = (0, general_utils_js_namespaceObject.extend)({}, enabledFlags || {});
569
+ const overriddenKeys = Object.keys(overriddenFlags);
570
+ for(let i = 0; i < overriddenKeys.length; i++)finalFlags[overriddenKeys[i]] = overriddenFlags[overriddenKeys[i]];
571
+ if (!this._override_warning) {
572
+ this._logger.warn(' Overriding feature flags!', {
573
+ enabledFlags,
574
+ overriddenFlags,
575
+ finalFlags
576
+ });
577
+ this._override_warning = true;
578
+ }
579
+ return finalFlags;
580
+ }
581
+ getFlagPayloads() {
582
+ const flagPayloads = this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_PAYLOADS);
583
+ const overriddenPayloads = this._prop(external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS);
584
+ if (!overriddenPayloads) return flagPayloads || {};
585
+ const finalPayloads = (0, general_utils_js_namespaceObject.extend)({}, flagPayloads || {});
586
+ const overriddenKeys = Object.keys(overriddenPayloads);
587
+ for(let i = 0; i < overriddenKeys.length; i++)finalPayloads[overriddenKeys[i]] = overriddenPayloads[overriddenKeys[i]];
588
+ if (!this._override_warning) {
589
+ this._logger.warn(' Overriding feature flag payloads!', {
590
+ flagPayloads,
591
+ overriddenPayloads,
592
+ finalPayloads
593
+ });
594
+ this._override_warning = true;
595
+ }
596
+ return finalPayloads;
597
+ }
598
+ reloadFeatureFlags() {
599
+ if (this._reloadingDisabled || this._config.featureFlagsDisabled) return;
600
+ if (this._hasStatusZeroCircuitBreakerTripped()) return;
601
+ if (this._requestInFlight) this._additionalReloadRequested = true;
602
+ if (this._reloadDebouncer) return;
603
+ this._reloadingHandlers.slice().forEach((handler)=>{
604
+ try {
605
+ handler();
606
+ } catch (error) {
607
+ this._logger.error('Error while running feature flags reloading callback', error);
608
+ }
609
+ });
610
+ this._reloadDebouncer = setTimeout(()=>{
611
+ this._callFlagsEndpoint();
612
+ }, 5);
613
+ }
614
+ reloadFeatureFlagsAsync() {
615
+ if (!this._client || this._reloadingDisabled || this._config.featureFlagsDisabled || this._config.remoteRequestsDisabled || this._hasStatusZeroCircuitBreakerTripped()) return Promise.resolve({
616
+ status: 'skipped'
617
+ });
618
+ return new Promise((resolve)=>{
619
+ this._pendingReloads.add(resolve);
620
+ this.reloadFeatureFlags();
621
+ });
622
+ }
623
+ _settleReloads(completions, status) {
624
+ completions.forEach((complete)=>complete({
625
+ status
626
+ }));
627
+ completions.clear();
628
+ }
629
+ _clearDebouncer() {
630
+ clearTimeout(this._reloadDebouncer);
631
+ this._reloadDebouncer = void 0;
632
+ }
633
+ onReloading(handler) {
634
+ this._reloadingHandlers.push(handler);
635
+ return ()=>{
636
+ this._reloadingHandlers = this._reloadingHandlers.filter((entry)=>entry !== handler);
637
+ };
638
+ }
639
+ ensureFlagsLoaded() {
640
+ if (this._hasLoadedFlags || this._requestInFlight || this._reloadDebouncer) return;
641
+ this.reloadFeatureFlags();
642
+ }
643
+ setAnonymousDistinctId(anon_distinct_id) {
644
+ this.$anon_distinct_id = anon_distinct_id;
645
+ }
646
+ setReloadingPaused(isPaused) {
647
+ this._reloadingDisabled = isPaused;
648
+ }
649
+ resetFlagCallReported() {
650
+ this._remove(external_constants_js_namespaceObject.FLAG_CALL_REPORTED);
651
+ }
652
+ _callFlagsEndpoint(options) {
653
+ this._clearDebouncer();
654
+ const client = this._client;
655
+ if (!client || this._config.remoteRequestsDisabled || this._hasStatusZeroCircuitBreakerTripped()) return void this._settleReloads(this._pendingReloads, 'skipped');
656
+ if (this._requestInFlight) {
657
+ this._additionalReloadRequested = true;
658
+ return;
659
+ }
660
+ const data = {
661
+ token: client.projectToken,
662
+ distinct_id: client.distinctId,
663
+ groups: client.groups,
664
+ $anon_distinct_id: this.$anon_distinct_id,
665
+ person_properties: {
666
+ ...client.initialPersonProperties,
667
+ ...this._prop(external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY) || {},
668
+ $lib: client.library.name,
669
+ $lib_version: client.library.version
670
+ },
671
+ group_properties: this._prop(external_constants_js_namespaceObject.STORED_GROUP_PROPERTIES_KEY),
672
+ timezone: (0, timezone_js_namespaceObject.getTimezone)()
673
+ };
674
+ if (!(0, core_namespaceObject.isUndefined)(client.deviceId)) data.$device_id = client.deviceId;
675
+ if (options?.disableFlags || this._config.featureFlagsDisabled) data.disable_flags = true;
676
+ const evaluationContexts = this._getValidEvaluationEnvironments();
677
+ if (evaluationContexts.length) data.evaluation_contexts = evaluationContexts;
678
+ const flagKeys = this._getValidFlagKeys();
679
+ if (!(0, core_namespaceObject.isUndefined)(flagKeys)) data.flag_keys = flagKeys;
680
+ const isPartialFlagsResponse = this._config.onlyEvaluateSurveyFeatureFlags;
681
+ const path = `/flags/?v=2${isPartialFlagsResponse ? '&only_evaluate_survey_feature_flags=true' : ''}`;
682
+ const requestGeneration = this._requestGeneration;
683
+ const completions = this._pendingReloads;
684
+ this._pendingReloads = new Set();
685
+ this._activeReloads = completions;
686
+ this._requestInFlight = true;
687
+ const requestAdditionalReload = ()=>{
688
+ if (this._additionalReloadRequested) {
689
+ this._additionalReloadRequested = false;
690
+ this._callFlagsEndpoint();
691
+ }
692
+ };
693
+ const handleResponse = (response)=>{
694
+ const json = response.json ?? {};
695
+ const errorsLoading = 200 !== response.statusCode;
696
+ this._requestInFlight = false;
697
+ if (requestGeneration !== this._requestGeneration) return void requestAdditionalReload();
698
+ this._trackStatusZeroReachability(response.statusCode);
699
+ if (!errorsLoading && !this._additionalReloadRequested) this.$anon_distinct_id = void 0;
700
+ if (data.disable_flags && !this._additionalReloadRequested) return void this._settleReloads(completions, 'skipped');
701
+ this._flagsLoadedFromRemote = !errorsLoading;
702
+ const flagErrors = [];
703
+ if (response.error) flagErrors.push(response.error instanceof Error && 'AbortError' === response.error.name ? FeatureFlagError.TIMEOUT : response.error instanceof Error ? FeatureFlagError.CONNECTION_ERROR : FeatureFlagError.UNKNOWN_ERROR);
704
+ else if (200 !== response.statusCode) flagErrors.push(FeatureFlagError.apiError(response.statusCode));
705
+ if (json.errorsWhileComputingFlags) flagErrors.push(FeatureFlagError.ERRORS_WHILE_COMPUTING);
706
+ const isQuotaLimited = !!json.quotaLimited?.includes(QuotaLimitedResource.FeatureFlags);
707
+ if (isQuotaLimited) flagErrors.push(FeatureFlagError.QUOTA_LIMITED);
708
+ this._set({
709
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_ERRORS]: flagErrors
710
+ });
711
+ 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.');
712
+ else if (!data.disable_flags) this._receivedFeatureFlags(json, errorsLoading, {
713
+ partialResponse: isPartialFlagsResponse
714
+ });
715
+ this._settleReloads(completions, data.disable_flags ? 'skipped' : !flagErrors.length && (json.flags || json.featureFlags) ? 'loaded' : 'error');
716
+ requestAdditionalReload();
717
+ };
718
+ const handleError = (error)=>{
719
+ this._requestInFlight = false;
720
+ if (requestGeneration !== this._requestGeneration) return void requestAdditionalReload();
721
+ this._set({
722
+ [external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_ERRORS]: [
723
+ FeatureFlagError.CONNECTION_ERROR
724
+ ]
725
+ });
726
+ this._logger.error('Feature flag request failed', error);
727
+ if (this._fallBackToPersistedFlags()) this._fireFeatureFlagsCallbacks(true);
728
+ this._settleReloads(completions, 'error');
729
+ requestAdditionalReload();
730
+ };
731
+ let attemptsLeft = this._config.requestMaxRetries ?? 1;
732
+ const shouldRetry = ()=>attemptsLeft > 0 && requestGeneration === this._requestGeneration;
733
+ const attempt = ()=>{
734
+ if (requestGeneration !== this._requestGeneration || !this._client || this._config.remoteRequestsDisabled) {
735
+ this._requestInFlight = false;
736
+ this._settleReloads(completions, 'skipped');
737
+ requestAdditionalReload();
738
+ return;
739
+ }
740
+ const retryLater = ()=>{
741
+ attemptsLeft--;
742
+ setTimeout(attempt, FLAGS_RETRY_DELAY_MS);
743
+ };
744
+ try {
745
+ client.sendRequest(path, {
746
+ target: 'flags',
747
+ method: 'POST',
748
+ body: data,
749
+ compression: this._config.compression,
750
+ sentAt: 'body',
751
+ timeoutMs: this._config.requestTimeoutMs
752
+ }).then((response)=>{
753
+ if (isRetryableFlagsResponse(response) && shouldRetry()) return void retryLater();
754
+ handleResponse(response);
755
+ }).catch(handleError);
756
+ } catch (error) {
757
+ handleError(error);
758
+ }
759
+ };
760
+ attempt();
761
+ }
762
+ _hasStatusZeroCircuitBreakerTripped() {
763
+ return (0, request_reachability_js_namespaceObject.isStatusZeroFailureCircuitBreakerTripped)(this._consecutiveStatusZeroFailures, MAX_CONSECUTIVE_FLAGS_STATUS_ZERO_FAILURES);
764
+ }
765
+ _trackStatusZeroReachability(statusCode) {
766
+ this._consecutiveStatusZeroFailures = (0, request_reachability_js_namespaceObject.updateStatusZeroFailureCount)(statusCode, this._consecutiveStatusZeroFailures, MAX_CONSECUTIVE_FLAGS_STATUS_ZERO_FAILURES, ()=>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.'));
767
+ }
768
+ getFeatureFlag(key, options = {}) {
769
+ if (options.fresh && !this._flagsLoadedFromRemote) return;
770
+ if (!this._hasLoadedFlags && !(this.getFlags() && this.getFlags().length > 0)) return void this._logger.warn('getFeatureFlag for key "' + key + FLAG_TIMEOUT_MSG);
771
+ if (this._checkAndTriggerStaleRefresh()) return;
772
+ const result = this.getFeatureFlagResult(key, options);
773
+ return result?.variant ?? result?.enabled;
774
+ }
775
+ getFeatureFlagDetails(key) {
776
+ const details = this.getFlagsWithDetails();
777
+ return details[key];
778
+ }
779
+ getFeatureFlagPayload(key) {
780
+ const result = this.getFeatureFlagResult(key, {
781
+ send_event: false
782
+ });
783
+ return result?.payload;
784
+ }
785
+ getFeatureFlagResult(key, options = {}) {
786
+ if (options.fresh && !this._flagsLoadedFromRemote) return;
787
+ if (!this._hasLoadedFlags && !(this.getFlags() && this.getFlags().length > 0)) return void this._logger.warn('getFeatureFlagResult for key "' + key + FLAG_TIMEOUT_MSG);
788
+ if (this._checkAndTriggerStaleRefresh()) return;
789
+ const flagVariants = this.getFlagVariants();
790
+ const flagExists = key in flagVariants;
791
+ const flagValue = flagVariants[key];
792
+ const payloads = this.getFlagPayloads();
793
+ const payload = payloads[key];
794
+ const flagReportValue = String(flagValue);
795
+ const requestId = this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_REQUEST_ID) || void 0;
796
+ const evaluatedAt = this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT) || void 0;
797
+ let flagCallReported = this._prop(external_constants_js_namespaceObject.FLAG_CALL_REPORTED) || {};
798
+ let sessionIdToPersist;
799
+ if (this._config.deduplicateCallsPerSession) {
800
+ const currentSessionId = this._client?.session.sessionId;
801
+ const storedSessionId = this._prop(external_constants_js_namespaceObject.FLAG_CALL_REPORTED_SESSION_ID);
802
+ if (currentSessionId && currentSessionId !== storedSessionId) {
803
+ flagCallReported = {};
804
+ sessionIdToPersist = currentSessionId;
805
+ }
806
+ }
807
+ if (!options.send_event && 'send_event' in options) {
808
+ if (sessionIdToPersist) this._set({
809
+ [external_constants_js_namespaceObject.FLAG_CALL_REPORTED]: flagCallReported,
810
+ [external_constants_js_namespaceObject.FLAG_CALL_REPORTED_SESSION_ID]: sessionIdToPersist
811
+ });
812
+ } else if (key in flagCallReported && flagCallReported[key].includes(flagReportValue)) {
813
+ if (sessionIdToPersist) this._set({
814
+ [external_constants_js_namespaceObject.FLAG_CALL_REPORTED]: flagCallReported,
815
+ [external_constants_js_namespaceObject.FLAG_CALL_REPORTED_SESSION_ID]: sessionIdToPersist
816
+ });
817
+ } else {
818
+ if ((0, core_namespaceObject.isArray)(flagCallReported[key])) flagCallReported[key].push(flagReportValue);
819
+ else flagCallReported[key] = [
820
+ flagReportValue
821
+ ];
822
+ this._set({
823
+ [external_constants_js_namespaceObject.FLAG_CALL_REPORTED]: flagCallReported,
824
+ ...sessionIdToPersist ? {
825
+ [external_constants_js_namespaceObject.FLAG_CALL_REPORTED_SESSION_ID]: sessionIdToPersist
826
+ } : {}
827
+ });
828
+ const flagDetails = this.getFeatureFlagDetails(key);
829
+ const errors = [
830
+ ...this._prop(external_constants_js_namespaceObject.PERSISTENCE_FEATURE_FLAG_ERRORS) ?? []
831
+ ];
832
+ if ((0, core_namespaceObject.isUndefined)(flagValue)) errors.push(FeatureFlagError.FLAG_MISSING);
833
+ const properties = {
834
+ $feature_flag: key,
835
+ $feature_flag_response: flagValue,
836
+ $feature_flag_payload: payload ?? null,
837
+ $feature_flag_request_id: requestId,
838
+ $feature_flag_evaluated_at: evaluatedAt,
839
+ $feature_flag_bootstrapped_response: this._config.bootstrap?.featureFlags?.[key] ?? null,
840
+ $feature_flag_bootstrapped_payload: this._config.bootstrap?.featureFlagPayloads?.[key] ?? null,
841
+ $used_bootstrap_value: !this._flagsLoadedFromRemote
842
+ };
843
+ if (!(0, core_namespaceObject.isUndefined)(flagDetails?.metadata?.has_experiment)) properties.$feature_flag_has_experiment = flagDetails.metadata.has_experiment;
844
+ if (!(0, core_namespaceObject.isUndefined)(flagDetails?.metadata?.version)) properties.$feature_flag_version = flagDetails.metadata.version;
845
+ const reason = flagDetails?.reason?.description ?? flagDetails?.reason?.code;
846
+ if (reason) properties.$feature_flag_reason = reason;
847
+ if (flagDetails?.metadata?.id) properties.$feature_flag_id = flagDetails.metadata.id;
848
+ if (!(0, core_namespaceObject.isUndefined)(flagDetails?.original_variant) || !(0, core_namespaceObject.isUndefined)(flagDetails?.original_enabled)) properties.$feature_flag_original_response = (0, core_namespaceObject.isUndefined)(flagDetails.original_variant) ? flagDetails.original_enabled : flagDetails.original_variant;
849
+ if (flagDetails?.metadata?.original_payload) properties.$feature_flag_original_payload = flagDetails?.metadata?.original_payload;
850
+ if (errors.length) properties.$feature_flag_error = errors.join(',');
851
+ this._captureFeatureFlagCalled(properties);
852
+ }
853
+ if (!flagExists) return;
854
+ return {
855
+ key,
856
+ enabled: !!flagValue,
857
+ variant: 'string' == typeof flagValue ? flagValue : void 0,
858
+ payload: (0, core_namespaceObject.parsePayload)(payload)
859
+ };
860
+ }
861
+ _captureFeatureFlagCalled(properties) {
862
+ try {
863
+ this._client?.capture('$feature_flag_called', properties);
864
+ } catch (error) {
865
+ this._logger.error('Failed to capture feature flag call', error);
866
+ }
867
+ }
868
+ getRemoteConfigPayload(key, callback) {
869
+ this._getRemoteConfigPayload(key, callback);
870
+ }
871
+ async _getRemoteConfigPayload(key, callback) {
872
+ const client = this._client;
873
+ if (!client) return;
874
+ const data = {
875
+ distinct_id: client.distinctId,
876
+ token: client.projectToken,
877
+ person_properties: {
878
+ $lib: client.library.name,
879
+ $lib_version: client.library.version
880
+ }
881
+ };
882
+ const evaluationContexts = this._getValidEvaluationEnvironments();
883
+ if (evaluationContexts.length) data.evaluation_contexts = evaluationContexts;
884
+ const flagKeys = this._getValidFlagKeys();
885
+ if (!(0, core_namespaceObject.isUndefined)(flagKeys)) data.flag_keys = flagKeys;
886
+ let payload;
887
+ try {
888
+ const response = await client.sendRequest('/flags/?v=2', {
889
+ target: 'flags',
890
+ method: 'POST',
891
+ body: data,
892
+ compression: this._config.compression,
893
+ sentAt: 'body',
894
+ timeoutMs: this._config.requestTimeoutMs
895
+ });
896
+ const payloads = response.json?.featureFlagPayloads;
897
+ payload = payloads?.[key] || void 0;
898
+ } catch (error) {
899
+ this._logger.error('Remote config feature flag request failed', error);
900
+ return;
901
+ }
902
+ try {
903
+ callback(payload);
904
+ } catch (error) {
905
+ this._logger.error('Remote config feature flag callback failed', error);
906
+ }
907
+ }
908
+ isFeatureEnabled(key, options = {}) {
909
+ if (options.fresh && !this._flagsLoadedFromRemote) return options.defaultValue;
910
+ if (!this._hasLoadedFlags && !(this.getFlags() && this.getFlags().length > 0)) {
911
+ this._logger.warn('isFeatureEnabled for key "' + key + FLAG_TIMEOUT_MSG);
912
+ return options.defaultValue;
913
+ }
914
+ const flagValue = this.getFeatureFlag(key, options);
915
+ return (0, core_namespaceObject.isUndefined)(flagValue) ? options.defaultValue : !!flagValue;
916
+ }
917
+ addFeatureFlagsHandler(handler) {
918
+ this.featureFlagEventHandlers.push(handler);
919
+ }
920
+ removeFeatureFlagsHandler(handler) {
921
+ this.featureFlagEventHandlers = this.featureFlagEventHandlers.filter((h)=>h !== handler);
922
+ }
923
+ receivedFeatureFlags(response, errorsLoading, options) {
924
+ this._receivedFeatureFlags(response, errorsLoading, options);
925
+ }
926
+ _receivedFeatureFlags(response, errorsLoading, options) {
927
+ if (!this._client) return;
928
+ this._hasLoadedFlags = true;
929
+ const currentFlags = this.getFlagVariants();
930
+ const currentFlagPayloads = this.getFlagPayloads();
931
+ const currentFlagDetails = this.getFlagsWithDetails();
932
+ const statePatch = parseFlagsResponse(response, currentFlags, currentFlagPayloads, currentFlagDetails, options, this._logger);
933
+ if (statePatch) if (options?.persist === false) {
934
+ const hasPersistedFlags = !(0, core_namespaceObject.isUndefined)(this._client.kv.get(external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS));
935
+ this._bootstrapState = statePatch;
936
+ if (!hasPersistedFlags) this._set(statePatch);
937
+ } else {
938
+ this._clearBootstrapState();
939
+ this._markCrossTabFeatureFlagSnapshot(statePatch, response, !!options?.partialResponse);
940
+ this._set(statePatch);
941
+ }
942
+ else if (errorsLoading) this._fallBackToPersistedFlags();
943
+ if (!errorsLoading) this._staleCacheRefreshTriggered = false;
944
+ this._fireFeatureFlagsCallbacks(errorsLoading);
945
+ }
946
+ override(flags, suppressWarning = false) {
947
+ this._logger.warn('override is deprecated. Please use overrideFeatureFlags instead.');
948
+ this.overrideFeatureFlags({
949
+ flags: flags,
950
+ suppressWarning: suppressWarning
951
+ });
952
+ }
953
+ overrideFeatureFlags(overrideOptions) {
954
+ this._overrideFeatureFlags(overrideOptions);
955
+ }
956
+ _overrideFeatureFlags(overrideOptions) {
957
+ if (!this._client) return void this._logger.warn('posthog.featureFlags.overrideFeatureFlags called before feature flags were ready');
958
+ if (false === overrideOptions) {
959
+ this._remove([
960
+ external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS,
961
+ external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS
962
+ ]);
963
+ this._fireFeatureFlagsCallbacks();
964
+ forceDebugLogger.info('All overrides cleared');
965
+ return;
966
+ }
967
+ if ((0, core_namespaceObject.isArray)(overrideOptions)) {
968
+ this._set({
969
+ [external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS]: arrayToFlagsRecord(overrideOptions)
970
+ });
971
+ this._fireFeatureFlagsCallbacks();
972
+ forceDebugLogger.info('Flag overrides set', {
973
+ flags: overrideOptions
974
+ });
975
+ return;
976
+ }
977
+ if (overrideOptions && 'object' == typeof overrideOptions && ('flags' in overrideOptions || 'payloads' in overrideOptions)) {
978
+ const options = overrideOptions;
979
+ this._override_warning = Boolean(options.suppressWarning ?? false);
980
+ const statePatch = {};
981
+ const flags = options.flags;
982
+ const payloads = options.payloads;
983
+ if (flags) statePatch[external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS] = (0, core_namespaceObject.isArray)(flags) ? arrayToFlagsRecord(flags) : flags;
984
+ if (payloads) statePatch[external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS] = payloads;
985
+ if (Object.keys(statePatch).length) this._set(statePatch);
986
+ if (false === flags && false === payloads) this._remove([
987
+ external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS,
988
+ external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS
989
+ ]);
990
+ else if (false === flags) this._remove(external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS);
991
+ else if (false === payloads) this._remove(external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAG_PAYLOADS);
992
+ this._fireFeatureFlagsCallbacks();
993
+ if (false === flags) forceDebugLogger.info('Flag overrides cleared');
994
+ else if (flags) forceDebugLogger.info('Flag overrides set', {
995
+ flags
996
+ });
997
+ if (false === payloads) forceDebugLogger.info('Payload overrides cleared');
998
+ else if (payloads) forceDebugLogger.info('Payload overrides set', {
999
+ payloads
1000
+ });
1001
+ return;
1002
+ }
1003
+ if (overrideOptions && 'object' == typeof overrideOptions) {
1004
+ this._set({
1005
+ [external_constants_js_namespaceObject.PERSISTENCE_OVERRIDE_FEATURE_FLAGS]: overrideOptions
1006
+ });
1007
+ this._fireFeatureFlagsCallbacks();
1008
+ forceDebugLogger.info('Flag overrides set', {
1009
+ flags: overrideOptions
1010
+ });
1011
+ return;
1012
+ }
1013
+ this._logger.warn('Invalid overrideOptions provided to overrideFeatureFlags', {
1014
+ overrideOptions
1015
+ });
1016
+ }
1017
+ onFeatureFlags(callback) {
1018
+ this.addFeatureFlagsHandler(callback);
1019
+ if (this._hasLoadedFlags) {
1020
+ const { flags, flagVariants } = this._prepareFeatureFlagsForCallbacks();
1021
+ try {
1022
+ callback(flags, flagVariants);
1023
+ } catch (error) {
1024
+ this._logger.error('Error while running feature flags callback', error);
1025
+ }
1026
+ }
1027
+ return ()=>this.removeFeatureFlagsHandler(callback);
1028
+ }
1029
+ updateEarlyAccessFeatureEnrollment(key, isEnrolled, stage) {
1030
+ const existing_early_access_features = this._prop(external_constants_js_namespaceObject.PERSISTENCE_EARLY_ACCESS_FEATURES) || [];
1031
+ const feature = existing_early_access_features.find((f)=>f.flagKey === key);
1032
+ const enrollmentPersonProp = {
1033
+ [`$feature_enrollment/${key}`]: isEnrolled
1034
+ };
1035
+ const properties = {
1036
+ $feature_flag: key,
1037
+ $feature_enrollment: isEnrolled,
1038
+ $set: enrollmentPersonProp
1039
+ };
1040
+ if (feature) properties['$early_access_feature_name'] = feature.name;
1041
+ if (stage) properties['$feature_enrollment_stage'] = stage;
1042
+ const newFlags = {
1043
+ ...this.getFlagVariants(),
1044
+ [key]: isEnrolled
1045
+ };
1046
+ this._crossTabPersistence?.markCrossTabFeatureFlagChanges({
1047
+ [external_constants_js_namespaceObject.PERSISTENCE_ACTIVE_FEATURE_FLAGS]: [
1048
+ key
1049
+ ],
1050
+ [external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS]: [
1051
+ key
1052
+ ],
1053
+ [external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY]: Object.keys(enrollmentPersonProp)
1054
+ });
1055
+ this._set({
1056
+ [external_constants_js_namespaceObject.PERSISTENCE_ACTIVE_FEATURE_FLAGS]: Object.keys(filterActiveFeatureFlags(newFlags)),
1057
+ [external_constants_js_namespaceObject.ENABLED_FEATURE_FLAGS]: newFlags,
1058
+ [external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY]: {
1059
+ ...this._prop(external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY) || {},
1060
+ ...enrollmentPersonProp
1061
+ }
1062
+ });
1063
+ this._fireFeatureFlagsCallbacks();
1064
+ try {
1065
+ this._client?.capture('$feature_enrollment_update', properties);
1066
+ } catch (error) {
1067
+ this._logger.error('Failed to capture early access feature enrollment', error);
1068
+ }
1069
+ }
1070
+ getEarlyAccessFeatures(callback, force_reload = false, stages) {
1071
+ const existing_early_access_features = this._prop(external_constants_js_namespaceObject.PERSISTENCE_EARLY_ACCESS_FEATURES);
1072
+ if (existing_early_access_features && !force_reload) return void callback(existing_early_access_features);
1073
+ this._getEarlyAccessFeatures(callback, stages);
1074
+ }
1075
+ async _getEarlyAccessFeatures(callback, stages) {
1076
+ const client = this._client;
1077
+ if (!client) return;
1078
+ const stageParams = stages ? `&${stages.map((s)=>`stage=${s}`).join('&')}` : '';
1079
+ let earlyAccessFeatures;
1080
+ try {
1081
+ const response = await client.sendRequest(`/api/early_access_features/?token=${client.projectToken}${stageParams}`, {
1082
+ target: 'api',
1083
+ method: 'GET',
1084
+ sentAt: 'query'
1085
+ });
1086
+ if (!response.json) return;
1087
+ earlyAccessFeatures = response.json.earlyAccessFeatures;
1088
+ this._set({
1089
+ [external_constants_js_namespaceObject.PERSISTENCE_EARLY_ACCESS_FEATURES]: earlyAccessFeatures
1090
+ });
1091
+ } catch (error) {
1092
+ this._logger.error('Early access feature request failed', error);
1093
+ return;
1094
+ }
1095
+ try {
1096
+ callback(earlyAccessFeatures);
1097
+ } catch (error) {
1098
+ this._logger.error('Early access feature callback failed', error);
1099
+ }
1100
+ }
1101
+ _prepareFeatureFlagsForCallbacks() {
1102
+ const flags = this.getFlags();
1103
+ const flagVariants = this.getFlagVariants();
1104
+ const truthyFlags = flags.filter((flag)=>flagVariants[flag]);
1105
+ const truthyFlagVariants = Object.keys(flagVariants).filter((variantKey)=>flagVariants[variantKey]).reduce((res, key)=>{
1106
+ res[key] = flagVariants[key];
1107
+ return res;
1108
+ }, {});
1109
+ return {
1110
+ flags: truthyFlags,
1111
+ flagVariants: truthyFlagVariants
1112
+ };
1113
+ }
1114
+ _fireFeatureFlagsCallbacks(errorsLoading) {
1115
+ this._rebuildEventProperties();
1116
+ const { flags, flagVariants } = this._prepareFeatureFlagsForCallbacks();
1117
+ this.featureFlagEventHandlers.forEach((handler)=>{
1118
+ try {
1119
+ handler(flags, flagVariants, {
1120
+ errorsLoading
1121
+ });
1122
+ } catch (error) {
1123
+ this._logger.error('Error while running feature flags callback', error);
1124
+ }
1125
+ });
1126
+ }
1127
+ setPersonPropertiesForFlags(properties, reloadFeatureFlags = true) {
1128
+ this._setPersonPropertiesForFlags(properties, reloadFeatureFlags);
1129
+ }
1130
+ _setPersonPropertiesForFlags(properties, reloadFeatureFlags = true) {
1131
+ const existingProperties = this._prop(external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY) || {};
1132
+ const propsToSet = properties?.['$set'] || (properties?.['$set_once'] ? {} : properties);
1133
+ const propsToSetOnce = properties?.['$set_once'];
1134
+ const setOnceProps = {};
1135
+ if (propsToSetOnce) {
1136
+ for(const key in propsToSetOnce)if (Object.prototype.hasOwnProperty.call(propsToSetOnce, key)) {
1137
+ if (!(key in existingProperties)) setOnceProps[key] = propsToSetOnce[key];
1138
+ }
1139
+ }
1140
+ this._set({
1141
+ [external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY]: {
1142
+ ...existingProperties,
1143
+ ...setOnceProps,
1144
+ ...propsToSet
1145
+ }
1146
+ });
1147
+ if (reloadFeatureFlags) this.reloadFeatureFlags();
1148
+ }
1149
+ unsetPersonPropertiesForFlags(propertyNames, reloadFeatureFlags = true) {
1150
+ const existingProperties = this._prop(external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY) || {};
1151
+ const nextProperties = {
1152
+ ...existingProperties
1153
+ };
1154
+ propertyNames.forEach((name)=>{
1155
+ delete nextProperties[name];
1156
+ });
1157
+ this._set({
1158
+ [external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY]: nextProperties
1159
+ });
1160
+ if (reloadFeatureFlags) this.reloadFeatureFlags();
1161
+ }
1162
+ resetPersonPropertiesForFlags(reloadFeatureFlags = true) {
1163
+ this._remove(external_constants_js_namespaceObject.STORED_PERSON_PROPERTIES_KEY);
1164
+ if (reloadFeatureFlags) this.reloadFeatureFlags();
1165
+ }
1166
+ setGroupPropertiesForFlags(properties, reloadFeatureFlags = true) {
1167
+ const existingProperties = this._prop(external_constants_js_namespaceObject.STORED_GROUP_PROPERTIES_KEY) || {};
1168
+ const nextProperties = {
1169
+ ...existingProperties
1170
+ };
1171
+ for (const groupType of Object.keys(properties))nextProperties[groupType] = {
1172
+ ...existingProperties[groupType],
1173
+ ...properties[groupType]
1174
+ };
1175
+ this._set({
1176
+ [external_constants_js_namespaceObject.STORED_GROUP_PROPERTIES_KEY]: nextProperties
1177
+ });
1178
+ if (reloadFeatureFlags) this.reloadFeatureFlags();
1179
+ }
1180
+ resetGroupPropertiesForFlags(group_type) {
1181
+ if (group_type) {
1182
+ const existingProperties = this._prop(external_constants_js_namespaceObject.STORED_GROUP_PROPERTIES_KEY) || {};
1183
+ this._set({
1184
+ [external_constants_js_namespaceObject.STORED_GROUP_PROPERTIES_KEY]: {
1185
+ ...existingProperties,
1186
+ [group_type]: {}
1187
+ }
1188
+ });
1189
+ } else this._remove(external_constants_js_namespaceObject.STORED_GROUP_PROPERTIES_KEY);
1190
+ }
1191
+ reset() {
1192
+ this._requestGeneration++;
1193
+ this._settleReloads(this._pendingReloads, 'cancelled');
1194
+ this._settleReloads(this._activeReloads, 'cancelled');
1195
+ this._additionalReloadRequested = false;
1196
+ this._baseEventProperties = {};
1197
+ this._eventPropertiesWithFlagValues = {};
1198
+ this._bootstrapState = void 0;
1199
+ this._hasLoadedFlags = false;
1200
+ this._reloadingDisabled = false;
1201
+ this._flagsLoadedFromRemote = false;
1202
+ this.$anon_distinct_id = void 0;
1203
+ this._clearDebouncer();
1204
+ this._override_warning = false;
1205
+ this._consecutiveStatusZeroFailures = 0;
1206
+ }
1207
+ }
1208
+ exports.FeatureFlagError = __webpack_exports__.FeatureFlagError;
1209
+ exports.PostHogFeatureFlags = __webpack_exports__.PostHogFeatureFlags;
1210
+ exports.QuotaLimitedResource = __webpack_exports__.QuotaLimitedResource;
1211
+ exports.filterActiveFeatureFlags = __webpack_exports__.filterActiveFeatureFlags;
1212
+ exports.parseFlagsResponse = __webpack_exports__.parseFlagsResponse;
1213
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
1214
+ "FeatureFlagError",
1215
+ "PostHogFeatureFlags",
1216
+ "QuotaLimitedResource",
1217
+ "filterActiveFeatureFlags",
1218
+ "parseFlagsResponse"
1219
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
1220
+ Object.defineProperty(exports, '__esModule', {
1221
+ value: true
1222
+ });