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.
package/lib/index.d.ts CHANGED
@@ -588,10 +588,15 @@ interface IdentifyMessage {
588
588
  properties?: Record<string | number, any>;
589
589
  disableGeoip?: boolean;
590
590
  }
591
+ interface SendFeatureFlagsOptions {
592
+ onlyEvaluateLocally?: boolean;
593
+ personProperties?: Record<string, any>;
594
+ groupProperties?: Record<string, Record<string, any>>;
595
+ }
591
596
  interface EventMessage extends IdentifyMessage {
592
597
  event: string;
593
598
  groups?: Record<string, string | number>;
594
- sendFeatureFlags?: boolean;
599
+ sendFeatureFlags?: boolean | SendFeatureFlagsOptions;
595
600
  timestamp?: Date;
596
601
  uuid?: string;
597
602
  }
@@ -912,6 +917,8 @@ declare abstract class PostHogBackendClient extends PostHogCoreStateless impleme
912
917
  reloadFeatureFlags(): Promise<void>;
913
918
  _shutdown(shutdownTimeoutMs?: number): Promise<void>;
914
919
  private _requestRemoteConfigPayload;
920
+ private extractPropertiesFromEvent;
921
+ private getFeatureFlagsForEvent;
915
922
  private addLocalPersonAndGroupProperties;
916
923
  captureException(error: unknown, distinctId?: string, additionalProperties?: Record<string | number, any>): void;
917
924
  captureExceptionImmediate(error: unknown, distinctId?: string, additionalProperties?: Record<string | number, any>): Promise<void>;
@@ -965,4 +972,4 @@ declare class PostHog extends PostHogBackendClient {
965
972
  getLibraryId(): string;
966
973
  }
967
974
 
968
- export { EventMessage, FeatureFlagCondition, FlagProperty, GroupIdentifyMessage, IPostHog, IdentifyMessage, PostHog, PostHogFeatureFlag, PostHogOptions, PostHogSentryIntegration, PropertyGroup, SentryIntegrationOptions, createEventProcessor, sentryIntegration, setupExpressErrorHandler };
975
+ export { EventMessage, FeatureFlagCondition, FlagProperty, GroupIdentifyMessage, IPostHog, IdentifyMessage, PostHog, PostHogFeatureFlag, PostHogOptions, PostHogSentryIntegration, PropertyGroup, SendFeatureFlagsOptions, SentryIntegrationOptions, createEventProcessor, sentryIntegration, setupExpressErrorHandler };
@@ -1342,7 +1342,7 @@ function snipLine(line, colno) {
1342
1342
  return newLine;
1343
1343
  }
1344
1344
 
1345
- var version = "5.3.1";
1345
+ var version = "5.5.0";
1346
1346
 
1347
1347
  var PostHogPersistedProperty;
1348
1348
  (function (PostHogPersistedProperty) {
@@ -2636,7 +2636,9 @@ class FeatureFlagsPoller {
2636
2636
  payloads[flag.key] = matchPayload;
2637
2637
  }
2638
2638
  } catch (e) {
2639
- if (e instanceof InconclusiveMatchError) ; else if (e instanceof Error) {
2639
+ if (e instanceof InconclusiveMatchError) {
2640
+ this.onError?.(new Error(`Unable to compute flag locally: ${flag.key} - ${e.message}`));
2641
+ } else if (e instanceof Error) {
2640
2642
  this.onError?.(new Error(`Error computing flag locally: ${flag.key}: ${e}`));
2641
2643
  }
2642
2644
  fallbackToFlags = true;
@@ -3260,32 +3262,17 @@ class PostHogBackendClient extends PostHogCoreStateless {
3260
3262
  uuid
3261
3263
  });
3262
3264
  };
3263
- const _getFlags = async (distinctId, groups, disableGeoip) => {
3264
- return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
3265
- };
3266
3265
  // :TRICKY: If we flush, or need to shut down, to not lose events we want this promise to resolve before we flush
3267
3266
  const capturePromise = Promise.resolve().then(async () => {
3268
3267
  if (sendFeatureFlags) {
3269
- // If we are sending feature flags, we need to make sure we have the latest flags
3270
- // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
3271
- return await _getFlags(distinctId, groups, disableGeoip);
3268
+ // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
3269
+ const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined;
3270
+ return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
3272
3271
  }
3273
3272
  if (event === '$feature_flag_called') {
3274
3273
  // 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.
3275
3274
  return {};
3276
3275
  }
3277
- if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3278
- // Otherwise we may as well check for the flags locally and include them if they are already loaded
3279
- const groupsWithStringValues = {};
3280
- for (const [key, value] of Object.entries(groups || {})) {
3281
- groupsWithStringValues[key] = String(value);
3282
- }
3283
- return await this.getAllFlags(distinctId, {
3284
- groups: groupsWithStringValues,
3285
- disableGeoip,
3286
- onlyEvaluateLocally: true
3287
- });
3288
- }
3289
3276
  return {};
3290
3277
  }).then(flags => {
3291
3278
  // Derive the relevant flag properties to add
@@ -3334,31 +3321,16 @@ class PostHogBackendClient extends PostHogCoreStateless {
3334
3321
  uuid
3335
3322
  });
3336
3323
  };
