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.
@@ -1320,7 +1320,7 @@ function snipLine(line, colno) {
1320
1320
  return newLine;
1321
1321
  }
1322
1322
 
1323
- var version = "5.3.1";
1323
+ var version = "5.5.0";
1324
1324
 
1325
1325
  var PostHogPersistedProperty;
1326
1326
  (function (PostHogPersistedProperty) {
@@ -2614,7 +2614,9 @@ class FeatureFlagsPoller {
2614
2614
  payloads[flag.key] = matchPayload;
2615
2615
  }
2616
2616
  } catch (e) {
2617
- if (e instanceof InconclusiveMatchError) ; else if (e instanceof Error) {
2617
+ if (e instanceof InconclusiveMatchError) {
2618
+ this.onError?.(new Error(`Unable to compute flag locally: ${flag.key} - ${e.message}`));
2619
+ } else if (e instanceof Error) {
2618
2620
  this.onError?.(new Error(`Error computing flag locally: ${flag.key}: ${e}`));
2619
2621
  }
2620
2622
  fallbackToFlags = true;
@@ -3238,32 +3240,17 @@ class PostHogBackendClient extends PostHogCoreStateless {
3238
3240
  uuid
3239
3241
  });
3240
3242
  };
3241
- const _getFlags = async (distinctId, groups, disableGeoip) => {
3242
- return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
3243
- };
3244
3243
  // :TRICKY: If we flush, or need to shut down, to not lose events we want this promise to resolve before we flush
3245
3244
  const capturePromise = Promise.resolve().then(async () => {
3246
3245
  if (sendFeatureFlags) {
3247
- // If we are sending feature flags, we need to make sure we have the latest flags
3248
- // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
3249
- return await _getFlags(distinctId, groups, disableGeoip);
3246
+ // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
3247
+ const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined;
3248
+ return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
3250
3249
  }
3251
3250
  if (event === '$feature_flag_called') {
3252
3251
  // 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.
3253
3252
  return {};
3254
3253
  }
3255
- if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3256
- // Otherwise we may as well check for the flags locally and include them if they are already loaded
3257
- const groupsWithStringValues = {};
3258
- for (const [key, value] of Object.entries(groups || {})) {
3259
- groupsWithStringValues[key] = String(value);
3260
- }
3261
- return await this.getAllFlags(distinctId, {
3262
- groups: groupsWithStringValues,
3263
- disableGeoip,
3264
- onlyEvaluateLocally: true
3265
- });
3266
- }
3267
3254
  return {};
3268
3255
  }).then(flags => {
3269
3256
  // Derive the relevant flag properties to add
@@ -3312,31 +3299,16 @@ class PostHogBackendClient extends PostHogCoreStateless {
3312
3299
  uuid
3313
3300
  });
3314
3301
  };
3315
- const _getFlags = async (distinctId, groups, disableGeoip) => {
3316
- return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
3317
- };
3318
3302
  const capturePromise = Promise.resolve().then(async () => {
3319
3303
  if (sendFeatureFlags) {
3320
- // If we are sending feature flags, we need to make sure we have the latest flags
3321
- // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
3322
- return await _getFlags(distinctId, groups, disableGeoip);
3304
+ // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
3305
+ const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined;
3306
+ return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
3323
3307
  }
3324
3308
  if (event === '$feature_flag_called') {
3325
3309
  // 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.
3326
3310
  return {};
3327
3311
  }
3328
- if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3329
- // Otherwise we may as well check for the flags locally and include them if they are already loaded
3330
- const groupsWithStringValues = {};
3331
- for (const [key, value] of Object.entries(groups || {})) {
3332
- groupsWithStringValues[key] = String(value);
3333
- }
3334
- return await this.getAllFlags(distinctId, {
3335
- groups: groupsWithStringValues,
3336
- disableGeoip,
3337
- onlyEvaluateLocally: true
3338
- });
3339
- }
3340
3312
  return {};
3341
3313
  }).then(flags => {
3342
3314
  // Derive the relevant flag properties to add
@@ -3671,6 +3643,75 @@ class PostHogBackendClient extends PostHogCoreStateless {
3671
3643
  }
3672
3644
  }
3673
3645
  }
3646
+ extractPropertiesFromEvent(eventProperties, groups) {
3647
+ if (!eventProperties) {
3648
+ return {
3649
+ personProperties: {},
3650
+ groupProperties: {}
3651
+ };
3652
+ }
3653
+ const personProperties = {};
3654
+ const groupProperties = {};
3655
+ for (const [key, value] of Object.entries(eventProperties)) {
3656
+ // If the value is a plain object and the key exists in groups, treat it as group properties
3657
+ if (isPlainObject(value) && groups && key in groups) {
3658
+ const groupProps = {};
3659
+ for (const [groupKey, groupValue] of Object.entries(value)) {
3660
+ groupProps[String(groupKey)] = String(groupValue);
3661
+ }
3662
+ groupProperties[String(key)] = groupProps;
3663
+ } else {
3664
+ // Otherwise treat as person property
3665
+ personProperties[String(key)] = String(value);
3666
+ }
3667
+ }
3668
+ return {
3669
+ personProperties,
3670
+ groupProperties
3671
+ };
3672
+ }
3673
+ async getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions) {
3674
+ // Use properties directly from options if they exist
3675
+ const finalPersonProperties = sendFeatureFlagsOptions?.personProperties || {};
3676
+ const finalGroupProperties = sendFeatureFlagsOptions?.groupProperties || {};
3677
+ // Check if we should only evaluate locally
3678
+ const onlyEvaluateLocally = sendFeatureFlagsOptions?.onlyEvaluateLocally ?? false;
3679
+ // If onlyEvaluateLocally is true, only use local evaluation
3680
+ if (onlyEvaluateLocally) {
3681
+ if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3682
+ const groupsWithStringValues = {};
3683
+ for (const [key, value] of Object.entries(groups || {})) {
3684
+ groupsWithStringValues[key] = String(value);
3685
+ }
3686
+ return await this.getAllFlags(distinctId, {
3687
+ groups: groupsWithStringValues,
3688
+ personProperties: finalPersonProperties,
3689
+ groupProperties: finalGroupProperties,
3690
+ disableGeoip,
3691
+ onlyEvaluateLocally: true
3692
+ });
3693
+ } else {
3694
+ // If onlyEvaluateLocally is true but we don't have local flags, return empty
3695
+ return {};
3696
+ }
3697
+ }
3698
+ // 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)
3699
+ if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3700
+ const groupsWithStringValues = {};
3701
+ for (const [key, value] of Object.entries(groups || {})) {
3702
+ groupsWithStringValues[key] = String(value);
3703
+ }
3704
+ return await this.getAllFlags(distinctId, {
3705
+ groups: groupsWithStringValues,
3706
+ personProperties: finalPersonProperties,
3707
+ groupProperties: finalGroupProperties,
3708
+ disableGeoip,
3709
+ onlyEvaluateLocally: true
3710
+ });
3711
+ }
3712
+ // Fall back to remote evaluation if local evaluation is not available
3713
+ return (await super.getFeatureFlagsStateless(distinctId, groups, finalPersonProperties, finalGroupProperties, disableGeoip)).flags;
3714
+ }
3674
3715
  addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties) {
3675
3716
  const allPersonProperties = {
3676
3717
  distinct_id: distinctId,