posthog-node 4.10.2 → 4.11.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 +10 -2
- package/lib/index.cjs.js +293 -64
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +73 -13
- package/lib/index.esm.js +275 -64
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/featureFlagUtils.d.ts +34 -0
- package/lib/posthog-core/src/index.d.ts +18 -7
- package/lib/posthog-core/src/types.d.ts +74 -6
- package/lib/posthog-node/src/crypto-helpers.d.ts +3 -0
- package/lib/posthog-node/src/crypto.d.ts +2 -0
- package/lib/posthog-node/src/feature-flags.d.ts +8 -8
- package/lib/posthog-node/src/lazy.d.ts +23 -0
- package/lib/posthog-node/src/posthog-node.d.ts +4 -3
- package/lib/posthog-node/src/types.d.ts +3 -3
- package/lib/posthog-node/test/test-utils.d.ts +2 -0
- package/package.json +1 -1
- package/src/crypto-helpers.ts +36 -0
- package/src/crypto.ts +22 -0
- package/src/feature-flags.ts +42 -41
- package/src/lazy.ts +55 -0
- package/src/posthog-node.ts +36 -17
- package/src/types.ts +3 -3
- package/test/crypto.spec.ts +36 -0
- package/test/feature-flags.decide.spec.ts +293 -0
- package/test/feature-flags.spec.ts +2 -2
- package/test/lazy.spec.ts +71 -0
- package/test/posthog-node.spec.ts +18 -18
- package/test/test-utils.ts +24 -1
- package/benchmarks/rusha-vs-native.mjs +0 -70
|
@@ -614,7 +614,7 @@ describe('PostHog Node.js', () => {
|
|
|
614
614
|
)
|
|
615
615
|
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
616
616
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
617
|
-
'http://example.com/decide/?v=
|
|
617
|
+
'http://example.com/decide/?v=4',
|
|
618
618
|
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
619
619
|
)
|
|
620
620
|
})
|
|
@@ -647,7 +647,7 @@ describe('PostHog Node.js', () => {
|
|
|
647
647
|
await waitForPromises()
|
|
648
648
|
|
|
649
649
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
650
|
-
'http://example.com/decide/?v=
|
|
650
|
+
'http://example.com/decide/?v=4',
|
|
651
651
|
expect.objectContaining({ method: 'POST' })
|
|
652
652
|
)
|
|
653
653
|
|
|
@@ -656,7 +656,7 @@ describe('PostHog Node.js', () => {
|
|
|
656
656
|
distinct_id: 'distinct_id',
|
|
657
657
|
event: 'node test event',
|
|
658
658
|
properties: expect.objectContaining({
|
|
659
|
-
$active_feature_flags: ['feature-1', 'feature-2', 'feature-
|
|
659
|
+
$active_feature_flags: ['feature-1', 'feature-2', 'feature-array', 'feature-variant'],
|
|
660
660
|
'$feature/feature-1': true,
|
|
661
661
|
'$feature/feature-2': true,
|
|
662
662
|
'$feature/feature-array': true,
|
|
@@ -672,7 +672,7 @@ describe('PostHog Node.js', () => {
|
|
|
672
672
|
|
|
673
673
|
expect(mockedFetch).not.toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
674
674
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
675
|
-
'http://example.com/decide/?v=
|
|
675
|
+
'http://example.com/decide/?v=4',
|
|
676
676
|
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
677
677
|
)
|
|
678
678
|
})
|
|
@@ -732,7 +732,7 @@ describe('PostHog Node.js', () => {
|
|
|
732
732
|
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
733
733
|
// no decide call
|
|
734
734
|
expect(mockedFetch).not.toHaveBeenCalledWith(
|
|
735
|
-
'http://example.com/decide/?v=
|
|
735
|
+
'http://example.com/decide/?v=4',
|
|
736
736
|
expect.objectContaining({ method: 'POST' })
|
|
737
737
|
)
|
|
738
738
|
|
|
@@ -790,7 +790,7 @@ describe('PostHog Node.js', () => {
|
|
|
790
790
|
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
791
791
|
// no decide call
|
|
792
792
|
expect(mockedFetch).not.toHaveBeenCalledWith(
|
|
793
|
-
'http://example.com/decide/?v=
|
|
793
|
+
'http://example.com/decide/?v=4',
|
|
794
794
|
expect.objectContaining({ method: 'POST' })
|
|
795
795
|
)
|
|
796
796
|
|
|
@@ -838,12 +838,12 @@ describe('PostHog Node.js', () => {
|
|
|
838
838
|
await waitForFlushTimer()
|
|
839
839
|
|
|
840
840
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
841
|
-
'http://example.com/decide/?v=
|
|
841
|
+
'http://example.com/decide/?v=4',
|
|
842
842
|
expect.objectContaining({ method: 'POST', body: expect.not.stringContaining('geoip_disable') })
|
|
843
843
|
)
|
|
844
844
|
|
|
845
845
|
expect(getLastBatchEvents()?.[0].properties).toEqual({
|
|
846
|
-
$active_feature_flags: ['feature-1', 'feature-2', 'feature-
|
|
846
|
+
$active_feature_flags: ['feature-1', 'feature-2', 'feature-array', 'feature-variant'],
|
|
847
847
|
'$feature/feature-1': true,
|
|
848
848
|
'$feature/feature-2': true,
|
|
849
849
|
'$feature/feature-array': true,
|
|
@@ -1091,7 +1091,7 @@ describe('PostHog Node.js', () => {
|
|
|
1091
1091
|
).resolves.toEqual(2)
|
|
1092
1092
|
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1093
1093
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1094
|
-
'http://example.com/decide/?v=
|
|
1094
|
+
'http://example.com/decide/?v=4',
|
|
1095
1095
|
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1096
1096
|
)
|
|
1097
1097
|
})
|
|
@@ -1133,7 +1133,7 @@ describe('PostHog Node.js', () => {
|
|
|
1133
1133
|
).resolves.toEqual([1])
|
|
1134
1134
|
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1135
1135
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1136
|
-
'http://example.com/decide/?v=
|
|
1136
|
+
'http://example.com/decide/?v=4',
|
|
1137
1137
|
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1138
1138
|
)
|
|
1139
1139
|
})
|
|
@@ -1153,7 +1153,7 @@ describe('PostHog Node.js', () => {
|
|
|
1153
1153
|
).resolves.toEqual(2)
|
|
1154
1154
|
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1155
1155
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1156
|
-
'http://example.com/decide/?v=
|
|
1156
|
+
'http://example.com/decide/?v=4',
|
|
1157
1157
|
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1158
1158
|
)
|
|
1159
1159
|
|
|
@@ -1162,7 +1162,7 @@ describe('PostHog Node.js', () => {
|
|
|
1162
1162
|
await expect(posthog.isFeatureEnabled('feature-variant', '123', { disableGeoip: false })).resolves.toEqual(true)
|
|
1163
1163
|
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1164
1164
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1165
|
-
'http://example.com/decide/?v=
|
|
1165
|
+
'http://example.com/decide/?v=4',
|
|
1166
1166
|
expect.objectContaining({ method: 'POST', body: expect.not.stringContaining('geoip_disable') })
|
|
1167
1167
|
)
|
|
1168
1168
|
})
|
|
@@ -1176,7 +1176,7 @@ describe('PostHog Node.js', () => {
|
|
|
1176
1176
|
jest.runOnlyPendingTimers()
|
|
1177
1177
|
|
|
1178
1178
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1179
|
-
'http://example.com/decide/?v=
|
|
1179
|
+
'http://example.com/decide/?v=4',
|
|
1180
1180
|
expect.objectContaining({
|
|
1181
1181
|
body: JSON.stringify({
|
|
1182
1182
|
token: 'TEST_API_KEY',
|
|
@@ -1206,7 +1206,7 @@ describe('PostHog Node.js', () => {
|
|
|
1206
1206
|
jest.runOnlyPendingTimers()
|
|
1207
1207
|
|
|
1208
1208
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1209
|
-
'http://example.com/decide/?v=
|
|
1209
|
+
'http://example.com/decide/?v=4',
|
|
1210
1210
|
expect.objectContaining({
|
|
1211
1211
|
body: JSON.stringify({
|
|
1212
1212
|
token: 'TEST_API_KEY',
|
|
@@ -1237,7 +1237,7 @@ describe('PostHog Node.js', () => {
|
|
|
1237
1237
|
jest.runOnlyPendingTimers()
|
|
1238
1238
|
|
|
1239
1239
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1240
|
-
'http://example.com/decide/?v=
|
|
1240
|
+
'http://example.com/decide/?v=4',
|
|
1241
1241
|
expect.objectContaining({
|
|
1242
1242
|
body: JSON.stringify({
|
|
1243
1243
|
token: 'TEST_API_KEY',
|
|
@@ -1261,7 +1261,7 @@ describe('PostHog Node.js', () => {
|
|
|
1261
1261
|
jest.runOnlyPendingTimers()
|
|
1262
1262
|
|
|
1263
1263
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1264
|
-
'http://example.com/decide/?v=
|
|
1264
|
+
'http://example.com/decide/?v=4',
|
|
1265
1265
|
expect.objectContaining({
|
|
1266
1266
|
body: JSON.stringify({
|
|
1267
1267
|
token: 'TEST_API_KEY',
|
|
@@ -1281,7 +1281,7 @@ describe('PostHog Node.js', () => {
|
|
|
1281
1281
|
jest.runOnlyPendingTimers()
|
|
1282
1282
|
|
|
1283
1283
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1284
|
-
'http://example.com/decide/?v=
|
|
1284
|
+
'http://example.com/decide/?v=4',
|
|
1285
1285
|
expect.objectContaining({
|
|
1286
1286
|
body: JSON.stringify({
|
|
1287
1287
|
token: 'TEST_API_KEY',
|
|
@@ -1303,7 +1303,7 @@ describe('PostHog Node.js', () => {
|
|
|
1303
1303
|
jest.runOnlyPendingTimers()
|
|
1304
1304
|
|
|
1305
1305
|
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1306
|
-
'http://example.com/decide/?v=
|
|
1306
|
+
'http://example.com/decide/?v=4',
|
|
1307
1307
|
expect.objectContaining({
|
|
1308
1308
|
body: JSON.stringify({
|
|
1309
1309
|
token: 'TEST_API_KEY',
|
package/test/test-utils.ts
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
import { PostHogV4DecideResponse } from 'posthog-core/src/types'
|
|
2
|
+
|
|
3
|
+
export const apiImplementationV4 = (decideResponse?: PostHogV4DecideResponse) => {
|
|
4
|
+
return (url: any): Promise<any> => {
|
|
5
|
+
if ((url as any).includes('/decide/?v=4')) {
|
|
6
|
+
return Promise.resolve({
|
|
7
|
+
status: 200,
|
|
8
|
+
text: () => Promise.resolve('ok'),
|
|
9
|
+
json: () => Promise.resolve(decideResponse),
|
|
10
|
+
}) as any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return Promise.resolve({
|
|
14
|
+
status: 400,
|
|
15
|
+
text: () => Promise.resolve('ok'),
|
|
16
|
+
json: () =>
|
|
17
|
+
Promise.resolve({
|
|
18
|
+
status: 'ok',
|
|
19
|
+
}),
|
|
20
|
+
}) as any
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
1
24
|
export const apiImplementation = ({
|
|
2
25
|
localFlags,
|
|
3
26
|
decideFlags,
|
|
@@ -68,4 +91,4 @@ export const anyLocalEvalCall = [
|
|
|
68
91
|
'http://example.com/api/feature_flag/local_evaluation?token=TEST_API_KEY&send_cohorts',
|
|
69
92
|
expect.any(Object),
|
|
70
93
|
]
|
|
71
|
-
export const anyDecideCall = ['http://example.com/decide/?v=
|
|
94
|
+
export const anyDecideCall = ['http://example.com/decide/?v=4', expect.any(Object)]
|
|
@@ -1,70 +0,0 @@
|
|
|
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
|