posthog-node 4.8.0 → 4.9.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 +8 -0
- package/lib/index.cjs.js +34 -8
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.esm.js +34 -8
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +4 -4
- package/lib/posthog-core/src/types.d.ts +1 -0
- package/lib/posthog-node/test/test-utils.d.ts +2 -1
- package/package.json +1 -1
- package/src/feature-flags.ts +13 -0
- package/test/feature-flags.spec.ts +39 -1
- package/test/posthog-node.spec.ts +4 -0
- package/test/test-utils.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Next
|
|
2
2
|
|
|
3
|
+
# 4.9.0 – 2025-03-04
|
|
4
|
+
|
|
5
|
+
1. Allow feature flags to be evaluated individually when local evaluation is not being used
|
|
6
|
+
|
|
7
|
+
# 4.8.1 – 2025-02-26
|
|
8
|
+
|
|
9
|
+
1. Supports gracefully handling quotaLimited responses from the PostHog API for feature flag evaluation
|
|
10
|
+
|
|
3
11
|
# 4.8.0 - 2025-02-26
|
|
4
12
|
|
|
5
13
|
1. Add guardrails and exponential error backoff in the feature flag local evaluation poller to prevent high rates of 401/403 traffic towards `/local_evaluation`
|
package/lib/index.cjs.js
CHANGED
|
@@ -7,7 +7,7 @@ var node_fs = require('node:fs');
|
|
|
7
7
|
var node_readline = require('node:readline');
|
|
8
8
|
var node_path = require('node:path');
|
|
9
9
|
|
|
10
|
-
var version = "4.
|
|
10
|
+
var version = "4.9.0";
|
|
11
11
|
|
|
12
12
|
var PostHogPersistedProperty;
|
|
13
13
|
(function (PostHogPersistedProperty) {
|
|
@@ -958,6 +958,11 @@ class PostHogFetchNetworkError extends Error {
|
|
|
958
958
|
function isPostHogFetchError(err) {
|
|
959
959
|
return typeof err === 'object' && (err instanceof PostHogFetchHttpError || err instanceof PostHogFetchNetworkError);
|
|
960
960
|
}
|
|
961
|
+
var QuotaLimitedFeature;
|
|
962
|
+
(function (QuotaLimitedFeature) {
|
|
963
|
+
QuotaLimitedFeature["FeatureFlags"] = "feature_flags";
|
|
964
|
+
QuotaLimitedFeature["Recordings"] = "recordings";
|
|
965
|
+
})(QuotaLimitedFeature || (QuotaLimitedFeature = {}));
|
|
961
966
|
class PostHogCoreStateless {
|
|
962
967
|
constructor(apiKey, options) {
|
|
963
968
|
this.flushPromise = null;
|
|
@@ -1150,7 +1155,7 @@ class PostHogCoreStateless {
|
|
|
1150
1155
|
}
|
|
1151
1156
|
async getFeatureFlagStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1152
1157
|
await this._initPromise;
|
|
1153
|
-
const featureFlags = await this.getFeatureFlagsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip);
|
|
1158
|
+
const featureFlags = await this.getFeatureFlagsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [key]);
|
|
1154
1159
|
if (!featureFlags) {
|
|
1155
1160
|
// If we haven't loaded flags yet, or errored out, we respond with undefined
|
|
1156
1161
|
return undefined;
|
|
@@ -1166,7 +1171,7 @@ class PostHogCoreStateless {
|
|
|
1166
1171
|
}
|
|
1167
1172
|
async getFeatureFlagPayloadStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1168
1173
|
await this._initPromise;
|
|
1169
|
-
const payloads = await this.getFeatureFlagPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip);
|
|
1174
|
+
const payloads = await this.getFeatureFlagPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [key]);
|
|
1170
1175
|
if (!payloads) {
|
|
1171
1176
|
return undefined;
|
|
1172
1177
|
}
|
|
@@ -1177,9 +1182,9 @@ class PostHogCoreStateless {
|
|
|
1177
1182
|
}
|
|
1178
1183
|
return response;
|
|
1179
1184
|
}
|
|
1180
|
-
async getFeatureFlagPayloadsStateless(distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1185
|
+
async getFeatureFlagPayloadsStateless(distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip, flagKeysToEvaluate) {
|
|
1181
1186
|
await this._initPromise;
|
|
1182
|
-
const payloads = (await this.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip)).payloads;
|
|
1187
|
+
const payloads = (await this.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, flagKeysToEvaluate)).payloads;
|
|
1183
1188
|
return payloads;
|
|
1184
1189
|
}
|
|
1185
1190
|
_parsePayload(response) {
|
|
@@ -1190,17 +1195,28 @@ class PostHogCoreStateless {
|
|
|
1190
1195
|
return response;
|
|
1191
1196
|
}
|
|
1192
1197
|
}
|
|
1193
|
-
async getFeatureFlagsStateless(distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1198
|
+
async getFeatureFlagsStateless(distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip, flagKeysToEvaluate) {
|
|
1194
1199
|
await this._initPromise;
|
|
1195
|
-
return (await this.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip)).flags;
|
|
1200
|
+
return (await this.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, flagKeysToEvaluate)).flags;
|
|
1196
1201
|
}
|
|
1197
|
-
async getFeatureFlagsAndPayloadsStateless(distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1202
|
+
async getFeatureFlagsAndPayloadsStateless(distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip, flagKeysToEvaluate) {
|
|
1198
1203
|
await this._initPromise;
|
|
1199
1204
|
const extraPayload = {};
|
|
1200
1205
|
if (disableGeoip ?? this.disableGeoip) {
|
|
1201
1206
|
extraPayload['geoip_disable'] = true;
|
|
1202
1207
|
}
|
|
1208
|
+
if (flagKeysToEvaluate) {
|
|
1209
|
+
extraPayload['flag_keys_to_evaluate'] = flagKeysToEvaluate;
|
|
1210
|
+
}
|
|
1203
1211
|
const decideResponse = await this.getDecide(distinctId, groups, personProperties, groupProperties, extraPayload);
|
|
1212
|
+
// Add check for quota limitation on feature flags
|
|
1213
|
+
if (decideResponse?.quotaLimited?.includes(QuotaLimitedFeature.FeatureFlags)) {
|
|
1214
|
+
console.warn('[FEATURE FLAGS] Feature flags quota limit exceeded - feature flags unavailable. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts');
|
|
1215
|
+
return {
|
|
1216
|
+
flags: undefined,
|
|
1217
|
+
payloads: undefined,
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1204
1220
|
const flags = decideResponse?.featureFlags;
|
|
1205
1221
|
const payloads = decideResponse?.featureFlagPayloads;
|
|
1206
1222
|
let parsedPayloads = payloads;
|
|
@@ -1757,6 +1773,16 @@ class FeatureFlagsPoller {
|
|
|
1757
1773
|
this.authenticationErrorCount += 1;
|
|
1758
1774
|
throw new ClientError(`Your personal API key does not have permission to fetch feature flag definitions for local evaluation. Setting next polling interval to ${this.getPollingInterval()}ms. Are you sure you're using the correct personal and Project API key pair? More information: https://posthog.com/docs/api/overview`);
|
|
1759
1775
|
}
|
|
1776
|
+
if (res && res.status === 402) {
|
|
1777
|
+
// Quota limited - clear all flags
|
|
1778
|
+
console.warn('[FEATURE FLAGS] Feature flags quota limit exceeded - unsetting all local flags. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts');
|
|
1779
|
+
this.featureFlags = [];
|
|
1780
|
+
this.featureFlagsByKey = {};
|
|
1781
|
+
this.groupTypeMapping = {};
|
|
1782
|
+
this.cohorts = {};
|
|
1783
|
+
this.loadedSuccessfullyOnce = false;
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1760
1786
|
if (res && res.status !== 200) {
|
|
1761
1787
|
// something else went wrong, or the server is down.
|
|
1762
1788
|
// In this case, don't override existing flags
|