posthog-node 5.32.1 → 5.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { version } from "./version.mjs";
2
2
  import { PostHogCoreStateless, isBlockedUA, isPlainObject, safeSetTimeout, uuidv7 } from "@posthog/core";
3
3
  import { FeatureFlagError } from "./types.mjs";
4
+ import { FeatureFlagEvaluations } from "./feature-flag-evaluations.mjs";
4
5
  import { FeatureFlagsPoller, InconclusiveMatchError, RequiresServerEvaluation } from "./extensions/feature-flags/feature-flags.mjs";
5
6
  import error_tracking from "./extensions/error-tracking/index.mjs";
6
7
  import { PostHogMemoryStorage } from "./storage-memory.mjs";
@@ -10,6 +11,15 @@ const MAX_CACHE_SIZE = 50000;
10
11
  const WAITUNTIL_DEBOUNCE_MS = 50;
11
12
  const WAITUNTIL_MAX_WAIT_MS = 500;
12
13
  const DEFAULT_NODE_HOST = 'https://us.i.posthog.com';
14
+ const _emittedDeprecations = new Set();
15
+ function emitDeprecationWarningOnce(id, message) {
16
+ if (_emittedDeprecations.has(id)) return;
17
+ _emittedDeprecations.add(id);
18
+ console.warn(`[PostHog] ${message}`);
19
+ }
20
+ function _resetDeprecationWarningsForTests() {
21
+ _emittedDeprecations.clear();
22
+ }
13
23
  function normalizeApiKey(value) {
14
24
  return 'string' == typeof value ? value.trim() : '';
15
25
  }
@@ -21,6 +31,14 @@ function normalizeHost(value) {
21
31
  const normalizedValue = 'string' == typeof value ? value.trim() : '';
22
32
  return normalizedValue || DEFAULT_NODE_HOST;
23
33
  }
