posthog-node 2.0.2 → 2.2.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 +11 -1
- package/lib/index.cjs.js +183 -32
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +22 -0
- package/lib/index.esm.js +182 -31
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +2 -0
- package/lib/posthog-core/src/types.d.ts +8 -1
- package/lib/posthog-node/src/feature-flags.d.ts +5 -3
- package/lib/posthog-node/src/fetch.d.ts +2 -0
- package/lib/posthog-node/src/posthog-node.d.ts +2 -1
- package/lib/posthog-node/src/types.d.ts +1 -0
- package/package.json +4 -4
- package/src/feature-flags.ts +84 -22
- package/src/fetch.ts +20 -0
- package/src/posthog-node.ts +6 -3
- package/src/types.ts +1 -0
- package/test/feature-flags.spec.ts +442 -115
- package/test/posthog-node.spec.ts +41 -40
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
// import { PostHog } from '../'
|
|
2
2
|
import { PostHog as PostHog } from '../src/posthog-node'
|
|
3
|
-
jest.mock('
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
jest.mock('../src/fetch')
|
|
4
|
+
import { fetch } from '../src/fetch'
|
|
5
|
+
import { anyDecideCall, anyLocalEvalCall, apiImplementation } from './feature-flags.spec'
|
|
6
6
|
import { waitForPromises } from '../../posthog-core/test/test-utils/test-utils'
|
|
7
7
|
|
|
8
8
|
jest.mock('../package.json', () => ({ version: '1.2.3' }))
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const mockedFetch = jest.mocked(fetch, true)
|
|
11
11
|
|
|
12
12
|
const getLastBatchEvents = (): any[] | undefined => {
|
|
13
|
-
expect(
|
|
14
|
-
'http://example.com/batch/',
|
|
15
|
-
expect.objectContaining({ method: 'POST' })
|
|
16
|
-
)
|
|
13
|
+
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.objectContaining({ method: 'POST' }))
|
|
17
14
|
|
|
18
|
-
const call =
|
|
15
|
+
const call = mockedFetch.mock.calls.find((x) => (x[0] as string).includes('/batch/'))
|
|
19
16
|
if (!call) {
|
|
20
17
|
return undefined
|
|
21
18
|
}
|
|
@@ -32,7 +29,7 @@ describe('PostHog Node.js', () => {
|
|
|
32
29
|
host: 'http://example.com',
|
|
33
30
|
})
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
mockedFetch.mockResolvedValue({
|
|
36
33
|
status: 200,
|
|
37
34
|
text: () => Promise.resolve('ok'),
|
|
38
35
|
json: () =>
|
|
@@ -49,7 +46,7 @@ describe('PostHog Node.js', () => {
|
|
|
49
46
|
|
|
50
47
|
describe('core methods', () => {
|
|
51
48
|
it('should capture an event to shared queue', async () => {
|
|
52
|
-
expect(
|
|
49
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
53
50
|
posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
|
|
54
51
|
|
|
55
52
|
jest.runOnlyPendingTimers()
|
|
@@ -67,7 +64,7 @@ describe('PostHog Node.js', () => {
|
|
|
67
64
|
})
|
|
68
65
|
|
|
69
66
|
it('shouldnt muddy subsequent capture calls', async () => {
|
|
70
|
-
expect(
|
|
67
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
71
68
|
posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
|
|
72
69
|
|
|
73
70
|
jest.runOnlyPendingTimers()
|
|
@@ -83,7 +80,7 @@ describe('PostHog Node.js', () => {
|
|
|
83
80
|
library_version: '1.2.3',
|
|
84
81
|
})
|
|
85
82
|
)
|
|
86
|
-
|
|
83
|
+
mockedFetch.mockClear()
|
|
87
84
|
|
|
88
85
|
posthog.capture({
|
|
89
86
|
distinctId: '123',
|
|
@@ -108,7 +105,7 @@ describe('PostHog Node.js', () => {
|
|
|
108
105
|
})
|
|
109
106
|
|
|
110
107
|
it('should capture identify events on shared queue', async () => {
|
|
111
|
-
expect(
|
|
108
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
112
109
|
posthog.identify({ distinctId: '123', properties: { foo: 'bar' } })
|
|
113
110
|
jest.runOnlyPendingTimers()
|
|
114
111
|
const batchEvents = getLastBatchEvents()
|
|
@@ -124,7 +121,7 @@ describe('PostHog Node.js', () => {
|
|
|
124
121
|
})
|
|
125
122
|
|
|
126
123
|
it('should capture alias events on shared queue', async () => {
|
|
127
|
-
expect(
|
|
124
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
128
125
|
posthog.alias({ distinctId: '123', alias: '1234' })
|
|
129
126
|
jest.runOnlyPendingTimers()
|
|
130
127
|
const batchEvents = getLastBatchEvents()
|
|
@@ -149,7 +146,7 @@ describe('PostHog Node.js', () => {
|
|
|
149
146
|
'feature-variant': 'variant',
|
|
150
147
|
}
|
|
151
148
|
|
|
152
|
-
|
|
149
|
+
mockedFetch.mockImplementation(apiImplementation({ decideFlags: mockFeatureFlags }))
|
|
153
150
|
|
|
154
151
|
posthog = new PostHog('TEST_API_KEY', {
|
|
155
152
|
host: 'http://example.com',
|
|
@@ -158,24 +155,24 @@ describe('PostHog Node.js', () => {
|
|
|
158
155
|
})
|
|
159
156
|
|
|
160
157
|
it('should do getFeatureFlag', async () => {
|
|
161
|
-
expect(
|
|
158
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
162
159
|
await expect(posthog.getFeatureFlag('feature-variant', '123', { groups: { org: '123' } })).resolves.toEqual(
|
|
163
160
|
'variant'
|
|
164
161
|
)
|
|
165
|
-
expect(
|
|
162
|
+
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
166
163
|
})
|
|
167
164
|
|
|
168
165
|
it('should do isFeatureEnabled', async () => {
|
|
169
|
-
expect(
|
|
166
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
170
167
|
await expect(posthog.isFeatureEnabled('feature-1', '123', { groups: { org: '123' } })).resolves.toEqual(true)
|
|
171
168
|
await expect(posthog.isFeatureEnabled('feature-4', '123', { groups: { org: '123' } })).resolves.toEqual(false)
|
|
172
|
-
expect(
|
|
169
|
+
expect(mockedFetch).toHaveBeenCalledTimes(2)
|
|
173
170
|
})
|
|
174
171
|
|
|
175
172
|
it('captures feature flags when no personal API key is present', async () => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
expect(
|
|
173
|
+
mockedFetch.mockClear()
|
|
174
|
+
mockedFetch.mockClear()
|
|
175
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
179
176
|
|
|
180
177
|
posthog = new PostHog('TEST_API_KEY', {
|
|
181
178
|
host: 'http://example.com',
|
|
@@ -188,7 +185,7 @@ describe('PostHog Node.js', () => {
|
|
|
188
185
|
sendFeatureFlags: true,
|
|
189
186
|
})
|
|
190
187
|
|
|
191
|
-
expect(
|
|
188
|
+
expect(mockedFetch).toHaveBeenCalledWith(
|
|
192
189
|
'http://example.com/decide/?v=2',
|
|
193
190
|
expect.objectContaining({ method: 'POST' })
|
|
194
191
|
)
|
|
@@ -213,7 +210,8 @@ describe('PostHog Node.js', () => {
|
|
|
213
210
|
)
|
|
214
211
|
|
|
215
212
|
// no calls to `/local_evaluation`
|
|
216
|
-
|
|
213
|
+
|
|
214
|
+
expect(mockedFetch).not.toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
217
215
|
})
|
|
218
216
|
|
|
219
217
|
it('manages memory well when sending feature flags', async () => {
|
|
@@ -235,9 +233,10 @@ describe('PostHog Node.js', () => {
|
|
|
235
233
|
},
|
|
236
234
|
],
|
|
237
235
|
}
|
|
238
|
-
mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
|
|
239
236
|
|
|
240
|
-
|
|
237
|
+
mockedFetch.mockImplementation(
|
|
238
|
+
apiImplementation({ localFlags: flags, decideFlags: { 'beta-feature': 'decide-fallback-value' } })
|
|
239
|
+
)
|
|
241
240
|
|
|
242
241
|
posthog = new PostHog('TEST_API_KEY', {
|
|
243
242
|
host: 'http://example.com',
|
|
@@ -267,7 +266,7 @@ describe('PostHog Node.js', () => {
|
|
|
267
266
|
}),
|
|
268
267
|
},
|
|
269
268
|
])
|
|
270
|
-
|
|
269
|
+
mockedFetch.mockClear()
|
|
271
270
|
|
|
272
271
|
expect(Object.keys(posthog.distinctIdHasSentFlagCalls).length <= 10).toEqual(true)
|
|
273
272
|
}
|
|
@@ -292,9 +291,10 @@ describe('PostHog Node.js', () => {
|
|
|
292
291
|
},
|
|
293
292
|
],
|
|
294
293
|
}
|
|
295
|
-
mockedUndici.request.mockImplementation(localEvaluationImplementation(flags))
|
|
296
294
|
|
|
297
|
-
|
|
295
|
+
mockedFetch.mockImplementation(
|
|
296
|
+
apiImplementation({ localFlags: flags, decideFlags: { 'decide-flag': 'decide-value' } })
|
|
297
|
+
)
|
|
298
298
|
|
|
299
299
|
posthog = new PostHog('TEST_API_KEY', {
|
|
300
300
|
host: 'http://example.com',
|
|
@@ -308,7 +308,7 @@ describe('PostHog Node.js', () => {
|
|
|
308
308
|
})
|
|
309
309
|
).toEqual(true)
|
|
310
310
|
jest.runOnlyPendingTimers()
|
|
311
|
-
expect(
|
|
311
|
+
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
312
312
|
|
|
313
313
|
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
314
314
|
expect.objectContaining({
|
|
@@ -323,7 +323,7 @@ describe('PostHog Node.js', () => {
|
|
|
323
323
|
}),
|
|
324
324
|
})
|
|
325
325
|
)
|
|
326
|
-
|
|
326
|
+
mockedFetch.mockClear()
|
|
327
327
|
|
|
328
328
|
// # called again for same user, shouldn't call capture again
|
|
329
329
|
expect(
|
|
@@ -333,7 +333,7 @@ describe('PostHog Node.js', () => {
|
|
|
333
333
|
).toEqual(true)
|
|
334
334
|
jest.runOnlyPendingTimers()
|
|
335
335
|
|
|
336
|
-
expect(
|
|
336
|
+
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
337
337
|
|
|
338
338
|
// # called for different user, should call capture again
|
|
339
339
|
expect(
|
|
@@ -343,7 +343,7 @@ describe('PostHog Node.js', () => {
|
|
|
343
343
|
})
|
|
344
344
|
).toEqual(true)
|
|
345
345
|
jest.runOnlyPendingTimers()
|
|
346
|
-
expect(
|
|
346
|
+
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
347
347
|
|
|
348
348
|
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
349
349
|
expect.objectContaining({
|
|
@@ -359,7 +359,7 @@ describe('PostHog Node.js', () => {
|
|
|
359
359
|
}),
|
|
360
360
|
})
|
|
361
361
|
)
|
|
362
|
-
|
|
362
|
+
mockedFetch.mockClear()
|
|
363
363
|
|
|
364
364
|
// # called for different user, but send configuration is false, so should NOT call capture again
|
|
365
365
|
expect(
|
|
@@ -369,7 +369,7 @@ describe('PostHog Node.js', () => {
|
|
|
369
369
|
})
|
|
370
370
|
).toEqual(true)
|
|
371
371
|
jest.runOnlyPendingTimers()
|
|
372
|
-
expect(
|
|
372
|
+
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
373
373
|
|
|
374
374
|
// # called for different flag, falls back to decide, should call capture again
|
|
375
375
|
expect(
|
|
@@ -380,7 +380,8 @@ describe('PostHog Node.js', () => {
|
|
|
380
380
|
).toEqual('decide-value')
|
|
381
381
|
jest.runOnlyPendingTimers()
|
|
382
382
|
// one to decide, one to batch
|
|
383
|
-
expect(
|
|
383
|
+
expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
|
|
384
|
+
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
384
385
|
|
|
385
386
|
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
386
387
|
expect.objectContaining({
|
|
@@ -396,7 +397,7 @@ describe('PostHog Node.js', () => {
|
|
|
396
397
|
}),
|
|
397
398
|
})
|
|
398
399
|
)
|
|
399
|
-
|
|
400
|
+
mockedFetch.mockClear()
|
|
400
401
|
|
|
401
402
|
expect(
|
|
402
403
|
await posthog.isFeatureEnabled('decide-flag', 'some-distinct-id2345', {
|
|
@@ -406,8 +407,8 @@ describe('PostHog Node.js', () => {
|
|
|
406
407
|
).toEqual(true)
|
|
407
408
|
jest.runOnlyPendingTimers()
|
|
408
409
|
// call decide, but not batch
|
|
409
|
-
expect(
|
|
410
|
-
expect(
|
|
410
|
+
expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
|
|
411
|
+
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
411
412
|
})
|
|
412
413
|
})
|
|
413
414
|
})
|