posthog-node 4.12.0 → 4.14.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,11 @@
1
+ # 4.14.0 - 2025-04-24
2
+
3
+ 1. feat: Add super properties as a concept to the Node SDK
4
+
5
+ # 4.13.0 - 2025-04-21
6
+
7
+ 1. feat: Add method to wait for local evaluation feature flag definitions to be loaded
8
+
1
9
  # 4.12.0 – 2025-04-17
2
10
 
3
11
  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.14.0";
26
26
 
27
27
  var PostHogPersistedProperty;
28
28
  (function (PostHogPersistedProperty) {
@@ -327,7 +327,7 @@ function safeSetTimeout(fn, timeout) {
327
327
  return t;
328
328
  }
329
329
  function getFetch() {
330
- return typeof fetch !== 'undefined' ? fetch : typeof global.fetch !== 'undefined' ? global.fetch : undefined;
330
+ return typeof fetch !== 'undefined' ? fetch : typeof globalThis.fetch !== 'undefined' ? globalThis.fetch : undefined;
331
331
  }
332
332
  // FNV-1a hash function
333
333
  // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
@@ -1595,6 +1595,30 @@ class PostHogCoreStateless {
1595
1595
  }
1596
1596
  return newSurveys ?? [];
1597
1597
  }
1598
+ get props() {
1599
+ if (!this._props) {
1600
+ this._props = this.getPersistedProperty(PostHogPersistedProperty.Props);
1601
+ }
1602
+ return this._props || {};
1603
+ }
1604
+ set props(val) {
1605
+ this._props = val;
1606
+ }
1607
+ async register(properties) {
1608
+ this.wrap(() => {
1609
+ this.props = {
1610
+ ...this.props,
1611
+ ...properties,
1612
+ };
1613
+ this.setPersistedProperty(PostHogPersistedProperty.Props, this.props);
1614
+ });
1615
+ }
1616
+ async unregister(property) {
1617
+ this.wrap(() => {
1618
+ delete this.props[property];
1619
+ this.setPersistedProperty(PostHogPersistedProperty.Props, this.props);
1620
+ });
1621
+ }
1598
1622
  /***
1599
1623
  *** QUEUEING AND FLUSHING
1600
1624
  ***/
@@ -1988,6 +2012,7 @@ class FeatureFlagsPoller {
1988
2012
  this.fetch = options.fetch || fetch$1;
1989
2013
  this.onError = options.onError;
1990
2014
  this.customHeaders = customHeaders;
2015
+ this.onLoad = options.onLoad;
1991
2016
  void this.loadFeatureFlags();
1992
2017
  }
1993
2018
  debug(enabled = true) {
@@ -2204,6 +2229,13 @@ class FeatureFlagsPoller {
2204
2229
  await this._loadFeatureFlags();
2205
2230
  }
2206
2231
  }
2232
+ /**
2233
+ * Returns true if the feature flags poller has loaded successfully at least once and has more than 0 feature flags.
2234
+ * This is useful to check if local evaluation is ready before calling getFeatureFlag.
2235
+ */
2236
+ isLocalEvaluationReady() {
2237
+ return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
2238
+ }
2207
2239
  /**
2208
2240
  * If a client is misconfigured with an invalid or improper API key, the polling interval is doubled each time
2209
2241
  * until a successful request is made, up to a maximum of 60 seconds.
@@ -2279,6 +2311,7 @@ class FeatureFlagsPoller {
2279
2311
  this.loadedSuccessfullyOnce = true;
2280
2312
  this.shouldBeginExponentialBackoff = false;
2281
2313
  this.backOffCount = 0;
2314
+ this.onLoad?.(this.featureFlags.length);
2282
2315
  break;
2283
2316
  }
2284
2317
  default:
@@ -3069,16 +3102,28 @@ async function propertiesFromUnknownInput(stackParser, input, hint) {
3069
3102
  handled: true,
3070
3103
  type: 'generic'
3071
3104
  };
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;
3105
+ const errorList = getErrorList(mechanism, input, hint);
3106
+ const exceptionList = await Promise.all(errorList.map(async error => {
3107
+ const exception = await exceptionFromError(stackParser, error);
3108
+ exception.value = exception.value || '';
3109
+ exception.type = exception.type || 'Error';
3110
+ exception.mechanism = mechanism;
3111
+ return exception;
3112
+ }));
3077
3113
  const properties = {
3078
- $exception_list: [exception]
3114
+ $exception_list: exceptionList
3079
3115
  };
3080
3116
  return properties;
3081
3117
  }
3118
+ // Flatten error causes into a list of errors
3119
+ // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
3120
+ function getErrorList(mechanism, input, hint) {
3121
+ const error = getError(mechanism, input, hint);
3122
+ if (error.cause) {
3123
+ return [error, ...getErrorList(mechanism, error.cause, hint)];
3124
+ }
3125
+ return [error];
3126
+ }
3082
3127
  function getError(mechanism, exception, hint) {
3083
3128
  if (isError(exception)) {
3084
3129
  return exception;
@@ -3105,7 +3150,7 @@ function getErrorPropertyFromObject(obj) {
3105
3150
  for (const prop in obj) {
3106
3151
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
3107
3152
  const value = obj[prop];
3108
- if (value instanceof Error) {
3153
+ if (isError(value)) {
3109
3154
  return value;
3110
3155
  }
3111
3156
  }
@@ -3532,6 +3577,9 @@ class PostHog extends PostHogCoreStateless {
3532
3577
  onError: err => {
3533
3578
  this._events.emit('error', err);
3534
3579
  },
3580
+ onLoad: count => {
3581
+ this._events.emit('localEvaluationFlagsLoaded', count);
3582
+ },
3535
3583
  customHeaders: this.getCustomHeaders()
3536
3584
  });
3537
3585
  }
@@ -3664,6 +3712,28 @@ class PostHog extends PostHogCoreStateless {
3664
3712
  disableGeoip: data.disableGeoip
3665
3713
  });
3666
3714
  }
3715
+ isLocalEvaluationReady() {
3716
+ return this.featureFlagsPoller?.isLocalEvaluationReady() ?? false;
3717
+ }
3718
+ async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
3719
+ if (this.isLocalEvaluationReady()) {
3720
+ return true;
3721
+ }
3722
+ if (this.featureFlagsPoller === undefined) {
3723
+ return false;
3724
+ }
3725
+ return new Promise(resolve => {
3726
+ const timeout = setTimeout(() => {
3727
+ cleanup();
3728
+ resolve(false);
3729
+ }, timeoutMs);
3730
+ const cleanup = this._events.on('localEvaluationFlagsLoaded', count => {
3731
+ clearTimeout(timeout);
3732
+ cleanup();
3733
+ resolve(count > 0);
3734
+ });
3735
+ });
3736
+ }
3667
3737
  async getFeatureFlag(key, distinctId, options) {
3668
3738
  const {
3669
3739
  groups,
@@ -3841,6 +3911,10 @@ class PostHog extends PostHogCoreStateless {
3841
3911
  disableGeoip
3842
3912
  }, distinctId);
3843
3913
  }
3914
+ /**
3915
+ * Reloads the feature flag definitions from the server for local evaluation.
3916
+ * This is useful to call if you want to ensure that the feature flags are up to date before calling getFeatureFlag.
3917
+ */
3844
3918
  async reloadFeatureFlags() {
3845
3919
  await this.featureFlagsPoller?.loadFeatureFlags(true);
3846
3920
  }