3337
- const _getFlags = async (distinctId, groups, disableGeoip) => {
3338
- return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
3339
- };
3340
3324
  const capturePromise = Promise.resolve().then(async () => {
3341
3325
  if (sendFeatureFlags) {
3342
- // If we are sending feature flags, we need to make sure we have the latest flags
3343
- // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
3344
- return await _getFlags(distinctId, groups, disableGeoip);
3326
+ // If we are sending feature flags, we evaluate them locally if the user prefers it, otherwise we fall back to remote evaluation
3327
+ const sendFeatureFlagsOptions = typeof sendFeatureFlags === 'object' ? sendFeatureFlags : undefined;
3328
+ return await this.getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions);
3345
3329
  }
3346
3330
  if (event === '$feature_flag_called') {
3347
3331
  // 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.
3348
3332
  return {};
3349
3333
  }
3350
- if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3351
- // Otherwise we may as well check for the flags locally and include them if they are already loaded
3352
- const groupsWithStringValues = {};
3353
- for (const [key, value] of Object.entries(groups || {})) {
3354
- groupsWithStringValues[key] = String(value);
3355
- }
3356
- return await this.getAllFlags(distinctId, {
3357
- groups: groupsWithStringValues,
3358
- disableGeoip,
3359
- onlyEvaluateLocally: true
3360
- });
3361
- }
3362
3334
  return {};
3363
3335
  }).then(flags => {
3364
3336
  // Derive the relevant flag properties to add
@@ -3693,6 +3665,75 @@ class PostHogBackendClient extends PostHogCoreStateless {
3693
3665
  }
3694
3666
  }
3695
3667
  }
3668
+ extractPropertiesFromEvent(eventProperties, groups) {
3669
+ if (!eventProperties) {
3670
+ return {
3671
+ personProperties: {},
3672
+ groupProperties: {}
3673
+ };
3674
+ }
3675
+ const personProperties = {};
3676
+ const groupProperties = {};
3677
+ for (const [key, value] of Object.entries(eventProperties)) {
3678
+ // If the value is a plain object and the key exists in groups, treat it as group properties
3679
+ if (isPlainObject(value) && groups && key in groups) {
3680
+ const groupProps = {};
3681
+ for (const [groupKey, groupValue] of Object.entries(value)) {
3682
+ groupProps[String(groupKey)] = String(groupValue);
3683
+ }
3684
+ groupProperties[String(key)] = groupProps;
3685
+ } else {
3686
+ // Otherwise treat as person property
3687
+ personProperties[String(key)] = String(value);
3688
+ }
3689
+ }
3690
+ return {
3691
+ personProperties,
3692
+ groupProperties
3693
+ };
3694
+ }
3695
+ async getFeatureFlagsForEvent(distinctId, groups, disableGeoip, sendFeatureFlagsOptions) {
3696
+ // Use properties directly from options if they exist
3697
+ const finalPersonProperties = sendFeatureFlagsOptions?.personProperties || {};
3698
+ const finalGroupProperties = sendFeatureFlagsOptions?.groupProperties || {};
3699
+ // Check if we should only evaluate locally
3700
+ const onlyEvaluateLocally = sendFeatureFlagsOptions?.onlyEvaluateLocally ?? false;
3701
+ // If onlyEvaluateLocally is true, only use local evaluation
3702
+ if (onlyEvaluateLocally) {
3703
+ if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3704
+ const groupsWithStringValues = {};
3705
+ for (const [key, value] of Object.entries(groups || {})) {
3706
+ groupsWithStringValues[key] = String(value);
3707
+ }
3708
+ return await this.getAllFlags(distinctId, {
3709
+ groups: groupsWithStringValues,
3710
+ personProperties: finalPersonProperties,
3711
+ groupProperties: finalGroupProperties,
3712
+ disableGeoip,
3713
+ onlyEvaluateLocally: true
3714
+ });
3715
+ } else {
3716
+ // If onlyEvaluateLocally is true but we don't have local flags, return empty
3717
+ return {};
3718
+ }
3719
+ }
3720
+ // 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)
3721
+ if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
3722
+ const groupsWithStringValues = {};
3723
+ for (const [key, value] of Object.entries(groups || {})) {
3724
+ groupsWithStringValues[key] = String(value);
3725
+ }
3726
+ return await this.getAllFlags(distinctId, {
3727
+ groups: groupsWithStringValues,
3728
+ personProperties: finalPersonProperties,
3729
+ groupProperties: finalGroupProperties,
3730
+ disableGeoip,
3731
+ onlyEvaluateLocally: true
3732
+ });
3733
+ }
3734
+ // Fall back to remote evaluation if local evaluation is not available
3735
+ return (await super.getFeatureFlagsStateless(distinctId, groups, finalPersonProperties, finalGroupProperties, disableGeoip)).flags;
3736
+ }
3696
3737
  addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties) {
3697
3738
  const allPersonProperties = {
3698
3739
  distinct_id: distinctId,