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/CHANGELOG.md +52 -0
- package/lib/edge/index.cjs +79 -38
- package/lib/edge/index.cjs.map +1 -1
- package/lib/edge/index.mjs +79 -38
- package/lib/edge/index.mjs.map +1 -1
- package/lib/index.d.ts +9 -2
- package/lib/node/index.cjs +79 -38
- package/lib/node/index.cjs.map +1 -1
- package/lib/node/index.mjs +79 -38
- package/lib/node/index.mjs.map +1 -1
- package/package.json +1 -1
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 };
|
package/lib/node/index.cjs
CHANGED
|
@@ -1342,7 +1342,7 @@ function snipLine(line, colno) {
|
|
|
1342
1342
|
return newLine;
|
|
1343
1343
|
}
|
|
1344
1344
|
|
|
1345
|
-
var version = "5.
|
|
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)
|
|
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
|
|
3270
|
-
|
|
3271
|
-
return await
|
|
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
|
|
3343
|
-
|
|
3344
|
-
return await
|
|
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,
|