posthog-node 4.7.0 → 4.8.1
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 +61 -5
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.esm.js +59 -6
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +1 -1
- package/lib/posthog-core/src/types.d.ts +1 -0
- package/lib/posthog-core/src/utils.d.ts +1 -0
- package/lib/posthog-node/src/feature-flags.d.ts +9 -0
- package/lib/posthog-node/src/posthog-node.d.ts +3 -0
- package/lib/posthog-node/test/test-utils.d.ts +2 -1
- package/package.json +1 -1
- package/src/feature-flags.ts +45 -3
- package/src/posthog-node.ts +17 -5
- package/test/feature-flags.spec.ts +36 -0
- package/test/posthog-node.spec.ts +34 -2
- package/test/test-utils.ts +3 -1
package/lib/index.d.ts
CHANGED
|
@@ -108,6 +108,7 @@ type PostHogDecideResponse = {
|
|
|
108
108
|
[key: string]: JsonType;
|
|
109
109
|
};
|
|
110
110
|
errorsWhileComputingFlags: boolean;
|
|
111
|
+
quotaLimited?: string[];
|
|
111
112
|
sessionRecording?: boolean | {
|
|
112
113
|
[key: string]: JsonType;
|
|
113
114
|
};
|
|
@@ -406,6 +407,9 @@ type PostHogOptions = PostHogCoreOptions & {
|
|
|
406
407
|
maxCacheSize?: number;
|
|
407
408
|
fetch?: (url: string, options: PostHogFetchOptions) => Promise<PostHogFetchResponse>;
|
|
408
409
|
};
|
|
410
|
+
declare const MINIMUM_POLLING_INTERVAL = 100;
|
|
411
|
+
declare const THIRTY_SECONDS: number;
|
|
412
|
+
declare const SIXTY_SECONDS: number;
|
|
409
413
|
declare class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
410
414
|
private _memoryStorage;
|
|
411
415
|
private featureFlagsPoller?;
|
|
@@ -520,4 +524,4 @@ declare function setupExpressErrorHandler(_posthog: PostHog, app: {
|
|
|
520
524
|
use: (middleware: ExpressMiddleware | ExpressErrorMiddleware) => unknown;
|
|
521
525
|
}): void;
|
|
522
526
|
|
|
523
|
-
export { PostHog, PostHogOptions, PostHogSentryIntegration, SentryIntegrationOptions, createEventProcessor, sentryIntegration, setupExpressErrorHandler };
|
|
527
|
+
export { MINIMUM_POLLING_INTERVAL, PostHog, PostHogOptions, PostHogSentryIntegration, SIXTY_SECONDS, SentryIntegrationOptions, THIRTY_SECONDS, createEventProcessor, sentryIntegration, setupExpressErrorHandler };
|
package/lib/index.esm.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createReadStream } from 'node:fs';
|
|
|
3
3
|
import { createInterface } from 'node:readline';
|
|
4
4
|
import { posix, dirname, sep } from 'node:path';
|
|
5
5
|
|
|
6
|
-
var version = "4.
|
|
6
|
+
var version = "4.8.1";
|
|
7
7
|
|
|
8
8
|
var PostHogPersistedProperty;
|
|
9
9
|
(function (PostHogPersistedProperty) {
|
|
@@ -954,6 +954,11 @@ class PostHogFetchNetworkError extends Error {
|
|
|
954
954
|
function isPostHogFetchError(err) {
|
|
955
955
|
return typeof err === 'object' && (err instanceof PostHogFetchHttpError || err instanceof PostHogFetchNetworkError);
|
|
956
956
|
}
|
|
957
|
+
var QuotaLimitedFeature;
|
|
958
|
+
(function (QuotaLimitedFeature) {
|
|
959
|
+
QuotaLimitedFeature["FeatureFlags"] = "feature_flags";
|
|
960
|
+
QuotaLimitedFeature["Recordings"] = "recordings";
|
|
961
|
+
})(QuotaLimitedFeature || (QuotaLimitedFeature = {}));
|
|
957
962
|
class PostHogCoreStateless {
|
|
958
963
|
constructor(apiKey, options) {
|
|
959
964
|
this.flushPromise = null;
|
|
@@ -1197,6 +1202,14 @@ class PostHogCoreStateless {
|
|
|
1197
1202
|
extraPayload['geoip_disable'] = true;
|
|
1198
1203
|
}
|
|
1199
1204
|
const decideResponse = await this.getDecide(distinctId, groups, personProperties, groupProperties, extraPayload);
|
|
1205
|
+
// Add check for quota limitation on feature flags
|
|
1206
|
+
if (decideResponse?.quotaLimited?.includes(QuotaLimitedFeature.FeatureFlags)) {
|
|
1207
|
+
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');
|
|
1208
|
+
return {
|
|
1209
|
+
flags: undefined,
|
|
1210
|
+
payloads: undefined,
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1200
1213
|
const flags = decideResponse?.featureFlags;
|
|
1201
1214
|
const payloads = decideResponse?.featureFlagPayloads;
|
|
1202
1215
|
let parsedPayloads = payloads;
|
|
@@ -1491,6 +1504,8 @@ class FeatureFlagsPoller {
|
|
|
1491
1504
|
...options
|
|
1492
1505
|
}) {
|
|
1493
1506
|
this.debugMode = false;
|
|
1507
|
+
this.lastRequestWasAuthenticationError = false;
|
|
1508
|
+
this.authenticationErrorCount = 0;
|
|
1494
1509
|
this.pollingInterval = pollingInterval;
|
|
1495
1510
|
this.personalApiKey = personalApiKey;
|
|
1496
1511
|
this.featureFlags = [];
|
|
@@ -1502,7 +1517,6 @@ class FeatureFlagsPoller {
|
|
|
1502
1517
|
this.projectApiKey = projectApiKey;
|
|
1503
1518
|
this.host = host;
|
|
1504
1519
|
this.poller = undefined;
|
|
1505
|
-
// NOTE: as any is required here as the AbortSignal typing is slightly misaligned but works just fine
|
|
1506
1520
|
this.fetch = options.fetch || fetch$1;
|
|
1507
1521
|
this.onError = options.onError;
|
|
1508
1522
|
this.customHeaders = customHeaders;
|
|
@@ -1722,16 +1736,45 @@ class FeatureFlagsPoller {
|
|
|
1722
1736
|
await this._loadFeatureFlags();
|
|
1723
1737
|
}
|
|
1724
1738
|
}
|
|
1739
|
+
/**
|
|
1740
|
+
* If a client is misconfigured with an invalid or improper API key, the polling interval is doubled each time
|
|
1741
|
+
* until a successful request is made, up to a maximum of 60 seconds.
|
|
1742
|
+
*
|
|
1743
|
+
* @returns The polling interval to use for the next request.
|
|
1744
|
+
*/
|
|
1745
|
+
getPollingInterval() {
|
|
1746
|
+
if (!this.lastRequestWasAuthenticationError) {
|
|
1747
|
+
return this.pollingInterval;
|
|
1748
|
+
}
|
|
1749
|
+
return Math.min(SIXTY_SECONDS, this.pollingInterval * 2 ** this.authenticationErrorCount);
|
|
1750
|
+
}
|
|
1725
1751
|
async _loadFeatureFlags() {
|
|
1726
1752
|
if (this.poller) {
|
|
1727
1753
|
clearTimeout(this.poller);
|
|
1728
1754
|
this.poller = undefined;
|
|
1729
1755
|
}
|
|
1730
|
-
this.poller = setTimeout(() => this._loadFeatureFlags(), this.
|
|
1756
|
+
this.poller = setTimeout(() => this._loadFeatureFlags(), this.getPollingInterval());
|
|
1731
1757
|
try {
|
|
1732
1758
|
const res = await this._requestFeatureFlagDefinitions();
|
|
1733
1759
|
if (res && res.status === 401) {
|
|
1734
|
-
|
|
1760
|
+
this.lastRequestWasAuthenticationError = true;
|
|
1761
|
+
this.authenticationErrorCount += 1;
|
|
1762
|
+
throw new ClientError(`Your project key or personal API key is invalid. Setting next polling interval to ${this.getPollingInterval()}ms. More information: https://posthog.com/docs/api#rate-limiting`);
|
|
1763
|
+
}
|
|
1764
|
+
if (res && res.status === 403) {
|
|
1765
|
+
this.lastRequestWasAuthenticationError = true;
|
|
1766
|
+
this.authenticationErrorCount += 1;
|
|
1767
|
+
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`);
|
|
1768
|
+
}
|
|
1769
|
+
if (res && res.status === 402) {
|
|
1770
|
+
// Quota limited - clear all flags
|
|
1771
|
+
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');
|
|
1772
|
+
this.featureFlags = [];
|
|
1773
|
+
this.featureFlagsByKey = {};
|
|
1774
|
+
this.groupTypeMapping = {};
|
|
1775
|
+
this.cohorts = {};
|
|
1776
|
+
this.loadedSuccessfullyOnce = false;
|
|
1777
|
+
return;
|
|
1735
1778
|
}
|
|
1736
1779
|
if (res && res.status !== 200) {
|
|
1737
1780
|
// something else went wrong, or the server is down.
|
|
@@ -1747,6 +1790,8 @@ class FeatureFlagsPoller {
|
|
|
1747
1790
|
this.groupTypeMapping = responseJson.group_type_mapping || {};
|
|
1748
1791
|
this.cohorts = responseJson.cohorts || [];
|
|
1749
1792
|
this.loadedSuccessfullyOnce = true;
|
|
1793
|
+
this.lastRequestWasAuthenticationError = false;
|
|
1794
|
+
this.authenticationErrorCount = 0;
|
|
1750
1795
|
} catch (err) {
|
|
1751
1796
|
// if an error that is not an instance of ClientError is thrown
|
|
1752
1797
|
// we silently ignore the error when reloading feature flags
|
|
@@ -2935,7 +2980,11 @@ class ErrorTracking {
|
|
|
2935
2980
|
}
|
|
2936
2981
|
}
|
|
2937
2982
|
|
|
2983
|
+
// Standard local evaluation rate limit is 600 per minute (10 per second),
|
|
2984
|
+
// so the fastest a poller should ever be set is 100ms.
|
|
2985
|
+
const MINIMUM_POLLING_INTERVAL = 100;
|
|
2938
2986
|
const THIRTY_SECONDS = 30 * 1000;
|
|
2987
|
+
const SIXTY_SECONDS = 60 * 1000;
|
|
2939
2988
|
const MAX_CACHE_SIZE = 50 * 1000;
|
|
2940
2989
|
// The actual exported Nodejs API.
|
|
2941
2990
|
class PostHog extends PostHogCoreStateless {
|
|
@@ -2943,9 +2992,13 @@ class PostHog extends PostHogCoreStateless {
|
|
|
2943
2992
|
super(apiKey, options);
|
|
2944
2993
|
this._memoryStorage = new PostHogMemoryStorage();
|
|
2945
2994
|
this.options = options;
|
|
2995
|
+
this.options.featureFlagsPollingInterval = typeof options.featureFlagsPollingInterval === 'number' ? Math.max(options.featureFlagsPollingInterval, MINIMUM_POLLING_INTERVAL) : THIRTY_SECONDS;
|
|
2946
2996
|
if (options.personalApiKey) {
|
|
2997
|
+
if (options.personalApiKey.includes('phc_')) {
|
|
2998
|
+
throw new Error('Your Personal API key is invalid. These keys are prefixed with "phx_" and can be created in PostHog project settings.');
|
|
2999
|
+
}
|
|
2947
3000
|
this.featureFlagsPoller = new FeatureFlagsPoller({
|
|
2948
|
-
pollingInterval:
|
|
3001
|
+
pollingInterval: this.options.featureFlagsPollingInterval,
|
|
2949
3002
|
personalApiKey: options.personalApiKey,
|
|
2950
3003
|
projectApiKey: apiKey,
|
|
2951
3004
|
timeout: options.requestTimeout ?? 10000,
|
|
@@ -3400,5 +3453,5 @@ function setupExpressErrorHandler(_posthog, app) {
|
|
|
3400
3453
|
});
|
|
3401
3454
|
}
|
|
3402
3455
|
|
|
3403
|
-
export { PostHog, PostHogSentryIntegration, createEventProcessor, sentryIntegration, setupExpressErrorHandler };
|
|
3456
|
+
export { MINIMUM_POLLING_INTERVAL, PostHog, PostHogSentryIntegration, SIXTY_SECONDS, THIRTY_SECONDS, createEventProcessor, sentryIntegration, setupExpressErrorHandler };
|
|
3404
3457
|
//# sourceMappingURL=index.esm.js.map
|