posthog-node 4.3.1 → 4.4.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/README.md +1 -1
- package/benchmarks/rusha-vs-native.mjs +70 -0
- package/lib/index.cjs.js +34 -58
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +20 -5
- package/lib/index.esm.js +34 -58
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/types.d.ts +4 -1
- package/lib/posthog-node/src/types.d.ts +16 -4
- package/package.json +4 -4
- package/src/feature-flags.ts +2 -3
- package/src/posthog-node.ts +30 -51
- package/src/types.ts +16 -4
- package/test/posthog-node.spec.ts +2 -35
- package/tsconfig.json +2 -2
- package/src/types/rusha.d.ts +0 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Next
|
|
2
2
|
|
|
3
|
+
# 4.4.0 - 2025-01-15
|
|
4
|
+
|
|
5
|
+
Switch from rusha to native (node:crypto) sha1 implementation
|
|
6
|
+
|
|
7
|
+
# 4.3.2 - 2024-12-11
|
|
8
|
+
|
|
9
|
+
1. REVERT: Fix bug where this SDK incorrectly sent `$feature_flag_called` events with null values when using `getFeatureFlagPayload`.
|
|
10
|
+
|
|
3
11
|
# 4.3.1 - 2024-11-26
|
|
4
12
|
|
|
5
13
|
1. Fix bug where this SDK incorrectly sent `$feature_flag_called` events with null values when using `getFeatureFlagPayload`.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Please see the main [PostHog docs](https://www.posthog.com/docs).
|
|
4
4
|
|
|
5
|
-
Specifically, the [Node.js
|
|
5
|
+
Specifically, the [Node.js docs](https://posthog.com/docs/libraries/node) details.
|
|
6
6
|
|
|
7
7
|
## Questions?
|
|
8
8
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { bench, run, summary } from "mitata";
|
|
4
|
+
import { createHash } from "node:crypto"
|
|
5
|
+
import pkg from 'rusha';
|
|
6
|
+
const { createHash: createHashRusha } = pkg;
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line
|
|
9
|
+
const LONG_SCALE = 0xfffffffffffffff
|
|
10
|
+
|
|
11
|
+
// from https://github.com/PostHog/posthog-js-lite/blob/2baa794708d78d5d10940817c3768e47abe2da99/posthog-node/src/feature-flags.ts#L460-L465
|
|
12
|
+
function _hashRusha(key, distinctId, salt = '') {
|
|
13
|
+
// rusha is a fast sha1 implementation in pure javascript
|
|
14
|
+
const sha1Hash = createHashRusha()
|
|
15
|
+
sha1Hash.update(`${key}.${distinctId}${salt}`)
|
|
16
|
+
return parseInt(sha1Hash.digest('hex').slice(0, 15), 16) / LONG_SCALE
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function _hash(key, distinctId, salt = '') {
|
|
20
|
+
const sha1Hash = createHash("sha1")
|
|
21
|
+
sha1Hash.update(`${key}.${distinctId}${salt}`)
|
|
22
|
+
return parseInt(sha1Hash.digest('hex').slice(0, 15), 16) / LONG_SCALE
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
summary(() => {
|
|
26
|
+
bench("_hash with rusha", () => {
|
|
27
|
+
_hashRusha("test", "user_id")
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
bench("_hash with native", () => {
|
|
31
|
+
_hash("test", "user_id")
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await run();
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// !NODE
|
|
39
|
+
// node benchmarks/rusha-vs-native.mjs
|
|
40
|
+
// clk: ~3.99 GHz
|
|
41
|
+
// cpu: AMD Ryzen 7 7700 8-Core Processor
|
|
42
|
+
// runtime: node 22.11.0 (x64-linux)
|
|
43
|
+
|
|
44
|
+
// benchmark avg (min … max) p75 p99 (min … top 1%)
|
|
45
|
+
// ------------------------------------------- -------------------------------
|
|
46
|
+
// _hash with rusha 12.10 µs/iter 7.25 µs █
|
|
47
|
+
// (4.66 µs … 1.27 ms) 116.62 µs █▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
|
|
48
|
+
// _hash with native 547.04 ns/iter 435.45 ns █
|
|
49
|
+
// (370.08 ns … 3.61 µs) 3.58 µs █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
|
|
50
|
+
|
|
51
|
+
// summary
|
|
52
|
+
// _hash with native
|
|
53
|
+
// 22.12x faster than _hash with rusha
|
|
54
|
+
|
|
55
|
+
// !BUN
|
|
56
|
+
// bun benchmarks/rusha-vs-native.mjs
|
|
57
|
+
// clk: ~5.04 GHz
|
|
58
|
+
// cpu: AMD Ryzen 7 7700 8-Core Processor
|
|
59
|
+
// runtime: bun 1.1.37 (x64-linux)
|
|
60
|
+
|
|
61
|
+
// benchmark avg (min … max) p75 p99 (min … top 1%)
|
|
62
|
+
// ------------------------------------------- -------------------------------
|
|
63
|
+
// _hash with rusha 10.00 µs/iter 4.96 µs █
|
|
64
|
+
// (2.60 µs … 2.78 ms) 45.85 µs ▂█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
|
|
65
|
+
// _hash with native 471.82 ns/iter 420.00 ns █
|
|
66
|
+
// (370.00 ns … 949.79 µs) 1.99 µs █▆▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
|
|
67
|
+
|
|
68
|
+
// summary
|
|
69
|
+
// _hash with native
|
|
70
|
+
// 21.19x faster than _hash with rusha
|
package/lib/index.cjs.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var node_crypto = require('node:crypto');
|
|
6
6
|
|
|
7
|
-
var version = "4.
|
|
7
|
+
var version = "4.4.0";
|
|
8
8
|
|
|
9
9
|
var PostHogPersistedProperty;
|
|
10
10
|
(function (PostHogPersistedProperty) {
|
|
@@ -13,6 +13,8 @@ var PostHogPersistedProperty;
|
|
|
13
13
|
PostHogPersistedProperty["Props"] = "props";
|
|
14
14
|
PostHogPersistedProperty["FeatureFlags"] = "feature_flags";
|
|
15
15
|
PostHogPersistedProperty["FeatureFlagPayloads"] = "feature_flag_payloads";
|
|
16
|
+
PostHogPersistedProperty["BootstrapFeatureFlags"] = "bootstrap_feature_flags";
|
|
17
|
+
PostHogPersistedProperty["BootstrapFeatureFlagPayloads"] = "bootstrap_feature_flag_payloads";
|
|
16
18
|
PostHogPersistedProperty["OverrideFeatureFlags"] = "override_feature_flags";
|
|
17
19
|
PostHogPersistedProperty["Queue"] = "queue";
|
|
18
20
|
PostHogPersistedProperty["OptedOut"] = "opted_out";
|
|
@@ -23,6 +25,7 @@ var PostHogPersistedProperty;
|
|
|
23
25
|
PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
|
|
24
26
|
PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
|
|
25
27
|
PostHogPersistedProperty["SessionReplay"] = "session_replay";
|
|
28
|
+
PostHogPersistedProperty["DecideEndpointWasHit"] = "decide_endpoint_was_hit";
|
|
26
29
|
})(PostHogPersistedProperty || (PostHogPersistedProperty = {}));
|
|
27
30
|
|
|
28
31
|
function assert(truthyValue, message) {
|
|
@@ -1786,8 +1789,7 @@ class FeatureFlagsPoller {
|
|
|
1786
1789
|
// # uniformly distributed between 0 and 1, so if we want to show this feature to 20% of traffic
|
|
1787
1790
|
// # we can do _hash(key, distinct_id) < 0.2
|
|
1788
1791
|
function _hash(key, distinctId, salt = '') {
|
|
1789
|
-
|
|
1790
|
-
const sha1Hash = rusha.createHash();
|
|
1792
|
+
const sha1Hash = node_crypto.createHash('sha1');
|
|
1791
1793
|
sha1Hash.update(`${key}.${distinctId}${salt}`);
|
|
1792
1794
|
return parseInt(sha1Hash.digest('hex').slice(0, 15), 16) / LONG_SCALE;
|
|
1793
1795
|
}
|
|
@@ -2234,70 +2236,44 @@ class PostHog extends PostHogCoreStateless {
|
|
|
2234
2236
|
async getFeatureFlagPayload(key, distinctId, matchValue, options) {
|
|
2235
2237
|
const {
|
|
2236
2238
|
groups,
|
|
2237
|
-
disableGeoip
|
|
2238
|
-
|
|
2239
|
+
disableGeoip
|
|
2240
|
+
} = options || {};
|
|
2241
|
+
let {
|
|
2242
|
+
onlyEvaluateLocally,
|
|
2243
|
+
sendFeatureFlagEvents,
|
|
2239
2244
|
personProperties,
|
|
2240
2245
|
groupProperties
|
|
2241
2246
|
} = options || {};
|
|
2242
|
-
const
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
+
const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
|
|
2248
|
+
personProperties = adjustedProperties.allPersonProperties;
|
|
2249
|
+
groupProperties = adjustedProperties.allGroupProperties;
|
|
2250
|
+
let response = undefined;
|
|
2251
|
+
// Try to get match value locally if not provided
|
|
2252
|
+
if (!matchValue) {
|
|
2247
2253
|
matchValue = await this.getFeatureFlag(key, distinctId, {
|
|
2248
2254
|
...options,
|
|
2249
|
-
onlyEvaluateLocally: true
|
|
2250
|
-
sendFeatureFlagEvents: false
|
|
2255
|
+
onlyEvaluateLocally: true
|
|
2251
2256
|
});
|
|
2252
2257
|
}
|
|
2253
|
-
let response;
|
|
2254
|
-
let payload;
|
|
2255
2258
|
if (matchValue) {
|
|
2256
|
-
response = matchValue;
|
|
2257
|
-
payload = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue);
|
|
2258
|
-
} else {
|
|
2259
|
-
response = undefined;
|
|
2260
|
-
payload = undefined;
|
|
2259
|
+
response = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue);
|
|
2261
2260
|
}
|
|
2262
|
-
//
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
let fetchedOrLocalFlags;
|
|
2266
|
-
let fetchedOrLocalPayloads;
|
|
2267
|
-
if (payloadWasLocallyEvaluated || onlyEvaluateLocally) {
|
|
2268
|
-
if (response !== undefined) {
|
|
2269
|
-
fetchedOrLocalFlags = {
|
|
2270
|
-
[key]: response
|
|
2271
|
-
};
|
|
2272
|
-
fetchedOrLocalPayloads = {
|
|
2273
|
-
[key]: payload
|
|
2274
|
-
};
|
|
2275
|
-
} else {
|
|
2276
|
-
fetchedOrLocalFlags = {};
|
|
2277
|
-
fetchedOrLocalPayloads = {};
|
|
2278
|
-
}
|
|
2279
|
-
} else {
|
|
2280
|
-
const fetchedData = await super.getFeatureFlagsAndPayloadsStateless(distinctId, groups, allPersonProperties, allGroupProperties, disableGeoip);
|
|
2281
|
-
fetchedOrLocalFlags = fetchedData.flags || {};
|
|
2282
|
-
fetchedOrLocalPayloads = fetchedData.payloads || {};
|
|
2261
|
+
// set defaults
|
|
2262
|
+
if (onlyEvaluateLocally == undefined) {
|
|
2263
|
+
onlyEvaluateLocally = false;
|
|
2283
2264
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
},
|
|
2297
|
-
groups,
|
|
2298
|
-
disableGeoip
|
|
2299
|
-
});
|
|
2300
|
-
return finalPayload;
|
|
2265
|
+
if (sendFeatureFlagEvents == undefined) {
|
|
2266
|
+
sendFeatureFlagEvents = true;
|
|
2267
|
+
}
|
|
2268
|
+
// set defaults
|
|
2269
|
+
if (onlyEvaluateLocally == undefined) {
|
|
2270
|
+
onlyEvaluateLocally = false;
|
|
2271
|
+
}
|
|
2272
|
+
const payloadWasLocallyEvaluated = response !== undefined;
|
|
2273
|
+
if (!payloadWasLocallyEvaluated && !onlyEvaluateLocally) {
|
|
2274
|
+
response = await super.getFeatureFlagPayloadStateless(key, distinctId, groups, personProperties, groupProperties, disableGeoip);
|
|
2275
|
+
}
|
|
2276
|
+
return response;
|
|
2301
2277
|
}
|
|
2302
2278
|
async isFeatureEnabled(key, distinctId, options) {
|
|
2303
2279
|
const feat = await this.getFeatureFlag(key, distinctId, options);
|