posthog-node 3.0.0 → 3.1.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.
@@ -3,7 +3,7 @@ export declare function removeTrailingSlash(url: string): string;
3
3
  export interface RetriableOptions {
4
4
  retryCount?: number;
5
5
  retryDelay?: number;
6
- retryCheck?: (err: any) => true;
6
+ retryCheck?: (err: any) => boolean;
7
7
  }
8
8
  export declare function retriable<T>(fn: () => Promise<T>, props?: RetriableOptions): Promise<T>;
9
9
  export declare function generateUUID(globalThis?: any): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posthog-node",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "PostHog Node.js integration",
5
5
  "repository": "PostHog/posthog-node",
6
6
  "scripts": {
@@ -19,7 +19,8 @@
19
19
  "module": "lib/index.esm.js",
20
20
  "types": "lib/index.d.ts",
21
21
  "dependencies": {
22
- "axios": "^0.27.0"
22
+ "axios": "^0.27.0",
23
+ "rusha": "^0.8.14"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@types/node": "^18.0.0",
@@ -1,4 +1,4 @@
1
- import { createHash } from 'crypto'
1
+ import { createHash } from 'rusha'
2
2
  import { FeatureFlagCondition, FlagProperty, PostHogFeatureFlag, PropertyGroup } from './types'
3
3
  import { version } from '../package.json'
4
4
  import { JsonType, PostHogFetchOptions, PostHogFetchResponse } from 'posthog-core/src'
@@ -425,7 +425,7 @@ class FeatureFlagsPoller {
425
425
  try {
426
426
  return await this.fetch(url, options)
427
427
  } catch (err) {
428
- throw new Error(`Request failed with error: ${err}`)
428
+ throw err
429
429
  } finally {
430
430
  clearTimeout(abortTimeout)
431
431
  }
@@ -441,7 +441,8 @@ class FeatureFlagsPoller {
441
441
  // # uniformly distributed between 0 and 1, so if we want to show this feature to 20% of traffic
442
442
  // # we can do _hash(key, distinct_id) < 0.2
443
443
  function _hash(key: string, distinctId: string, salt: string = ''): number {
444
- const sha1Hash = createHash('sha1')
444
+ // rusha is a fast sha1 implementation in pure javascript
445
+ const sha1Hash = createHash()
445
446
  sha1Hash.update(`${key}.${distinctId}${salt}`)
446
447
  return parseInt(sha1Hash.digest('hex').slice(0, 15), 16) / LONG_SCALE
447
448
  }
package/src/fetch.ts CHANGED
@@ -10,11 +10,13 @@ export const fetch = async (url: string, options: PostHogFetchOptions): Promise<
10
10
  method: options.method.toLowerCase(),
11
11
  data: options.body,
12
12
  signal: options.signal,
13
+ // fetch only throws on network errors, not on HTTP errors
14
+ validateStatus: () => true,
13
15
  })
14
16
 
15
17
  return {
16
18
  status: res.status,
17
- text: () => res.data,
18
- json: () => res.data,
19
+ text: async () => res.data,
20
+ json: async () => res.data,
19
21
  }
20
22
  }
@@ -0,0 +1,23 @@
1
+ // Adjusted from type definitions for rusha 0.8
2
+ // Project: https://github.com/srijs/rusha#readme
3
+ // Definitions by: Jacopo Scazzosi <https://github.com/jacoscaz>
4
+ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5
+ // Minimum TypeScript Version: 4.0
6
+
7
+ /// <reference types="node" />
8
+
9
+ interface Hash {
10
+ update(value: string | number[] | ArrayBuffer | Buffer): Hash
11
+ digest(encoding?: undefined): ArrayBuffer
12
+ digest(encoding: 'hex'): string
13
+ }
14
+
15
+ interface Rusha {
16
+ createHash(): Hash
17
+ }
18
+
19
+ declare const Rusha: Rusha
20
+
21
+ declare module 'rusha' {
22
+ export = Rusha
23
+ }
@@ -1,6 +1,6 @@
1
- // import { PostHog } from '../'
1
+ // import { PostHog, PostHogOptions } from '../'
2
2
  // Uncomment below line while developing to not compile code everytime
3
- import { PostHog as PostHog } from '../src/posthog-node'
3
+ import { PostHog as PostHog, PostHogOptions } from '../src/posthog-node'
4
4
  import { matchProperty, InconclusiveMatchError } from '../src/feature-flags'
5
5
  jest.mock('../src/fetch')
6
6
  import { fetch } from '../src/fetch'
@@ -9,6 +9,10 @@ jest.spyOn(global.console, 'debug').mockImplementation()
9
9
 
10
10
  const mockedFetch = jest.mocked(fetch, true)
11
11
 
12
+ const posthogImmediateResolveOptions: PostHogOptions = {
13
+ fetchRetryCount: 0,
14
+ }
15
+
12
16
  export const apiImplementation = ({
13
17
  localFlags,
14
18
  decideFlags,
@@ -105,6 +109,7 @@ describe('local evaluation', () => {
105
109
  posthog = new PostHog('TEST_API_KEY', {
106
110
  host: 'http://example.com',
107
111
  personalApiKey: 'TEST_PERSONAL_API_KEY',
112
+ ...posthogImmediateResolveOptions,
108
113
  })
109
114
 
110
115
  expect(
@@ -151,6 +156,7 @@ describe('local evaluation', () => {
151
156
  posthog = new PostHog('TEST_API_KEY', {
152
157
  host: 'http://example.com',
153
158
  personalApiKey: 'TEST_PERSONAL_API_KEY',
159
+ ...posthogImmediateResolveOptions,
154
160
  })
155
161
 
156
162
  // # groups not passed in, hence false
@@ -231,6 +237,7 @@ describe('local evaluation', () => {
231
237
  posthog = new PostHog('TEST_API_KEY', {
232
238
  host: 'http://example.com',
233
239
  personalApiKey: 'TEST_PERSONAL_API_KEY',
240
+ ...posthogImmediateResolveOptions,
234
241
  })
235
242
  // # group_type_mappings not present, so fallback to `/decide`
236
243
  expect(
@@ -304,6 +311,7 @@ describe('local evaluation', () => {
304
311
  posthog = new PostHog('TEST_API_KEY', {
305
312
  host: 'http://example.com',
306
313
  personalApiKey: 'TEST_PERSONAL_API_KEY',
314
+ ...posthogImmediateResolveOptions,
307
315
  })
308
316
 
309
317
  expect(
@@ -426,6 +434,7 @@ describe('local evaluation', () => {
426
434
  posthog = new PostHog('TEST_API_KEY', {
427
435
  host: 'http://example.com',
428
436
  personalApiKey: 'TEST_PERSONAL_API_KEY',
437
+ ...posthogImmediateResolveOptions,
429
438
  })
430
439
 
431
440
  // # beta-feature fallbacks to decide because property type is unknown
@@ -488,6 +497,7 @@ describe('local evaluation', () => {
488
497
  posthog = new PostHog('TEST_API_KEY', {
489
498
  host: 'http://example.com',
490
499
  personalApiKey: 'TEST_PERSONAL_API_KEY',
500
+ ...posthogImmediateResolveOptions,
491
501
  })
492
502
 
493
503
  // # beta-feature should fallback to decide because property type is unknown
@@ -536,6 +546,7 @@ describe('local evaluation', () => {
536
546
  posthog = new PostHog('TEST_API_KEY', {
537
547
  host: 'http://example.com',
538
548
  personalApiKey: 'TEST_PERSONAL_API_KEY',
549
+ ...posthogImmediateResolveOptions,
539
550
  })
540
551
 
541
552
  // # beta-feature resolves to False
@@ -576,12 +587,20 @@ describe('local evaluation', () => {
576
587
  posthog = new PostHog('TEST_API_KEY', {
577
588
  host: 'http://example.com',
578
589
  personalApiKey: 'TEST_PERSONAL_API_KEY',
590
+ ...posthogImmediateResolveOptions,
591
+ })
592
+
593
+ let err: any = null
594
+ posthog.on('error', (e) => {
595
+ err = e
579
596
  })
580
597
 
581
598
  // # beta-feature2 falls back to decide, which on error returns undefined
582
599
  expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id')).toEqual(undefined)
583
600
  expect(await posthog.isFeatureEnabled('beta-feature2', 'some-distinct-id')).toEqual(undefined)
584
601
  expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
602
+ await posthog.shutdownAsync()
603
+ expect(err).toHaveProperty('name', 'PostHogFetchHttpError')
585
604
  })
586
605
 
587
606
  it('experience continuity flags are not evaluated locally', async () => {
@@ -612,6 +631,7 @@ describe('local evaluation', () => {
612
631
  posthog = new PostHog('TEST_API_KEY', {
613
632
  host: 'http://example.com',
614
633
  personalApiKey: 'TEST_PERSONAL_API_KEY',
634
+ ...posthogImmediateResolveOptions,
615
635
  })
616
636
 
617
637
  // # beta-feature2 falls back to decide, which on error returns default
@@ -680,6 +700,7 @@ describe('local evaluation', () => {
680
700
  posthog = new PostHog('TEST_API_KEY', {
681
701
  host: 'http://example.com',
682
702
  personalApiKey: 'TEST_PERSONAL_API_KEY',
703
+ ...posthogImmediateResolveOptions,
683
704
  })
684
705
 
685
706
  // # beta-feature value overridden by /decide
@@ -763,6 +784,7 @@ describe('local evaluation', () => {
763
784
  posthog = new PostHog('TEST_API_KEY', {
764
785
  host: 'http://example.com',
765
786
  personalApiKey: 'TEST_PERSONAL_API_KEY',
787
+ ...posthogImmediateResolveOptions,
766
788
  })
767
789
 
768
790
  // # beta-feature value overridden by /decide
@@ -835,6 +857,7 @@ describe('local evaluation', () => {
835
857
  posthog = new PostHog('TEST_API_KEY', {
836
858
  host: 'http://example.com',
837
859
  personalApiKey: 'TEST_PERSONAL_API_KEY',
860
+ ...posthogImmediateResolveOptions,
838
861
  })
839
862
 
840
863
  // # beta-feature2 has no value
@@ -916,6 +939,7 @@ describe('local evaluation', () => {
916
939
  posthog = new PostHog('TEST_API_KEY', {
917
940
  host: 'http://example.com',
918
941
  personalApiKey: 'TEST_PERSONAL_API_KEY',
942
+ ...posthogImmediateResolveOptions,
919
943
  })
920
944
 
921
945
  expect(
@@ -940,6 +964,7 @@ describe('local evaluation', () => {
940
964
  posthog = new PostHog('TEST_API_KEY', {
941
965
  host: 'http://example.com',
942
966
  personalApiKey: 'TEST_PERSONAL_API_KEY',
967
+ ...posthogImmediateResolveOptions,
943
968
  })
944
969
 
945
970
  expect(await posthog.getAllFlags('distinct-id')).toEqual({
@@ -965,6 +990,7 @@ describe('local evaluation', () => {
965
990
  posthog = new PostHog('TEST_API_KEY', {
966
991
  host: 'http://example.com',
967
992
  personalApiKey: 'TEST_PERSONAL_API_KEY',
993
+ ...posthogImmediateResolveOptions,
968
994
  })
969
995
 
970
996
  expect((await posthog.getAllFlagsAndPayloads('distinct-id')).featureFlagPayloads).toEqual({
@@ -1021,6 +1047,7 @@ describe('local evaluation', () => {
1021
1047
  posthog = new PostHog('TEST_API_KEY', {
1022
1048
  host: 'http://example.com',
1023
1049
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1050
+ ...posthogImmediateResolveOptions,
1024
1051
  })
1025
1052
 
1026
1053
  expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': true, 'disabled-feature': false })
@@ -1079,6 +1106,7 @@ describe('local evaluation', () => {
1079
1106
  posthog = new PostHog('TEST_API_KEY', {
1080
1107
  host: 'http://example.com',
1081
1108
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1109
+ ...posthogImmediateResolveOptions,
1082
1110
  })
1083
1111
 
1084
1112
  expect((await posthog.getAllFlagsAndPayloads('distinct-id')).featureFlagPayloads).toEqual({ 'beta-feature': 'new' })
@@ -1131,6 +1159,7 @@ describe('local evaluation', () => {
1131
1159
  posthog = new PostHog('TEST_API_KEY', {
1132
1160
  host: 'http://example.com',
1133
1161
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1162
+ ...posthogImmediateResolveOptions,
1134
1163
  })
1135
1164
 
1136
1165
  expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': true, 'disabled-feature': false })
@@ -1238,6 +1267,7 @@ describe('local evaluation', () => {
1238
1267
  posthog = new PostHog('TEST_API_KEY', {
1239
1268
  host: 'http://example.com',
1240
1269
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1270
+ ...posthogImmediateResolveOptions,
1241
1271
  })
1242
1272
 
1243
1273
  expect(
@@ -1319,6 +1349,7 @@ describe('local evaluation', () => {
1319
1349
  posthog = new PostHog('TEST_API_KEY', {
1320
1350
  host: 'http://example.com',
1321
1351
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1352
+ ...posthogImmediateResolveOptions,
1322
1353
  })
1323
1354
 
1324
1355
  expect(
@@ -1408,6 +1439,7 @@ describe('local evaluation', () => {
1408
1439
  posthog = new PostHog('TEST_API_KEY', {
1409
1440
  host: 'http://example.com',
1410
1441
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1442
+ ...posthogImmediateResolveOptions,
1411
1443
  })
1412
1444
 
1413
1445
  expect(
@@ -1489,6 +1521,7 @@ describe('local evaluation', () => {
1489
1521
  posthog = new PostHog('TEST_API_KEY', {
1490
1522
  host: 'http://example.com',
1491
1523
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1524
+ ...posthogImmediateResolveOptions,
1492
1525
  })
1493
1526
 
1494
1527
  expect(
@@ -1560,6 +1593,7 @@ describe('local evaluation', () => {
1560
1593
  posthog = new PostHog('TEST_API_KEY', {
1561
1594
  host: 'http://example.com',
1562
1595
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1596
+ ...posthogImmediateResolveOptions,
1563
1597
  })
1564
1598
 
1565
1599
  expect(
@@ -1632,6 +1666,7 @@ describe('local evaluation', () => {
1632
1666
  posthog = new PostHog('TEST_API_KEY', {
1633
1667
  host: 'http://example.com',
1634
1668
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1669
+ ...posthogImmediateResolveOptions,
1635
1670
  })
1636
1671
 
1637
1672
  expect(
@@ -1682,6 +1717,7 @@ describe('local evaluation', () => {
1682
1717
  posthog = new PostHog('TEST_API_KEY', {
1683
1718
  host: 'http://example.com',
1684
1719
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1720
+ ...posthogImmediateResolveOptions,
1685
1721
  })
1686
1722
 
1687
1723
  expect(
@@ -1762,6 +1798,7 @@ describe('local evaluation', () => {
1762
1798
  posthog = new PostHog('TEST_API_KEY', {
1763
1799
  host: 'http://example.com',
1764
1800
  personalApiKey: 'TEST_PERSONAL_API_KEY',
1801
+ ...posthogImmediateResolveOptions,
1765
1802
  })
1766
1803
 
1767
1804
  expect(
@@ -1990,10 +2027,6 @@ describe('consistency tests', () => {
1990
2027
  let posthog: PostHog
1991
2028
  jest.useFakeTimers()
1992
2029
 
1993
- afterEach(async () => {
1994
- await posthog.shutdownAsync()
1995
- })
1996
-
1997
2030
  it('is consistent for simple flags', () => {
1998
2031
  const flags = {
1999
2032
  flags: [
@@ -2015,6 +2048,7 @@ describe('consistency tests', () => {
2015
2048
  posthog = new PostHog('TEST_API_KEY', {
2016
2049
  host: 'http://example.com',
2017
2050
  personalApiKey: 'TEST_PERSONAL_API_KEY',
2051
+ ...posthogImmediateResolveOptions,
2018
2052
  })
2019
2053
 
2020
2054
  const results = [
@@ -3057,6 +3091,7 @@ describe('consistency tests', () => {
3057
3091
  posthog = new PostHog('TEST_API_KEY', {
3058
3092
  host: 'http://example.com',
3059
3093
  personalApiKey: 'TEST_PERSONAL_API_KEY',
3094
+ ...posthogImmediateResolveOptions,
3060
3095
  })
3061
3096
 
3062
3097
  const results = [
@@ -28,6 +28,7 @@ describe('PostHog Node.js', () => {
28
28
  beforeEach(() => {
29
29
  posthog = new PostHog('TEST_API_KEY', {
30
30
  host: 'http://example.com',
31
+ fetchRetryCount: 0,
31
32
  })
32
33
 
33
34
  mockedFetch.mockResolvedValue({
@@ -209,7 +210,6 @@ describe('PostHog Node.js', () => {
209
210
  host: 'http://example.com',
210
211
  disableGeoip: false,
211
212
  })
212
- client.debug()
213
213
  client.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
214
214
 
215
215
  jest.runOnlyPendingTimers()
@@ -266,8 +266,7 @@ describe('PostHog Node.js', () => {
266
266
  // a serverless posthog configuration
267
267
  posthog = new PostHog('TEST_API_KEY', {
268
268
  host: 'http://example.com',
269
- flushAt: 1,
270
- flushInterval: 0,
269
+ fetchRetryCount: 0,
271
270
  })
272
271
 
273
272
  mockedFetch.mockImplementation(async () => {
@@ -290,7 +289,13 @@ describe('PostHog Node.js', () => {
290
289
  })
291
290
 
292
291
  it('should shutdown cleanly', async () => {
293
- const logSpy = jest.spyOn(global.console, 'log')
292
+ posthog = new PostHog('TEST_API_KEY', {
293
+ host: 'http://example.com',
294
+ fetchRetryCount: 0,
295
+ flushAt: 1,
296
+ })
297
+
298
+ const logSpy = jest.spyOn(global.console, 'log').mockImplementation(() => {})
294
299
  jest.useRealTimers()
295
300
  // using debug mode to check console.log output
296
301
  // which tells us when the flush is complete
@@ -319,9 +324,10 @@ describe('PostHog Node.js', () => {
319
324
  })
320
325
 
321
326
  describe('groupIdentify', () => {
322
- it('should identify group with unique id', () => {
327
+ it('should identify group with unique id', async () => {
323
328
  posthog.groupIdentify({ groupType: 'posthog', groupKey: 'team-1', properties: { analytics: true } })
324
329
  jest.runOnlyPendingTimers()
330
+ await posthog.flushAsync()
325
331
  const batchEvents = getLastBatchEvents()
326
332
  expect(batchEvents).toMatchObject([
327
333
  {
@@ -338,7 +344,7 @@ describe('PostHog Node.js', () => {
338
344
  ])
339
345
  })
340
346
 
341
- it('should allow passing optional distinctID to identify group', () => {
347
+ it('should allow passing optional distinctID to identify group', async () => {
342
348
  posthog.groupIdentify({
343
349
  groupType: 'posthog',
344
350
  groupKey: 'team-1',
@@ -346,6 +352,7 @@ describe('PostHog Node.js', () => {
346
352
  distinctId: '123',
347
353
  })
348
354
  jest.runOnlyPendingTimers()
355
+ await posthog.flushAsync()
349
356
  const batchEvents = getLastBatchEvents()
350
357
  expect(batchEvents).toMatchObject([
351
358
  {
@@ -383,6 +390,7 @@ describe('PostHog Node.js', () => {
383
390
 
384
391
  posthog = new PostHog('TEST_API_KEY', {
385
392
  host: 'http://example.com',
393
+ fetchRetryCount: 0,
386
394
  })
387
395
  })
388
396
 
@@ -413,6 +421,7 @@ describe('PostHog Node.js', () => {
413
421
  posthog = new PostHog('TEST_API_KEY', {
414
422
  host: 'http://example.com',
415
423
  flushAt: 1,
424
+ fetchRetryCount: 0,
416
425
  })
417
426
 
418
427
  posthog.capture({
@@ -463,6 +472,7 @@ describe('PostHog Node.js', () => {
463
472
  posthog = new PostHog('TEST_API_KEY', {
464
473
  host: 'http://example.com',
465
474
  flushAt: 1,
475
+ fetchRetryCount: 0,
466
476
  })
467
477
 
468
478
  posthog.capture({
@@ -523,6 +533,7 @@ describe('PostHog Node.js', () => {
523
533
  host: 'http://example.com',
524
534
  personalApiKey: 'TEST_PERSONAL_API_KEY',
525
535
  maxCacheSize: 10,
536
+ fetchRetryCount: 0,
526
537
  })
527
538
 
528
539
  expect(Object.keys(posthog.distinctIdHasSentFlagCalls).length).toEqual(0)
@@ -581,6 +592,7 @@ describe('PostHog Node.js', () => {
581
592
  host: 'http://example.com',
582
593
  personalApiKey: 'TEST_PERSONAL_API_KEY',
583
594
  maxCacheSize: 10,
595
+ fetchRetryCount: 0,
584
596
  })
585
597
 
586
598
  expect(
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "extends": "../tsconfig.json",
3
3
  "compilerOptions": {
4
- "types": ["node"]
4
+ "types": ["node", "rusha"],
5
+ "typeRoots": ["./node_modules/@types", "../node_modules/@types", "./src/types"]
5
6
  }
6
7
  }