34
+ function buildFlagEventProperties(flagValues) {
35
+ if (!flagValues) return {};
36
+ const additionalProperties = {};
37
+ for (const [feature, variant] of Object.entries(flagValues))additionalProperties[`$feature/${feature}`] = variant;
38
+ const activeFlags = Object.keys(flagValues).filter((flag)=>false !== flagValues[flag]).sort();
39
+ if (activeFlags.length > 0) additionalProperties['$active_feature_flags'] = activeFlags;
40
+ return additionalProperties;
41
+ }
24
42
  class PostHogBackendClient extends PostHogCoreStateless {
25
43
  constructor(apiKey, options = {}){
26
44
  const normalizedApiKey = normalizeApiKey(apiKey);
@@ -317,37 +335,30 @@ class PostHogBackendClient extends PostHogCoreStateless {
317
335
  }
318
336
  if (sendFeatureFlagEvents) {
319
337
  const response = void 0 === result ? void 0 : false === result.enabled ? false : result.variant ?? true;
320
- const featureFlagReportedKey = `${key}_${response}`;
321
- if (!(distinctId in this.distinctIdHasSentFlagCalls) || !this.distinctIdHasSentFlagCalls[distinctId].includes(featureFlagReportedKey)) {
322
- if (Object.keys(this.distinctIdHasSentFlagCalls).length >= this.maxCacheSize) this.distinctIdHasSentFlagCalls = {};
323
- if (Array.isArray(this.distinctIdHasSentFlagCalls[distinctId])) this.distinctIdHasSentFlagCalls[distinctId].push(featureFlagReportedKey);
324
- else this.distinctIdHasSentFlagCalls[distinctId] = [
325
- featureFlagReportedKey
326
- ];
327
- const properties = {
328
- $feature_flag: key,
329
- $feature_flag_response: response,
330
- $feature_flag_id: flagId,
331
- $feature_flag_version: flagVersion,
332
- $feature_flag_reason: flagReason,
333
- locally_evaluated: flagWasLocallyEvaluated,
334
- [`$feature/${key}`]: response,
335
- $feature_flag_request_id: requestId,
336
- $feature_flag_evaluated_at: flagWasLocallyEvaluated ? Date.now() : evaluatedAt
337
- };
338
- if (flagWasLocallyEvaluated && this.featureFlagsPoller) {
339
- const flagDefinitionsLoadedAt = this.featureFlagsPoller.getFlagDefinitionsLoadedAt();
340
- if (void 0 !== flagDefinitionsLoadedAt) properties.$feature_flag_definitions_loaded_at = flagDefinitionsLoadedAt;
341
- }
342
- if (featureFlagError) properties.$feature_flag_error = featureFlagError;
343
- this.capture({
344
- distinctId,
345
- event: '$feature_flag_called',
346
- properties,
347
- groups,
348
- disableGeoip
349
- });
338
+ const properties = {
339
+ $feature_flag: key,
340
+ $feature_flag_response: response,
341
+ $feature_flag_id: flagId,
342
+ $feature_flag_version: flagVersion,
343
+ $feature_flag_reason: flagReason,
344
+ locally_evaluated: flagWasLocallyEvaluated,
345
+ [`$feature/${key}`]: response,
346
+ $feature_flag_request_id: requestId,
347
+ $feature_flag_evaluated_at: flagWasLocallyEvaluated ? Date.now() : evaluatedAt
348
+ };
349
+ if (flagWasLocallyEvaluated && this.featureFlagsPoller) {
350
+ const flagDefinitionsLoadedAt = this.featureFlagsPoller.getFlagDefinitionsLoadedAt();
351
+ if (void 0 !== flagDefinitionsLoadedAt) properties.$feature_flag_definitions_loaded_at = flagDefinitionsLoadedAt;
350
352
  }
353
+ if (featureFlagError) properties.$feature_flag_error = featureFlagError;
354
+ this._captureFlagCalledEventIfNeeded({
355
+ distinctId,
356
+ key,
357
+ response,
358
+ groups,
359
+ disableGeoip,
360
+ properties
361
+ });
351
362
  }
352
363
  if (void 0 !== result && void 0 !== this._payloadOverrides && key in this._payloadOverrides) result = {
353
364
  ...result,
@@ -356,6 +367,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
356
367
  return result;
357
368
  }
358
369
  async getFeatureFlag(key, distinctId, options) {
370
+ emitDeprecationWarningOnce('getFeatureFlag', "`getFeatureFlag` is deprecated and will be removed in a future major version. Use `posthog.evaluateFlags(distinctId, ...)` and call `flags.getFlag(key)` instead — this consolidates flag evaluation into a single `/flags` request per incoming request.");
359
371
  const result = await this._getFeatureFlagResult(key, distinctId, {
360
372
  ...options,
361
373
  sendFeatureFlagEvents: options?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true
@@ -365,6 +377,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
365
377
  return result.variant ?? true;
366
378
  }
367
379
  async getFeatureFlagPayload(key, distinctId, matchValue, options) {
380
+ emitDeprecationWarningOnce('getFeatureFlagPayload', "`getFeatureFlagPayload` is deprecated and will be removed in a future major version. Use `posthog.evaluateFlags(distinctId, ...)` and call `flags.getFlagPayload(key)` instead — this consolidates flag evaluation into a single `/flags` request per incoming request.");
368
381
  if (void 0 !== this._payloadOverrides && key in this._payloadOverrides) return this._payloadOverrides[key];
369
382
  const result = await this._getFeatureFlagResult(key, distinctId, {
370
383
  ...options,
@@ -392,8 +405,14 @@ class PostHogBackendClient extends PostHogCoreStateless {
392
405
  return parsed;
393
406
  }
394
407
  async isFeatureEnabled(key, distinctId, options) {
395
- const feat = await this.getFeatureFlag(key, distinctId, options);
396
- if (void 0 === feat) return;
408
+ emitDeprecationWarningOnce('isFeatureEnabled', "`isFeatureEnabled` is deprecated and will be removed in a future major version. Use `posthog.evaluateFlags(distinctId, ...)` and call `flags.isEnabled(key)` instead — this consolidates flag evaluation into a single `/flags` request per incoming request.");
409
+ const result = await this._getFeatureFlagResult(key, distinctId, {
410
+ ...options,
411
+ sendFeatureFlagEvents: options?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true
412
+ });
413
+ if (void 0 === result) return;
414
+ if (false === result.enabled) return false;
415
+ const feat = result.variant ?? true;
397
416
  return !!feat || false;
398
417
  }
399
418
  async getAllFlags(distinctIdOrOptions, options) {
@@ -454,6 +473,136 @@ class PostHogBackendClient extends PostHogCoreStateless {
454
473
  featureFlagPayloads
455
474
  };
456
475
  }
476
+ async evaluateFlags(distinctIdOrOptions, options) {
477
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(distinctIdOrOptions, options);
478
+ if (!resolvedDistinctId) {
479
+ this._logger.warn("[PostHog] distinctId is required to evaluate feature flags \u2014 pass it explicitly or use withContext()");
480
+ return new FeatureFlagEvaluations({
481
+ host: this._getFeatureFlagEvaluationsHost(),
482
+ distinctId: '',
483
+ flags: {}
484
+ });
485
+ }
486
+ const { groups, disableGeoip, flagKeys } = resolvedOptions || {};
487
+ let { onlyEvaluateLocally, personProperties, groupProperties } = resolvedOptions || {};
488
+ const adjustedProperties = this.addLocalPersonAndGroupProperties(resolvedDistinctId, groups, personProperties, groupProperties);
489
+ personProperties = adjustedProperties.allPersonProperties;
490
+ groupProperties = adjustedProperties.allGroupProperties;
491
+ const evaluationContext = this.createFeatureFlagEvaluationContext(resolvedDistinctId, groups, personProperties, groupProperties);
492
+ if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = this.options.strictLocalEvaluation ?? false;
493
+ const records = {};
494
+ let requestId;
495
+ let evaluatedAt;
496
+ let errorsWhileComputing = false;
497
+ let quotaLimited = false;
498
+ const localResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(evaluationContext, flagKeys);
499
+ const locallyEvaluatedKeys = new Set();
500
+ if (localResult) for (const [key, value] of Object.entries(localResult.response)){
501
+ const flagDef = this.featureFlagsPoller?.featureFlagsByKey[key];
502
+ records[key] = {
503
+ key,
504
+ enabled: false !== value,
505
+ variant: 'string' == typeof value ? value : void 0,
506
+ payload: localResult.payloads[key],
507
+ id: flagDef?.id,
508
+ version: void 0,
509
+ reason: 'Evaluated locally',
510
+ locallyEvaluated: true
511
+ };
512
+ locallyEvaluatedKeys.add(key);
513
+ }
514
+ const fallbackToFlags = localResult ? localResult.fallbackToFlags : true;
515
+ if (fallbackToFlags && !onlyEvaluateLocally) {
516
+ const details = await super.getFeatureFlagDetailsStateless(evaluationContext.distinctId, evaluationContext.groups, evaluationContext.personProperties, evaluationContext.groupProperties, disableGeoip, flagKeys);
517
+ if (details) {
518
+ requestId = details.requestId;
519
+ evaluatedAt = details.evaluatedAt;
520
+ errorsWhileComputing = Boolean(details.errorsWhileComputingFlags);
521
+ quotaLimited = Array.isArray(details.quotaLimited) && details.quotaLimited.includes('feature_flags');
522
+ for (const [key, detail] of Object.entries(details.flags)){
523
+ if (locallyEvaluatedKeys.has(key)) continue;
524
+ let parsedPayload;
525
+ if (detail.metadata?.payload !== void 0) try {
526
+ parsedPayload = JSON.parse(detail.metadata.payload);
527
+ } catch {
528
+ parsedPayload = detail.metadata.payload;
529
+ }
530
+ records[key] = {
531
+ key,
532
+ enabled: detail.enabled,
533
+ variant: detail.variant,
534
+ payload: parsedPayload,
535
+ id: detail.metadata?.id,
536
+ version: detail.metadata?.version,
537
+ reason: detail.reason?.description ?? detail.reason?.code,
538
+ locallyEvaluated: false
539
+ };
540
+ }
541
+ }
542
+ }
543
+ if (void 0 !== this._flagOverrides) for (const [key, value] of Object.entries(this._flagOverrides)){
544
+ if (void 0 === value) {
545
+ delete records[key];
546
+ continue;
547
+ }
548
+ const existing = records[key];
549
+ records[key] = {
550
+ key,
551
+ enabled: false !== value,
552
+ variant: 'string' == typeof value ? value : void 0,
553
+ payload: existing?.payload,
554
+ id: existing?.id,
555
+ version: existing?.version,
556
+ reason: existing?.reason,
557
+ locallyEvaluated: existing?.locallyEvaluated ?? false
558
+ };
559
+ }
560
+ if (void 0 !== this._payloadOverrides) for (const [key, payload] of Object.entries(this._payloadOverrides)){
561
+ const existing = records[key];
562
+ if (existing) records[key] = {
563
+ ...existing,
564
+ payload
565
+ };
566
+ }
567
+ return new FeatureFlagEvaluations({
568
+ host: this._getFeatureFlagEvaluationsHost(),
569
+ distinctId: resolvedDistinctId,
570
+ groups,
571
+ disableGeoip,
572
+ flags: records,
573
+ requestId,
574
+ evaluatedAt,
575
+ flagDefinitionsLoadedAt: this.featureFlagsPoller?.getFlagDefinitionsLoadedAt(),
576
+ errorsWhileComputing,
577
+ quotaLimited
578
+ });
579
+ }
580
+ _captureFlagCalledEventIfNeeded(params) {
581
+ const { distinctId, key, response, groups, disableGeoip, properties } = params;
582
+ const featureFlagReportedKey = `${key}_${response}`;
583
+ if (distinctId in this.distinctIdHasSentFlagCalls && this.distinctIdHasSentFlagCalls[distinctId].includes(featureFlagReportedKey)) return;
584
+ if (Object.keys(this.distinctIdHasSentFlagCalls).length >= this.maxCacheSize) this.distinctIdHasSentFlagCalls = {};
585
+ if (Array.isArray(this.distinctIdHasSentFlagCalls[distinctId])) this.distinctIdHasSentFlagCalls[distinctId].push(featureFlagReportedKey);
586
+ else this.distinctIdHasSentFlagCalls[distinctId] = [
587
+ featureFlagReportedKey
588
+ ];
589
+ this.capture({
590
+ distinctId,
591
+ event: '$feature_flag_called',
592
+ properties,
593
+ groups,
594
+ disableGeoip
595
+ });
596
+ }
597
+ _getFeatureFlagEvaluationsHost() {
598
+ if (!this._featureFlagEvaluationsHost) this._featureFlagEvaluationsHost = {
599
+ captureFlagCalledEventIfNeeded: (params)=>this._captureFlagCalledEventIfNeeded(params),
600
+ logWarning: (message)=>{
601
+ if (false !== this.options.featureFlagsLogWarnings) console.warn(`[PostHog] ${message}`);
602
+ }
603
+ };
604
+ return this._featureFlagEvaluationsHost;
605
+ }
457
606
  groupIdentify({ groupType, groupKey, properties, distinctId, disableGeoip }) {
458
607
  super.groupIdentifyStateless(groupType, groupKey, properties, {
459
608
  disableGeoip
@@ -630,27 +779,31 @@ class PostHogBackendClient extends PostHogCoreStateless {
630
779
  evaluationCache: {}
631
780
  };
632
781
  }
633
- captureException(error, distinctId, additionalProperties, uuid) {
782
+ captureException(error, distinctId, additionalProperties, uuid, flags) {
634
783
  if (!error_tracking.isPreviouslyCapturedError(error)) {
635
784
  const syntheticException = new Error('PostHog syntheticException');
636
785
  this.addPendingPromise(error_tracking.buildEventMessage(error, {
637
786
  syntheticException
638
787
  }, distinctId, additionalProperties).then((msg)=>this.capture({
639
788
  ...msg,
640
- uuid
789
+ uuid,
790
+ flags
641
791
  })));
642
792
  }
643
793
  }
644
- async captureExceptionImmediate(error, distinctId, additionalProperties) {
794
+ async captureExceptionImmediate(error, distinctId, additionalProperties, flags) {
645
795
  if (!error_tracking.isPreviouslyCapturedError(error)) {
646
796
  const syntheticException = new Error('PostHog syntheticException');
647
797
  return this.addPendingPromise(error_tracking.buildEventMessage(error, {
648
798
  syntheticException
649
- }, distinctId, additionalProperties).then((msg)=>this.captureImmediate(msg)));
799
+ }, distinctId, additionalProperties).then((msg)=>this.captureImmediate({
800
+ ...msg,
801
+ flags
802
+ })));
650
803
  }
651
804
  }
652
805
  async prepareEventMessage(props) {
653
- const { distinctId, event, properties, groups, sendFeatureFlags, timestamp, disableGeoip, uuid } = props;
806
+ const { distinctId, event, properties, groups, flags, sendFeatureFlags, timestamp, disableGeoip, uuid } = props;
654
807
  const contextData = this.context?.get();
655
808
  let mergedDistinctId = distinctId || contextData?.distinctId;
656
809
  const mergedProperties = {
@@ -668,6 +821,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
668
821
  event,
669
822
  properties: mergedProperties,
670
823
  groups,
824
+ flags,
671
825
  sendFeatureFlags,
672
826
  timestamp,
673
827
  disableGeoip,
@@ -675,18 +829,17 @@ class PostHogBackendClient extends PostHogCoreStateless {
675
829
  });
676
830
  if (!eventMessage) return Promise.reject(null);
677
831
  const eventProperties = await Promise.resolve().then(async ()=>{
832
+ if (flags) {
833
+ if (sendFeatureFlags) console.warn('[PostHog] Both `flags` and `sendFeatureFlags` were passed to capture(); using `flags` and ignoring `sendFeatureFlags`.');
834
+ return flags._getEventProperties();
835
+ }
678
836
  if (sendFeatureFlags) {
837
+ emitDeprecationWarningOnce('sendFeatureFlags', "`sendFeatureFlags` is deprecated and will be removed in a future major version. Pass a `flags` snapshot from `posthog.evaluateFlags(...)` instead — it avoids a second `/flags` request per capture and guarantees the event carries the exact flag values your code branched on.");
679
838
  const sendFeatureFlagsOptions = 'object' == typeof sendFeatureFlags ? sendFeatureFlags : void 0;
680
- return await this.getFeatureFlagsForEvent(eventMessage.distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
839
+ const flagValues = await this.getFeatureFlagsForEvent(eventMessage.distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
840
+ return buildFlagEventProperties(flagValues);
681
841
  }
682
- eventMessage.event;
683
842
  return {};
684
- }).then((flags)=>{
685
- const additionalProperties = {};
686
- if (flags) for (const [feature, variant] of Object.entries(flags))additionalProperties[`$feature/${feature}`] = variant;
687
- const activeFlags = Object.keys(flags || {}).filter((flag)=>flags?.[flag] !== false).sort();
688
- if (activeFlags.length > 0) additionalProperties['$active_feature_flags'] = activeFlags;
689
- return additionalProperties;
690
843
  }).catch(()=>({})).then((additionalProperties)=>{
691
844
  const props = {
692
845
  ...additionalProperties,
@@ -733,4 +886,4 @@ class PostHogBackendClient extends PostHogCoreStateless {
733
886
  return result;
734
887
  }
735
888
  }
736
- export { PostHogBackendClient };
889
+ export { PostHogBackendClient, _resetDeprecationWarningsForTests };
package/dist/exports.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './extensions/sentry-integration';
2
2
  export * from './extensions/express';
3
3
  export * from './types';
4
+ export { FeatureFlagEvaluations } from './feature-flag-evaluations';
4
5
  export { FeatureFlagError } from '@posthog/core';
5
6
  export type { FeatureFlagErrorType } from '@posthog/core';
6
7
  //# sourceMappingURL=exports.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAA;AAC/C,cAAc,sBAAsB,CAAA;AACpC,cAAc,SAAS,CAAA;AAIvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAA;AAC/C,cAAc,sBAAsB,CAAA;AACpC,cAAc,SAAS,CAAA;AAEvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAInE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA"}
package/dist/exports.js CHANGED
@@ -6,6 +6,9 @@ var __webpack_modules__ = {
6
6
  "./extensions/sentry-integration": function(module) {
7
7
  module.exports = require("./extensions/sentry-integration.js");
8
8
  },
9
+ "./feature-flag-evaluations": function(module) {
10
+ module.exports = require("./feature-flag-evaluations.js");
11
+ },
9
12
  "./types": function(module) {
10
13
  module.exports = require("./types.js");
11
14
  },
@@ -57,11 +60,13 @@ var __webpack_exports__ = {};
57
60
  (()=>{
58
61
  __webpack_require__.r(__webpack_exports__);
59
62
  __webpack_require__.d(__webpack_exports__, {
60
- FeatureFlagError: ()=>_posthog_core__WEBPACK_IMPORTED_MODULE_3__.FeatureFlagError
63
+ FeatureFlagError: ()=>_posthog_core__WEBPACK_IMPORTED_MODULE_4__.FeatureFlagError,
64
+ FeatureFlagEvaluations: ()=>_feature_flag_evaluations__WEBPACK_IMPORTED_MODULE_3__.FeatureFlagEvaluations
61
65
  });
62
66
  var _extensions_sentry_integration__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./extensions/sentry-integration");
63
67
  var __WEBPACK_REEXPORT_OBJECT__ = {};
64
68
  for(var __WEBPACK_IMPORT_KEY__ in _extensions_sentry_integration__WEBPACK_IMPORTED_MODULE_0__)if ([
69
+ "FeatureFlagEvaluations",
65
70
  "FeatureFlagError",
66
71
  "default"
67
72
  ].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
@@ -71,6 +76,7 @@ var __webpack_exports__ = {};
71
76
  var _extensions_express__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./extensions/express");
72
77
  var __WEBPACK_REEXPORT_OBJECT__ = {};
73
78
  for(var __WEBPACK_IMPORT_KEY__ in _extensions_express__WEBPACK_IMPORTED_MODULE_1__)if ([
79
+ "FeatureFlagEvaluations",
74
80
  "FeatureFlagError",
75
81
  "default"
76
82
  ].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
@@ -80,17 +86,21 @@ var __webpack_exports__ = {};
80
86
  var _types__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./types");
81
87
  var __WEBPACK_REEXPORT_OBJECT__ = {};
82
88
  for(var __WEBPACK_IMPORT_KEY__ in _types__WEBPACK_IMPORTED_MODULE_2__)if ([
89
+ "FeatureFlagEvaluations",
83
90
  "FeatureFlagError",
84
91
  "default"
85
92
  ].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
86
93
  return _types__WEBPACK_IMPORTED_MODULE_2__[key];
87
94
  }).bind(0, __WEBPACK_IMPORT_KEY__);
88
95
  __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
89
- var _posthog_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("@posthog/core");
96
+ var _feature_flag_evaluations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./feature-flag-evaluations");
97
+ var _posthog_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("@posthog/core");
90
98
  })();
91
99
  exports.FeatureFlagError = __webpack_exports__.FeatureFlagError;
100
+ exports.FeatureFlagEvaluations = __webpack_exports__.FeatureFlagEvaluations;
92
101
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
93
- "FeatureFlagError"
102
+ "FeatureFlagError",
103
+ "FeatureFlagEvaluations"
94
104
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
95
105
  Object.defineProperty(exports, '__esModule', {
96
106
  value: true
package/dist/exports.mjs CHANGED
@@ -1,5 +1,6 @@
1
+ import { FeatureFlagEvaluations } from "./feature-flag-evaluations.mjs";
1
2
  import { FeatureFlagError } from "@posthog/core";
2
3
  export * from "./extensions/sentry-integration.mjs";
3
4
  export * from "./extensions/express.mjs";
4
5
  export * from "./types.mjs";
5
- export { FeatureFlagError };
6
+ export { FeatureFlagError, FeatureFlagEvaluations };
@@ -0,0 +1,151 @@
1
+ import { FeatureFlagValue, JsonType } from '@posthog/core';
2
+ /**
3
+ * Internal per-flag record stored by a {@link FeatureFlagEvaluations} instance.
4
+ * Not part of the public API.
5
+ *
6
+ * @internal
7
+ */
8
+ export type EvaluatedFlagRecord = {
9
+ key: string;
10
+ enabled: boolean;
11
+ variant: string | undefined;
12
+ payload: JsonType | undefined;
13
+ id: number | undefined;
14
+ version: number | undefined;
15
+ reason: string | undefined;
16
+ locallyEvaluated: boolean;
17
+ };
18
+ /**
19
+ * Parameters passed to the host when a `$feature_flag_called` event should be captured.
20
+ *
21
+ * @internal
22
+ */
23
+ export type FlagCalledEventParams = {
24
+ distinctId: string;
25
+ key: string;
26
+ response: FeatureFlagValue | undefined;
27
+ groups: Record<string, string | number> | undefined;
28
+ disableGeoip: boolean | undefined;
29
+ properties: Record<string, any>;
30
+ };
31
+ /**
32
+ * Thin interface the evaluations object uses to talk back to the PostHog client.
33
+ * Keeps the class decoupled from the full client surface area.
34
+ *
35
+ * @internal
36
+ */
37
+ export interface FeatureFlagEvaluationsHost {
38
+ captureFlagCalledEventIfNeeded(params: FlagCalledEventParams): void;
39
+ logWarning(message: string): void;
40
+ }
41
+ /**
42
+ * A snapshot of feature flag evaluations for a single distinctId at a point in time.
43
+ *
44
+ * Returned by {@link IPostHog.evaluateFlags} — branch on `isEnabled()` / `getFlag()`
45
+ * and pass the same object to `capture()` via the `flags` option so the captured event
46
+ * carries the exact flag values the code branched on.
47
+ *
48
+ * ```ts
49
+ * const flags = await posthog.evaluateFlags(distinctId, { personProperties: { plan: 'enterprise' } })
50
+ *
51
+ * if (flags.isEnabled('new-dashboard')) {
52
+ * renderNewDashboard()
53
+ * }
54
+ *
55
+ * posthog.capture({ distinctId, event: 'page_viewed', flags })
56
+ * ```
57
+ *
58
+ * To narrow the set of flags that get attached to a captured event, use the in-memory
59
+ * helpers `only([...])` and `onlyAccessed()`. To narrow the set of flags requested from
60
+ * the server in the first place, pass `flagKeys` to `evaluateFlags()`.
61
+ */
62
+ export declare class FeatureFlagEvaluations {
63
+ private readonly _host;
64
+ private readonly _distinctId;
65
+ private readonly _groups;
66
+ private readonly _disableGeoip;
67
+ private readonly _flags;
68
+ private readonly _requestId;
69
+ private readonly _evaluatedAt;
70
+ private readonly _flagDefinitionsLoadedAt;
71
+ private readonly _errorsWhileComputing;
72
+ private readonly _quotaLimited;
73
+ private readonly _accessed;
74
+ private readonly _isSlice;
75
+ /**
76
+ * @internal — instances are created by the SDK via `posthog.evaluateFlags()`.
77
+ */
78
+ constructor(init: {
79
+ host: FeatureFlagEvaluationsHost;
80
+ distinctId: string;
81
+ groups?: Record<string, string | number>;
82
+ disableGeoip?: boolean;
83
+ flags: Record<string, EvaluatedFlagRecord>;
84
+ requestId?: string;
85
+ evaluatedAt?: number;
86
+ flagDefinitionsLoadedAt?: number;
87
+ errorsWhileComputing?: boolean;
88
+ quotaLimited?: boolean;
89
+ accessed?: Set<string>;
90
+ isSlice?: boolean;
91
+ });
92
+ /**
93
+ * Check whether a feature flag is enabled. Fires a `$feature_flag_called` event
94
+ * on the first access per (distinctId, flag, value) tuple, deduped via the SDK's
95
+ * existing cache.
96
+ *
97
+ * Flags that were not returned from the underlying evaluation are treated as
98
+ * disabled (returns `false`).
99
+ */
100
+ isEnabled(key: string): boolean;
101
+ /**
102
+ * Get the evaluated value of a feature flag. Fires a `$feature_flag_called` event
103
+ * on the first access per (distinctId, flag, value) tuple.
104
+ *
105
+ * Returns the variant string for multivariate flags, `true` for enabled flags
106
+ * without a variant, `false` for disabled flags, and `undefined` for flags that
107
+ * were not returned by the evaluation.
108
+ */
109
+ getFlag(key: string): FeatureFlagValue | undefined;
110
+ /**
111
+ * Get the payload associated with a feature flag. Does not count as an access
112
+ * for `onlyAccessed()` and does not fire any event.
113
+ */
114
+ getFlagPayload(key: string): JsonType | undefined;
115
+ /**
116
+ * Return a filtered copy containing only flags that have been accessed via
117
+ * `isEnabled()` or `getFlag()` before this call.
118
+ *
119
+ * Order-dependent: if nothing has been accessed yet, the returned snapshot is
120
+ * empty. The method honors its name — pre-access if you want a populated result.
121
+ *
122
+ * **Note:** the returned snapshot is intended for `capture()`, not for further
123
+ * branching. Calling `isEnabled()` / `getFlag()` on it for a key that was filtered
124
+ * out is a no-op (no event is fired) — the flag wasn't actually missing, it was
125
+ * excluded from the slice.
126
+ */
127
+ onlyAccessed(): FeatureFlagEvaluations;
128
+ /**
129
+ * Return a filtered copy containing only flags with the given keys. Keys that
130
+ * are not present in the evaluation are dropped and logged as a warning.
131
+ *
132
+ * **Note:** like `onlyAccessed()`, the returned snapshot is intended for `capture()`.
133
+ * Branching on a filtered key that was excluded from the slice is a no-op.
134
+ */
135
+ only(keys: string[]): FeatureFlagEvaluations;
136
+ /**
137
+ * Returns the flag keys that are part of this evaluation.
138
+ */
139
+ get keys(): string[];
140
+ /**
141
+ * Build the `$feature/*` and `$active_feature_flags` event properties derived
142
+ * from the current flag set. Called by `capture()` when an event is captured
143
+ * with `flags: ...`.
144
+ *
145
+ * @internal
146
+ */
147
+ _getEventProperties(): Record<string, any>;
148
+ private _cloneWith;
149
+ private _recordAccess;
150
+ }
151
+ //# sourceMappingURL=feature-flag-evaluations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-flag-evaluations.d.ts","sourceRoot":"","sources":["../src/feature-flag-evaluations.ts"],"names":[],"mappings":"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"}