posthog-node 4.12.0 → 4.13.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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 4.13.0 - 2025-04-21
2
+
3
+ 1. feat: Add method to wait for local evaluation feature flag definitions to be loaded
4
+
1
5
  # 4.12.0 – 2025-04-17
2
6
 
3
7
  1. chore: roll out new feature flag evaluation backend to majority of customers
package/lib/index.cjs.js CHANGED
@@ -22,7 +22,7 @@ function _interopNamespace(e) {
22
22
  return Object.freeze(n);
23
23
  }
24
24
 
25
- var version = "4.12.0";
25
+ var version = "4.13.0";
26
26
 
27
27
  var PostHogPersistedProperty;
28
28
  (function (PostHogPersistedProperty) {
@@ -1988,6 +1988,7 @@ class FeatureFlagsPoller {
1988
1988
  this.fetch = options.fetch || fetch$1;
1989
1989
  this.onError = options.onError;
1990
1990
  this.customHeaders = customHeaders;
1991
+ this.onLoad = options.onLoad;
1991
1992
  void this.loadFeatureFlags();
1992
1993
  }
1993
1994
  debug(enabled = true) {
@@ -2204,6 +2205,13 @@ class FeatureFlagsPoller {
2204
2205
  await this._loadFeatureFlags();
2205
2206
  }
2206
2207
  }
2208
+ /**
2209
+ * Returns true if the feature flags poller has loaded successfully at least once and has more than 0 feature flags.
2210
+ * This is useful to check if local evaluation is ready before calling getFeatureFlag.
2211
+ */
2212
+ isLocalEvaluationReady() {
2213
+ return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
2214
+ }
2207
2215
  /**
2208
2216
  * If a client is misconfigured with an invalid or improper API key, the polling interval is doubled each time
2209
2217
  * until a successful request is made, up to a maximum of 60 seconds.
@@ -2279,6 +2287,7 @@ class FeatureFlagsPoller {
2279
2287
  this.loadedSuccessfullyOnce = true;
2280
2288
  this.shouldBeginExponentialBackoff = false;
2281
2289
  this.backOffCount = 0;
2290
+ this.onLoad?.(this.featureFlags.length);
2282
2291
  break;
2283
2292
  }
2284
2293
  default:
@@ -3069,16 +3078,28 @@ async function propertiesFromUnknownInput(stackParser, input, hint) {
3069
3078
  handled: true,
3070
3079
  type: 'generic'
3071
3080
  };
3072
- const error = getError(mechanism, input, hint);
3073
- const exception = await exceptionFromError(stackParser, error);
3074
- exception.value = exception.value || '';
3075
- exception.type = exception.type || 'Error';
3076
- exception.mechanism = mechanism;
3081
+ const errorList = getErrorList(mechanism, input, hint);
3082
+ const exceptionList = await Promise.all(errorList.map(async error => {
3083
+ const exception = await exceptionFromError(stackParser, error);
3084
+ exception.value = exception.value || '';
3085
+ exception.type = exception.type || 'Error';
3086
+ exception.mechanism = mechanism;
3087
+ return exception;
3088
+ }));
3077
3089
  const properties = {
3078
- $exception_list: [exception]
3090
+ $exception_list: exceptionList
3079
3091
  };
3080
3092
  return properties;
3081
3093
  }
3094
+ // Flatten error causes into a list of errors
3095
+ // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
3096
+ function getErrorList(mechanism, input, hint) {
3097
+ const error = getError(mechanism, input, hint);
3098
+ if (error.cause) {
3099
+ return [error, ...getErrorList(mechanism, error.cause, hint)];
3100
+ }
3101
+ return [error];
3102
+ }
3082
3103
  function getError(mechanism, exception, hint) {
3083
3104
  if (isError(exception)) {
3084
3105
  return exception;
@@ -3105,7 +3126,7 @@ function getErrorPropertyFromObject(obj) {
3105
3126
  for (const prop in obj) {
3106
3127
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
3107
3128
  const value = obj[prop];
3108
- if (value instanceof Error) {
3129
+ if (isError(value)) {
3109
3130
  return value;
3110
3131
  }
3111
3132
  }
@@ -3532,6 +3553,9 @@ class PostHog extends PostHogCoreStateless {
3532
3553
  onError: err => {
3533
3554
  this._events.emit('error', err);
3534
3555
  },
3556
+ onLoad: count => {
3557
+ this._events.emit('localEvaluationFlagsLoaded', count);
3558
+ },
3535
3559
  customHeaders: this.getCustomHeaders()
3536
3560
  });
3537
3561
  }
@@ -3664,6 +3688,28 @@ class PostHog extends PostHogCoreStateless {
3664
3688
  disableGeoip: data.disableGeoip
3665
3689
  });
3666
3690
  }
3691
+ isLocalEvaluationReady() {
3692
+ return this.featureFlagsPoller?.isLocalEvaluationReady() ?? false;
3693
+ }
3694
+ async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
3695
+ if (this.isLocalEvaluationReady()) {
3696
+ return true;
3697
+ }
3698
+ if (this.featureFlagsPoller === undefined) {
3699
+ return false;
3700
+ }
3701
+ return new Promise(resolve => {
3702
+ const timeout = setTimeout(() => {
3703
+ cleanup();
3704
+ resolve(false);
3705
+ }, timeoutMs);
3706
+ const cleanup = this._events.on('localEvaluationFlagsLoaded', count => {
3707
+ clearTimeout(timeout);
3708
+ cleanup();
3709
+ resolve(count > 0);
3710
+ });
3711
+ });
3712
+ }
3667
3713
  async getFeatureFlag(key, distinctId, options) {
3668
3714
  const {
3669
3715
  groups,
@@ -3841,6 +3887,10 @@ class PostHog extends PostHogCoreStateless {
3841
3887
  disableGeoip
3842
3888
  }, distinctId);
3843
3889
  }
3890
+ /**
3891
+ * Reloads the feature flag definitions from the server for local evaluation.
3892
+ * This is useful to call if you want to ensure that the feature flags are up to date before calling getFeatureFlag.
3893
+ */
3844
3894
  async reloadFeatureFlags() {
3845
3895
  await this.featureFlagsPoller?.loadFeatureFlags(true);
3846
3896
  }