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 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 integration](https://posthog.com/docs/integrate/server/node) details.
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 rusha = require('rusha');
5
+ var node_crypto = require('node:crypto');
6
6
 
7
- var version = "4.3.1";
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
- // rusha is a fast sha1 implementation in pure javascript
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
- onlyEvaluateLocally = false,
2239
+ disableGeoip
2240
+ } = options || {};
2241
+ let {
2242
+ onlyEvaluateLocally,
2243
+ sendFeatureFlagEvents,
2239
2244
  personProperties,
2240
2245
  groupProperties
2241
2246
  } = options || {};
2242
- const {
2243
- allPersonProperties,
2244
- allGroupProperties
2245
- } = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
2246
- if (matchValue === undefined) {
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
- // Determine if the payload was evaluated locally
2263
- const payloadWasLocallyEvaluated = payload !== undefined;
2264
- // Fetch final flags and payloads either locally or from the remote server
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
- const finalResponse = fetchedOrLocalFlags[key];
2285
- const finalPayload = fetchedOrLocalPayloads[key];
2286
- const finalLocallyEvaluated = payloadWasLocallyEvaluated;
2287
- this.capture({
2288
- distinctId,
2289
- event: '$feature_flag_called',
2290
- properties: {
2291
- $feature_flag: key,
2292
- $feature_flag_response: finalResponse,
2293
- $feature_flag_payload: finalPayload,
2294
- locally_evaluated: finalLocallyEvaluated,
2295
- [`$feature/${key}`]: finalResponse
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);