posthog-node 5.3.1 → 5.5.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.
@@ -915,7 +915,7 @@ function setupExpressErrorHandler(_posthog, app) {
915
915
  });
916
916
  }
917
917
 
918
- var version = "5.3.1";
918
+ var version = "5.5.0";
919
919
 
920
920
  var PostHogPersistedProperty;
921
921
  (function (PostHogPersistedProperty) {
@@ -2209,7 +2209,9 @@ class FeatureFlagsPoller {
2209
2209
  payloads[flag.key] = matchPayload;
2210
2210
  }
2211
2211
  } catch (e) {
2212
- if (e instanceof InconclusiveMatchError) ; else if (e instanceof Error) {
2212
+ if (e instanceof InconclusiveMatchError) {
2213
+ this.onError?.(new Error(`Unable to compute flag locally: ${flag.key} - ${e.message}`));
2214
+ } else if (e instanceof Error) {
2213
2215
  this.onError?.(new Error(`Error computing flag locally: ${flag.key}: ${e}`));
2214
2216
  }
2215
2217
  fallbackToFlags = true;
@@ -2833,32 +2835,17 @@ class PostHogBackendClient extends PostHogCoreStateless {
2833
2835
  uuid
2834
2836
  });
2835
2837
  };
2836
- const _getFlags = async (distinctId, groups, disableGeoip) => {
2837
- return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
2838
- };
2839
2838
  // :TRICKY: If we flush, or need to shut down, to not lose events we want this promise to resolve before we flush
2840
2839
  const capturePromise = Promise.resolve().then(async () => {
2841
2840
  if (sendFeatureFlags) {
2842
- // If we are sending feature flags, we need to make sure we have the latest flags
2843
- // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
2844
- return await _getFlags(distinctId, groups, disableGeoip);
2841
+ // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
2842
+ const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined;
2843
+ return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
2845
2844
  }
2846
2845
  if (event === '$feature_flag_called') {
2847
2846
  // If we're capturing a $feature_flag_called event, we don't want to enrich the event with cached flags that may be out of date.
2848
2847
  return {};
2849
2848
  }
2850
- if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
2851
- // Otherwise we may as well check for the flags locally and include them if they are already loaded
2852
- const groupsWithStringValues = {};
2853
- for (const [key, value] of Object.entries(groups || {})) {
2854
- groupsWithStringValues[key] = String(value);
2855
- }
2856
- return await this.getAllFlags(distinctId, {
2857
- groups: groupsWithStringValues,
2858
- disableGeoip,
2859
- onlyEvaluateLocally: true
2860
- });
2861
- }
2862
2849
  return {};
2863
2850
  }).then(flags => {
2864
2851
  // Derive the relevant flag properties to add
@@ -2907,31 +2894,16 @@ class PostHogBackendClient extends PostHogCoreStateless {
2907
2894
  uuid
2908
2895
  });
2909
2896
  };
2910
- const _getFlags = async (distinctId, groups, disableGeoip) => {
2911
- return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
2912
- };
2913
2897
  const capturePromise = Promise.resolve().then(async () => {
2914
2898
  if (sendFeatureFlags) {
2915
- // If we are sending feature flags, we need to make sure we have the latest flags
2916
- // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
2917
- return await _getFlags(distinctId, groups, disableGeoip);
2899
+ // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
2900
+ const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined;
2901
+ return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
2918
2902
  }
2919
2903
  if (event === '$feature_flag_called') {
2920
2904
  // If we're capturing a $feature_flag_called event, we don't want to enrich the event with cached flags that may be out of date.
2921
2905
  return {};
2922
2906
  }
2923
- if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
2924
- // Otherwise we may as well check for the flags locally and include them if they are already loaded
2925
- const groupsWithStringValues = {};
2926
- for (const [key, value] of Object.entries(groups || {})) {
2927
- groupsWithStringValues[key] = String(value);
2928
- }
2929
- return await this.getAllFlags(distinctId, {
2930
- groups: groupsWithStringValues,
2931
- disableGeoip,
2932
- onlyEvaluateLocally: true
2933
- });
2934
- }
2935
2907
  return {};
2936
2908
  }).then(flags => {
2937
2909
  // Derive the relevant flag properties to add
@@ -3266,6 +3238,75 @@ class PostHogBackendClient extends PostHogCoreStateless {
3266
3238
  }
3267
3239
  }
3268
3240
  }
3241
+ extractPropertiesFromEvent(eventProperties, groups) {
3242
+ if (!eventProperties) {
3243
+ return {
3244
+ personProperties: {},
3245
+ groupProperties: {}
3246
+ };
3247
+ }
3248
+ const personProperties = {};
3249
+ const groupProperties = {};
3250
+ for (const [key, value] of Object.entries(eventProperties)) {
3251
+ // If the value is a plain object and the key exists in groups, treat it as group properties
3252
+ if (isPlainObject(value) && groups && key in groups) {
3253
+ const groupProps = {};
3254
+ for (const [groupKey, groupValue] of Object.entries(value)) {
3255
+ groupProps[String(groupKey)] = String(groupValue);
3256
+ }
3257
+ groupProperties[String(key)] = groupProps;
3258
+ } else {
3259
+ // Otherwise treat as person property
3260
+ personProperties[String(key)] = String(value);
3261
+ }
3262
+ }
3263
+ return {
3264
+ personProperties,
3265
+ groupProperties
3266
+ };
3267
+ }
3268
+ async getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions) {
3269
+ // Use properties directly from options if they exist
3270
+ const finalPersonProperties = sendFeatureFlagsOptions?.personProperties || {};
3271
+ const finalGroupProperties = sendFeatureFlagsOptions?.groupProperties || {};
3272
+ // Check if we should only evaluate locally
3273
+ const onlyEvaluateLocally = sendFeatureFlagsOptions?.onlyEvaluateLocally ?? false;
3274
+ // If onlyEvaluateLocally is true, only use local evaluation
3275
+ if (onlyEvaluateLocally) {
3276
+ if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3277
+ const groupsWithStringValues = {};
3278
+ for (const [key, value] of Object.entries(groups || {})) {
3279
+ groupsWithStringValues[key] = String(value);
3280
+ }
3281
+ return await this.getAllFlags(distinctId, {
3282
+ groups: groupsWithStringValues,
3283
+ personProperties: finalPersonProperties,
3284
+ groupProperties: finalGroupProperties,
3285
+ disableGeoip,
3286
+ onlyEvaluateLocally: true
3287
+ });
3288
+ } else {
3289
+ // If onlyEvaluateLocally is true but we don't have local flags, return empty
3290
+ return {};
3291
+ }
3292
+ }
3293
+ // Prefer local evaluation if available (default behavior; I'd rather not penalize users who haven't updated to the new API but still want to use local evaluation)
3294
+ if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3295
+ const groupsWithStringValues = {};
3296
+ for (const [key, value] of Object.entries(groups || {})) {
3297
+ groupsWithStringValues[key] = String(value);
3298
+ }
3299
+ return await this.getAllFlags(distinctId, {
3300
+ groups: groupsWithStringValues,
3301
+ personProperties: finalPersonProperties,
3302
+ groupProperties: finalGroupProperties,
3303
+ disableGeoip,
3304
+ onlyEvaluateLocally: true
3305
+ });
3306
+ }
3307
+ // Fall back to remote evaluation if local evaluation is not available
3308
+ return (await super.getFeatureFlagsStateless(distinctId, groups, finalPersonProperties, finalGroupProperties, disableGeoip)).flags;
3309
+ }
3269
3310
  addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties) {
3270
3311
  const allPersonProperties = {
3271
3312
  distinct_id: distinctId,