posthog-node 4.0.0 → 4.0.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.
- package/CHANGELOG.md +5 -0
- package/lib/index.cjs.js +28 -21
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.esm.js +28 -21
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/types.d.ts +1 -1
- package/lib/posthog-node/src/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/feature-flags.ts +20 -10
- package/src/posthog-node.ts +1 -6
- package/src/types.ts +1 -1
- package/test/posthog-node.spec.ts +69 -7
- package/test/test-utils.ts +3 -1
|
@@ -37,7 +37,7 @@ export declare type PostHogCoreOptions = {
|
|
|
37
37
|
featureFlagsRequestTimeoutMs?: number;
|
|
38
38
|
/** For Session Analysis how long before we expire a session (defaults to 30 mins) */
|
|
39
39
|
sessionExpirationTimeSeconds?: number;
|
|
40
|
-
/** Whether to post events to PostHog in JSON or compressed format */
|
|
40
|
+
/** Whether to post events to PostHog in JSON or compressed format. Defaults to 'form' */
|
|
41
41
|
captureMode?: 'json' | 'form';
|
|
42
42
|
disableGeoip?: boolean;
|
|
43
43
|
};
|
package/package.json
CHANGED
package/src/feature-flags.ts
CHANGED
|
@@ -148,11 +148,15 @@ class FeatureFlagsPoller {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// Undefined means a loading or missing data issue. Null means evaluation happened and there was no match
|
|
151
|
-
if (response === undefined) {
|
|
151
|
+
if (response === undefined || response === null) {
|
|
152
152
|
return null
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
try {
|
|
156
|
+
return JSON.parse(response)
|
|
157
|
+
} catch {
|
|
158
|
+
return response
|
|
159
|
+
}
|
|
156
160
|
}
|
|
157
161
|
|
|
158
162
|
async getAllFlagsAndPayloads(
|
|
@@ -308,7 +312,7 @@ class FeatureFlagsPoller {
|
|
|
308
312
|
let matches = false
|
|
309
313
|
|
|
310
314
|
if (propertyType === 'cohort') {
|
|
311
|
-
matches = matchCohort(prop, properties, this.cohorts)
|
|
315
|
+
matches = matchCohort(prop, properties, this.cohorts, this.debugMode)
|
|
312
316
|
} else {
|
|
313
317
|
matches = matchProperty(prop, properties)
|
|
314
318
|
}
|
|
@@ -558,7 +562,8 @@ function matchProperty(
|
|
|
558
562
|
function matchCohort(
|
|
559
563
|
property: FeatureFlagCondition['properties'][number],
|
|
560
564
|
propertyValues: Record<string, any>,
|
|
561
|
-
cohortProperties: FeatureFlagsPoller['cohorts']
|
|
565
|
+
cohortProperties: FeatureFlagsPoller['cohorts'],
|
|
566
|
+
debugMode: boolean = false
|
|
562
567
|
): boolean {
|
|
563
568
|
const cohortId = String(property.value)
|
|
564
569
|
if (!(cohortId in cohortProperties)) {
|
|
@@ -566,13 +571,14 @@ function matchCohort(
|
|
|
566
571
|
}
|
|
567
572
|
|
|
568
573
|
const propertyGroup = cohortProperties[cohortId]
|
|
569
|
-
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties)
|
|
574
|
+
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode)
|
|
570
575
|
}
|
|
571
576
|
|
|
572
577
|
function matchPropertyGroup(
|
|
573
578
|
propertyGroup: PropertyGroup,
|
|
574
579
|
propertyValues: Record<string, any>,
|
|
575
|
-
cohortProperties: FeatureFlagsPoller['cohorts']
|
|
580
|
+
cohortProperties: FeatureFlagsPoller['cohorts'],
|
|
581
|
+
debugMode: boolean = false
|
|
576
582
|
): boolean {
|
|
577
583
|
if (!propertyGroup) {
|
|
578
584
|
return true
|
|
@@ -592,7 +598,7 @@ function matchPropertyGroup(
|
|
|
592
598
|
// a nested property group
|
|
593
599
|
for (const prop of properties as PropertyGroup[]) {
|
|
594
600
|
try {
|
|
595
|
-
const matches = matchPropertyGroup(prop, propertyValues, cohortProperties)
|
|
601
|
+
const matches = matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode)
|
|
596
602
|
if (propertyGroupType === 'AND') {
|
|
597
603
|
if (!matches) {
|
|
598
604
|
return false
|
|
@@ -605,7 +611,9 @@ function matchPropertyGroup(
|
|
|
605
611
|
}
|
|
606
612
|
} catch (err) {
|
|
607
613
|
if (err instanceof InconclusiveMatchError) {
|
|
608
|
-
|
|
614
|
+
if (debugMode) {
|
|
615
|
+
console.debug(`Failed to compute property ${prop} locally: ${err}`)
|
|
616
|
+
}
|
|
609
617
|
errorMatchingLocally = true
|
|
610
618
|
} else {
|
|
611
619
|
throw err
|
|
@@ -623,7 +631,7 @@ function matchPropertyGroup(
|
|
|
623
631
|
try {
|
|
624
632
|
let matches: boolean
|
|
625
633
|
if (prop.type === 'cohort') {
|
|
626
|
-
matches = matchCohort(prop, propertyValues, cohortProperties)
|
|
634
|
+
matches = matchCohort(prop, propertyValues, cohortProperties, debugMode)
|
|
627
635
|
} else {
|
|
628
636
|
matches = matchProperty(prop, propertyValues)
|
|
629
637
|
}
|
|
@@ -649,7 +657,9 @@ function matchPropertyGroup(
|
|
|
649
657
|
}
|
|
650
658
|
} catch (err) {
|
|
651
659
|
if (err instanceof InconclusiveMatchError) {
|
|
652
|
-
|
|
660
|
+
if (debugMode) {
|
|
661
|
+
console.debug(`Failed to compute property ${prop} locally: ${err}`)
|
|
662
|
+
}
|
|
653
663
|
errorMatchingLocally = true
|
|
654
664
|
} else {
|
|
655
665
|
throw err
|
package/src/posthog-node.ts
CHANGED
|
@@ -340,12 +340,7 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
340
340
|
disableGeoip
|
|
341
341
|
)
|
|
342
342
|
}
|
|
343
|
-
|
|
344
|
-
try {
|
|
345
|
-
return JSON.parse(response as any)
|
|
346
|
-
} catch {
|
|
347
|
-
return response
|
|
348
|
-
}
|
|
343
|
+
return response
|
|
349
344
|
}
|
|
350
345
|
|
|
351
346
|
async isFeatureEnabled(
|
package/src/types.ts
CHANGED
|
@@ -466,11 +466,14 @@ describe('PostHog Node.js', () => {
|
|
|
466
466
|
'feature-2': true,
|
|
467
467
|
'feature-variant': 'variant',
|
|
468
468
|
'disabled-flag': false,
|
|
469
|
+
'feature-array': true,
|
|
469
470
|
}
|
|
470
471
|
|
|
472
|
+
// these are stringified in apiImplementation
|
|
471
473
|
const mockFeatureFlagPayloads = {
|
|
472
474
|
'feature-1': { color: 'blue' },
|
|
473
475
|
'feature-variant': 2,
|
|
476
|
+
'feature-array': [1],
|
|
474
477
|
}
|
|
475
478
|
|
|
476
479
|
const multivariateFlag = {
|
|
@@ -497,7 +500,7 @@ describe('PostHog Node.js', () => {
|
|
|
497
500
|
{ key: 'third-variant', name: 'Third Variant', rollout_percentage: 25 },
|
|
498
501
|
],
|
|
499
502
|
},
|
|
500
|
-
payloads: { 'first-variant': 'some-payload', 'third-variant': { a: 'json' } },
|
|
503
|
+
payloads: { 'first-variant': 'some-payload', 'third-variant': JSON.stringify({ a: 'json' }) },
|
|
501
504
|
},
|
|
502
505
|
}
|
|
503
506
|
const basicFlag = {
|
|
@@ -520,7 +523,7 @@ describe('PostHog Node.js', () => {
|
|
|
520
523
|
rollout_percentage: 100,
|
|
521
524
|
},
|
|
522
525
|
],
|
|
523
|
-
payloads: { true: 300 },
|
|
526
|
+
payloads: { true: '300' },
|
|
524
527
|
},
|
|
525
528
|
}
|
|
526
529
|
const falseFlag = {
|
|
@@ -536,7 +539,23 @@ describe('PostHog Node.js', () => {
|
|
|
536
539
|
rollout_percentage: 0,
|
|
537
540
|
},
|
|
538
541
|
],
|
|
539
|
-
payloads: { true: 300 },
|
|
542
|
+
payloads: { true: '300' },
|
|
543
|
+
},
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const arrayFlag = {
|
|
547
|
+
id: 5,
|
|
548
|
+
name: 'Beta Feature',
|
|
549
|
+
key: 'feature-array',
|
|
550
|
+
active: true,
|
|
551
|
+
filters: {
|
|
552
|
+
groups: [
|
|
553
|
+
{
|
|
554
|
+
properties: [],
|
|
555
|
+
rollout_percentage: 100,
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
payloads: { true: JSON.stringify([1]) },
|
|
540
559
|
},
|
|
541
560
|
}
|
|
542
561
|
|
|
@@ -544,7 +563,7 @@ describe('PostHog Node.js', () => {
|
|
|
544
563
|
apiImplementation({
|
|
545
564
|
decideFlags: mockFeatureFlags,
|
|
546
565
|
decideFlagPayloads: mockFeatureFlagPayloads,
|
|
547
|
-
localFlags: { flags: [multivariateFlag, basicFlag, falseFlag] },
|
|
566
|
+
localFlags: { flags: [multivariateFlag, basicFlag, falseFlag, arrayFlag] },
|
|
548
567
|
})
|
|
549
568
|
)
|
|
550
569
|
|
|
@@ -603,9 +622,10 @@ describe('PostHog Node.js', () => {
|
|
|
603
622
|
distinct_id: 'distinct_id',
|
|
604
623
|
event: 'node test event',
|
|
605
624
|
properties: expect.objectContaining({
|
|
606
|
-
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant'],
|
|
625
|
+
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant', 'feature-array'],
|
|
607
626
|
'$feature/feature-1': true,
|
|
608
627
|
'$feature/feature-2': true,
|
|
628
|
+
'$feature/feature-array': true,
|
|
609
629
|
'$feature/feature-variant': 'variant',
|
|
610
630
|
$lib: 'posthog-node',
|
|
611
631
|
$lib_version: '1.2.3',
|
|
@@ -659,8 +679,9 @@ describe('PostHog Node.js', () => {
|
|
|
659
679
|
distinct_id: 'distinct_id',
|
|
660
680
|
event: 'node test event',
|
|
661
681
|
properties: expect.objectContaining({
|
|
662
|
-
$active_feature_flags: ['beta-feature-local'],
|
|
682
|
+
$active_feature_flags: ['beta-feature-local', 'feature-array'],
|
|
663
683
|
'$feature/beta-feature-local': 'third-variant',
|
|
684
|
+
'$feature/feature-array': true,
|
|
664
685
|
'$feature/false-flag': false,
|
|
665
686
|
$lib: 'posthog-node',
|
|
666
687
|
$lib_version: '1.2.3',
|
|
@@ -756,9 +777,10 @@ describe('PostHog Node.js', () => {
|
|
|
756
777
|
)
|
|
757
778
|
|
|
758
779
|
expect(getLastBatchEvents()?.[0].properties).toEqual({
|
|
759
|
-
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant'],
|
|
780
|
+
$active_feature_flags: ['feature-1', 'feature-2', 'feature-variant', 'feature-array'],
|
|
760
781
|
'$feature/feature-1': true,
|
|
761
782
|
'$feature/feature-2': true,
|
|
783
|
+
'$feature/feature-array': true,
|
|
762
784
|
'$feature/disabled-flag': false,
|
|
763
785
|
'$feature/feature-variant': 'variant',
|
|
764
786
|
$lib: 'posthog-node',
|
|
@@ -1008,6 +1030,46 @@ describe('PostHog Node.js', () => {
|
|
|
1008
1030
|
)
|
|
1009
1031
|
})
|
|
1010
1032
|
|
|
1033
|
+
it('should not double parse json with getFeatureFlagPayloads and local eval', async () => {
|
|
1034
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1035
|
+
|
|
1036
|
+
posthog = new PostHog('TEST_API_KEY', {
|
|
1037
|
+
host: 'http://example.com',
|
|
1038
|
+
flushAt: 1,
|
|
1039
|
+
fetchRetryCount: 0,
|
|
1040
|
+
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
1041
|
+
})
|
|
1042
|
+
|
|
1043
|
+
mockedFetch.mockClear()
|
|
1044
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1045
|
+
|
|
1046
|
+
await expect(
|
|
1047
|
+
posthog.getFeatureFlagPayload('feature-array', '123', true, { onlyEvaluateLocally: true })
|
|
1048
|
+
).resolves.toEqual([1])
|
|
1049
|
+
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1050
|
+
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
1051
|
+
|
|
1052
|
+
mockedFetch.mockClear()
|
|
1053
|
+
|
|
1054
|
+
await expect(posthog.getFeatureFlagPayload('feature-array', '123')).resolves.toEqual([1])
|
|
1055
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1056
|
+
|
|
1057
|
+
await expect(posthog.getFeatureFlagPayload('false-flag', '123', true)).resolves.toEqual(300)
|
|
1058
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1059
|
+
})
|
|
1060
|
+
|
|
1061
|
+
it('should not double parse json with getFeatureFlagPayloads and server eval', async () => {
|
|
1062
|
+
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1063
|
+
await expect(
|
|
1064
|
+
posthog.getFeatureFlagPayload('feature-array', '123', undefined, { groups: { org: '123' } })
|
|
1065
|
+
).resolves.toEqual([1])
|
|
1066
|
+
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1067
|
+
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1068
|
+
'http://example.com/decide/?v=3',
|
|
1069
|
+
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1070
|
+
)
|
|
1071
|
+
})
|
|
1072
|
+
|
|
1011
1073
|
it('should do getFeatureFlagPayloads without matchValue', async () => {
|
|
1012
1074
|
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1013
1075
|
await expect(
|
package/test/test-utils.ts
CHANGED
|
@@ -20,7 +20,9 @@ export const apiImplementation = ({
|
|
|
20
20
|
} else {
|
|
21
21
|
return Promise.resolve({
|
|
22
22
|
featureFlags: decideFlags,
|
|
23
|
-
featureFlagPayloads:
|
|
23
|
+
featureFlagPayloads: Object.fromEntries(
|
|
24
|
+
Object.entries(decideFlagPayloads || {}).map(([k, v]) => [k, JSON.stringify(v)])
|
|
25
|
+
),
|
|
24
26
|
})
|
|
25
27
|
}
|
|
26
28
|
},
|