@posthog/core 1.41.0 → 1.42.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/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.mjs +2 -2
- package/dist/metrics/config.d.ts +9 -0
- package/dist/metrics/config.d.ts.map +1 -0
- package/dist/metrics/config.js +49 -0
- package/dist/metrics/config.mjs +15 -0
- package/dist/metrics/index.d.ts +8 -1
- package/dist/metrics/index.d.ts.map +1 -1
- package/dist/metrics/index.js +39 -19
- package/dist/metrics/index.mjs +36 -19
- package/dist/metrics/types.d.ts +1 -1
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/posthog-core-stateless.d.ts +9 -1
- package/dist/posthog-core-stateless.d.ts.map +1 -1
- package/dist/posthog-core-stateless.js +43 -0
- package/dist/posthog-core-stateless.mjs +43 -0
- package/dist/surveys/activation.d.ts +20 -0
- package/dist/surveys/activation.d.ts.map +1 -0
- package/dist/surveys/activation.js +45 -0
- package/dist/surveys/activation.mjs +8 -0
- package/dist/surveys/events.d.ts +1 -4
- package/dist/surveys/events.d.ts.map +1 -1
- package/dist/surveys/index.d.ts +2 -0
- package/dist/surveys/index.d.ts.map +1 -1
- package/dist/surveys/index.js +16 -2
- package/dist/surveys/index.mjs +3 -1
- package/dist/surveys/keys.d.ts +14 -0
- package/dist/surveys/keys.d.ts.map +1 -0
- package/dist/surveys/keys.js +45 -0
- package/dist/surveys/keys.mjs +8 -0
- package/dist/types.d.ts +87 -76
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +65 -76
- package/dist/types.mjs +66 -77
- package/package.json +2 -2
- package/src/index.ts +3 -0
- package/src/metrics/config.ts +23 -0
- package/src/metrics/index.spec.ts +49 -0
- package/src/metrics/index.ts +66 -24
- package/src/metrics/types.ts +1 -0
- package/src/posthog-core-stateless.ts +51 -1
- package/src/surveys/activation.spec.ts +61 -0
- package/src/surveys/activation.ts +28 -0
- package/src/surveys/events.ts +1 -5
- package/src/surveys/index.ts +2 -0
- package/src/surveys/keys.spec.ts +29 -0
- package/src/surveys/keys.ts +23 -0
- package/src/types.ts +88 -76
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals'
|
|
2
|
+
import { SurveySchedule } from '../types'
|
|
3
|
+
import { canSurveyActivateRepeatedly, doesSurveyActivateByEvent } from './activation'
|
|
4
|
+
|
|
5
|
+
describe('doesSurveyActivateByEvent', () => {
|
|
6
|
+
const cases: [string, Parameters<typeof doesSurveyActivateByEvent>[0], boolean][] = [
|
|
7
|
+
['non-empty event values', { conditions: { events: { values: [{ name: 'event' }] } } }, true],
|
|
8
|
+
['empty event values', { conditions: { events: { values: [] } } }, false],
|
|
9
|
+
['undefined conditions', { conditions: undefined }, false],
|
|
10
|
+
['null conditions', { conditions: null }, false],
|
|
11
|
+
['null events', { conditions: { events: null } }, false],
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
it.each(cases)('%s returns %p', (_name, survey, expected) => {
|
|
15
|
+
expect(doesSurveyActivateByEvent(survey)).toBe(expected)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('canSurveyActivateRepeatedly', () => {
|
|
20
|
+
const cases: [string, Parameters<typeof canSurveyActivateRepeatedly>[0], boolean][] = [
|
|
21
|
+
['always schedule without events', { schedule: SurveySchedule.Always }, true],
|
|
22
|
+
[
|
|
23
|
+
'events with repeated activation enabled',
|
|
24
|
+
{
|
|
25
|
+
schedule: SurveySchedule.Once,
|
|
26
|
+
conditions: { events: { repeatedActivation: true, values: [{ name: 'event' }] } },
|
|
27
|
+
},
|
|
28
|
+
true,
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
'events with repeated activation disabled',
|
|
32
|
+
{
|
|
33
|
+
schedule: SurveySchedule.Once,
|
|
34
|
+
conditions: { events: { repeatedActivation: false, values: [{ name: 'event' }] } },
|
|
35
|
+
},
|
|
36
|
+
false,
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
'events without repeated activation',
|
|
40
|
+
{
|
|
41
|
+
schedule: SurveySchedule.Once,
|
|
42
|
+
conditions: { events: { values: [{ name: 'event' }] } },
|
|
43
|
+
},
|
|
44
|
+
false,
|
|
45
|
+
],
|
|
46
|
+
[
|
|
47
|
+
'repeated activation without event values',
|
|
48
|
+
{
|
|
49
|
+
schedule: SurveySchedule.Once,
|
|
50
|
+
conditions: { events: { repeatedActivation: true, values: [] } },
|
|
51
|
+
},
|
|
52
|
+
false,
|
|
53
|
+
],
|
|
54
|
+
['undefined schedule without events', { schedule: undefined }, false],
|
|
55
|
+
['null schedule without events', { schedule: null }, false],
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
it.each(cases)('%s returns %p', (_name, survey, expected) => {
|
|
59
|
+
expect(canSurveyActivateRepeatedly(survey)).toBe(expected)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Survey, SurveySchedule } from '../types'
|
|
2
|
+
|
|
3
|
+
// conditions is narrowed to the fields these predicates actually read, so both SDKs'
|
|
4
|
+
// Survey shapes (which still drift in nested condition types) satisfy it structurally.
|
|
5
|
+
type SurveyForRepeatActivation = Pick<Survey, 'schedule'> & {
|
|
6
|
+
conditions?: {
|
|
7
|
+
events?: {
|
|
8
|
+
repeatedActivation?: boolean
|
|
9
|
+
values?: { name: string }[]
|
|
10
|
+
} | null
|
|
11
|
+
} | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function doesSurveyActivateByEvent(survey: SurveyForRepeatActivation): boolean {
|
|
15
|
+
return !!survey.conditions?.events?.values?.length
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Platform-independent part of "can this survey show again after being seen":
|
|
20
|
+
* event-repeated activation or an 'always' schedule. SDKs may OR in
|
|
21
|
+
* platform-specific state (e.g. the web SDK's in-progress partial responses).
|
|
22
|
+
*/
|
|
23
|
+
export function canSurveyActivateRepeatedly(survey: SurveyForRepeatActivation): boolean {
|
|
24
|
+
return (
|
|
25
|
+
(doesSurveyActivateByEvent(survey) && !!survey.conditions?.events?.repeatedActivation) ||
|
|
26
|
+
survey.schedule === SurveySchedule.Always
|
|
27
|
+
)
|
|
28
|
+
}
|
package/src/surveys/events.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SurveyWithIteration } from './keys'
|
|
1
2
|
import { SurveyResponses, SurveyResponseValue } from '../types'
|
|
2
3
|
import { isArray, isNullish, isUndefined } from '../utils'
|
|
3
4
|
|
|
@@ -73,8 +74,3 @@ type SurveyQuestionForResponses = {
|
|
|
73
74
|
type SurveyForResponses = {
|
|
74
75
|
questions: SurveyQuestionForResponses[]
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
-
type SurveyWithIteration = {
|
|
78
|
-
id: string
|
|
79
|
-
current_iteration?: number | null
|
|
80
|
-
}
|
package/src/surveys/index.ts
CHANGED
|
@@ -16,3 +16,5 @@ export {
|
|
|
16
16
|
getLanguageFromStoredPersonProperties,
|
|
17
17
|
normalizeLanguageCode,
|
|
18
18
|
} from './translations'
|
|
19
|
+
export { canSurveyActivateRepeatedly, doesSurveyActivateByEvent } from './activation'
|
|
20
|
+
export { getSurveyIterationKey, isSurveyKeyForSurvey, type SurveyWithIteration } from './keys'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals'
|
|
2
|
+
import { getSurveyIterationKey, isSurveyKeyForSurvey } from './keys'
|
|
3
|
+
|
|
4
|
+
describe('getSurveyIterationKey', () => {
|
|
5
|
+
const cases: [number | null | undefined, string][] = [
|
|
6
|
+
[2, 'survey-1_2'],
|
|
7
|
+
[1, 'survey-1_1'],
|
|
8
|
+
[0, 'survey-1'],
|
|
9
|
+
[null, 'survey-1'],
|
|
10
|
+
[undefined, 'survey-1'],
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
it.each(cases)('current_iteration %p produces key %p', (currentIteration, expected) => {
|
|
14
|
+
expect(getSurveyIterationKey({ id: 'survey-1', current_iteration: currentIteration })).toBe(expected)
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('isSurveyKeyForSurvey', () => {
|
|
19
|
+
it.each([
|
|
20
|
+
['bare id', 'abc', 'abc', true],
|
|
21
|
+
['first iteration', 'abc_1', 'abc', true],
|
|
22
|
+
['later iteration', 'abc_12', 'abc', true],
|
|
23
|
+
['different survey id', 'def_1', 'abc', false],
|
|
24
|
+
['prefix collision with bare id', 'abcd', 'abc', false],
|
|
25
|
+
['prefix collision with iteration key', 'abcd_1', 'abc', false],
|
|
26
|
+
])('%s: key %p for survey %p returns %p', (_name, key, surveyId, expected) => {
|
|
27
|
+
expect(isSurveyKeyForSurvey(key, surveyId)).toBe(expected)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Survey } from '../types'
|
|
2
|
+
|
|
3
|
+
export type SurveyWithIteration = Pick<Survey, 'id' | 'current_iteration'>
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* True when a stored display-state key belongs to the given survey: its bare id
|
|
7
|
+
* or any iteration-qualified `id_n` key.
|
|
8
|
+
*/
|
|
9
|
+
export function isSurveyKeyForSurvey(key: string, surveyId: string): boolean {
|
|
10
|
+
return key === surveyId || key.startsWith(`${surveyId}_`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Iteration-qualified survey identifier ('id' or 'id_iteration'), used to key
|
|
15
|
+
* per-survey display state (seen, in-progress, ...). Keying by iteration lets a
|
|
16
|
+
* repeating survey become visible again when a new iteration starts.
|
|
17
|
+
*/
|
|
18
|
+
export function getSurveyIterationKey(survey: SurveyWithIteration): string {
|
|
19
|
+
if (survey.current_iteration && survey.current_iteration > 0) {
|
|
20
|
+
return `${survey.id}_${survey.current_iteration}`
|
|
21
|
+
}
|
|
22
|
+
return survey.id
|
|
23
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -633,43 +633,49 @@ export type SurveyAppearance = {
|
|
|
633
633
|
widgetColor?: string
|
|
634
634
|
}
|
|
635
635
|
|
|
636
|
-
export
|
|
637
|
-
TopLeft
|
|
638
|
-
TopCenter
|
|
639
|
-
TopRight
|
|
640
|
-
MiddleLeft
|
|
641
|
-
MiddleCenter
|
|
642
|
-
MiddleRight
|
|
643
|
-
Left
|
|
644
|
-
Right
|
|
645
|
-
Center
|
|
646
|
-
}
|
|
636
|
+
export const SurveyPosition = {
|
|
637
|
+
TopLeft: 'top_left',
|
|
638
|
+
TopCenter: 'top_center',
|
|
639
|
+
TopRight: 'top_right',
|
|
640
|
+
MiddleLeft: 'middle_left',
|
|
641
|
+
MiddleCenter: 'middle_center',
|
|
642
|
+
MiddleRight: 'middle_right',
|
|
643
|
+
Left: 'left',
|
|
644
|
+
Right: 'right',
|
|
645
|
+
Center: 'center',
|
|
646
|
+
} as const
|
|
647
|
+
export type SurveyPosition = (typeof SurveyPosition)[keyof typeof SurveyPosition]
|
|
647
648
|
|
|
648
|
-
export
|
|
649
|
-
Button
|
|
650
|
-
Tab
|
|
651
|
-
Selector
|
|
652
|
-
}
|
|
649
|
+
export const SurveyWidgetType = {
|
|
650
|
+
Button: 'button',
|
|
651
|
+
Tab: 'tab',
|
|
652
|
+
Selector: 'selector',
|
|
653
|
+
} as const
|
|
654
|
+
export type SurveyWidgetType = (typeof SurveyWidgetType)[keyof typeof SurveyWidgetType]
|
|
653
655
|
|
|
654
|
-
export
|
|
655
|
-
Popover
|
|
656
|
-
API
|
|
657
|
-
Widget
|
|
658
|
-
ExternalSurvey
|
|
659
|
-
}
|
|
656
|
+
export const SurveyType = {
|
|
657
|
+
Popover: 'popover',
|
|
658
|
+
API: 'api',
|
|
659
|
+
Widget: 'widget',
|
|
660
|
+
ExternalSurvey: 'external_survey',
|
|
661
|
+
} as const
|
|
662
|
+
export type SurveyType = (typeof SurveyType)[keyof typeof SurveyType]
|
|
660
663
|
|
|
661
664
|
export type SurveyQuestion = BasicSurveyQuestion | LinkSurveyQuestion | RatingSurveyQuestion | MultipleSurveyQuestion
|
|
662
665
|
|
|
663
|
-
export
|
|
664
|
-
Html
|
|
665
|
-
Text
|
|
666
|
-
}
|
|
666
|
+
export const SurveyQuestionDescriptionContentType = {
|
|
667
|
+
Html: 'html',
|
|
668
|
+
Text: 'text',
|
|
669
|
+
} as const
|
|
670
|
+
export type SurveyQuestionDescriptionContentType =
|
|
671
|
+
(typeof SurveyQuestionDescriptionContentType)[keyof typeof SurveyQuestionDescriptionContentType]
|
|
667
672
|
|
|
668
673
|
// Survey validation types
|
|
669
|
-
export
|
|
670
|
-
MinLength
|
|
671
|
-
MaxLength
|
|
672
|
-
}
|
|
674
|
+
export const SurveyValidationType = {
|
|
675
|
+
MinLength: 'min_length',
|
|
676
|
+
MaxLength: 'max_length',
|
|
677
|
+
} as const
|
|
678
|
+
export type SurveyValidationType = (typeof SurveyValidationType)[keyof typeof SurveyValidationType]
|
|
673
679
|
|
|
674
680
|
export interface SurveyValidationRule {
|
|
675
681
|
type: SurveyValidationType
|
|
@@ -710,16 +716,16 @@ type SurveyQuestionBase = {
|
|
|
710
716
|
}
|
|
711
717
|
|
|
712
718
|
export type BasicSurveyQuestion = SurveyQuestionBase & {
|
|
713
|
-
type: SurveyQuestionType.Open
|
|
719
|
+
type: typeof SurveyQuestionType.Open
|
|
714
720
|
}
|
|
715
721
|
|
|
716
722
|
export type LinkSurveyQuestion = SurveyQuestionBase & {
|
|
717
|
-
type: SurveyQuestionType.Link
|
|
723
|
+
type: typeof SurveyQuestionType.Link
|
|
718
724
|
link?: string | null
|
|
719
725
|
}
|
|
720
726
|
|
|
721
727
|
export type RatingSurveyQuestion = SurveyQuestionBase & {
|
|
722
|
-
type: SurveyQuestionType.Rating
|
|
728
|
+
type: typeof SurveyQuestionType.Rating
|
|
723
729
|
display: SurveyRatingDisplay
|
|
724
730
|
scale: 2 | 3 | 5 | 7 | 10
|
|
725
731
|
lowerBoundLabel: string
|
|
@@ -727,49 +733,52 @@ export type RatingSurveyQuestion = SurveyQuestionBase & {
|
|
|
727
733
|
skipSubmitButton?: boolean
|
|
728
734
|
}
|
|
729
735
|
|
|
730
|
-
export
|
|
731
|
-
Number
|
|
732
|
-
Emoji
|
|
733
|
-
}
|
|
736
|
+
export const SurveyRatingDisplay = {
|
|
737
|
+
Number: 'number',
|
|
738
|
+
Emoji: 'emoji',
|
|
739
|
+
} as const
|
|
740
|
+
export type SurveyRatingDisplay = (typeof SurveyRatingDisplay)[keyof typeof SurveyRatingDisplay]
|
|
734
741
|
|
|
735
742
|
export type MultipleSurveyQuestion = SurveyQuestionBase & {
|
|
736
|
-
type: SurveyQuestionType.SingleChoice | SurveyQuestionType.MultipleChoice
|
|
743
|
+
type: typeof SurveyQuestionType.SingleChoice | typeof SurveyQuestionType.MultipleChoice
|
|
737
744
|
choices: string[]
|
|
738
745
|
hasOpenChoice?: boolean
|
|
739
746
|
shuffleOptions?: boolean
|
|
740
747
|
skipSubmitButton?: boolean
|
|
741
748
|
}
|
|
742
749
|
|
|
743
|
-
export
|
|
744
|
-
Open
|
|
745
|
-
MultipleChoice
|
|
746
|
-
SingleChoice
|
|
747
|
-
Rating
|
|
748
|
-
Link
|
|
749
|
-
}
|
|
750
|
+
export const SurveyQuestionType = {
|
|
751
|
+
Open: 'open',
|
|
752
|
+
MultipleChoice: 'multiple_choice',
|
|
753
|
+
SingleChoice: 'single_choice',
|
|
754
|
+
Rating: 'rating',
|
|
755
|
+
Link: 'link',
|
|
756
|
+
} as const
|
|
757
|
+
export type SurveyQuestionType = (typeof SurveyQuestionType)[keyof typeof SurveyQuestionType]
|
|
750
758
|
|
|
751
|
-
export
|
|
752
|
-
NextQuestion
|
|
753
|
-
End
|
|
754
|
-
ResponseBased
|
|
755
|
-
SpecificQuestion
|
|
756
|
-
}
|
|
759
|
+
export const SurveyQuestionBranchingType = {
|
|
760
|
+
NextQuestion: 'next_question',
|
|
761
|
+
End: 'end',
|
|
762
|
+
ResponseBased: 'response_based',
|
|
763
|
+
SpecificQuestion: 'specific_question',
|
|
764
|
+
} as const
|
|
765
|
+
export type SurveyQuestionBranchingType = (typeof SurveyQuestionBranchingType)[keyof typeof SurveyQuestionBranchingType]
|
|
757
766
|
|
|
758
767
|
export type NextQuestionBranching = {
|
|
759
|
-
type: SurveyQuestionBranchingType.NextQuestion
|
|
768
|
+
type: typeof SurveyQuestionBranchingType.NextQuestion
|
|
760
769
|
}
|
|
761
770
|
|
|
762
771
|
export type EndBranching = {
|
|
763
|
-
type: SurveyQuestionBranchingType.End
|
|
772
|
+
type: typeof SurveyQuestionBranchingType.End
|
|
764
773
|
}
|
|
765
774
|
|
|
766
775
|
export type ResponseBasedBranching = {
|
|
767
|
-
type: SurveyQuestionBranchingType.ResponseBased
|
|
776
|
+
type: typeof SurveyQuestionBranchingType.ResponseBased
|
|
768
777
|
responseValues: Record<string, any>
|
|
769
778
|
}
|
|
770
779
|
|
|
771
780
|
export type SpecificQuestionBranching = {
|
|
772
|
-
type: SurveyQuestionBranchingType.SpecificQuestion
|
|
781
|
+
type: typeof SurveyQuestionBranchingType.SpecificQuestion
|
|
773
782
|
index: number
|
|
774
783
|
}
|
|
775
784
|
|
|
@@ -783,20 +792,22 @@ export type SurveyResponses = Record<string, SurveyResponseValue>
|
|
|
783
792
|
|
|
784
793
|
export type SurveyCallback = (surveys: Survey[]) => void
|
|
785
794
|
|
|
786
|
-
export
|
|
787
|
-
Regex
|
|
788
|
-
NotRegex
|
|
789
|
-
Exact
|
|
790
|
-
IsNot
|
|
791
|
-
Icontains
|
|
792
|
-
NotIcontains
|
|
793
|
-
}
|
|
795
|
+
export const SurveyMatchType = {
|
|
796
|
+
Regex: 'regex',
|
|
797
|
+
NotRegex: 'not_regex',
|
|
798
|
+
Exact: 'exact',
|
|
799
|
+
IsNot: 'is_not',
|
|
800
|
+
Icontains: 'icontains',
|
|
801
|
+
NotIcontains: 'not_icontains',
|
|
802
|
+
} as const
|
|
803
|
+
export type SurveyMatchType = (typeof SurveyMatchType)[keyof typeof SurveyMatchType]
|
|
794
804
|
|
|
795
|
-
export
|
|
796
|
-
Once
|
|
797
|
-
Recurring
|
|
798
|
-
Always
|
|
799
|
-
}
|
|
805
|
+
export const SurveySchedule = {
|
|
806
|
+
Once: 'once',
|
|
807
|
+
Recurring: 'recurring',
|
|
808
|
+
Always: 'always',
|
|
809
|
+
} as const
|
|
810
|
+
export type SurveySchedule = (typeof SurveySchedule)[keyof typeof SurveySchedule]
|
|
800
811
|
|
|
801
812
|
export type SurveyElement = {
|
|
802
813
|
text?: string
|
|
@@ -853,9 +864,9 @@ export type Survey = {
|
|
|
853
864
|
}
|
|
854
865
|
start_date?: string
|
|
855
866
|
end_date?: string
|
|
856
|
-
current_iteration?: number
|
|
857
|
-
current_iteration_start_date?: string
|
|
858
|
-
schedule?: SurveySchedule
|
|
867
|
+
current_iteration?: number | null
|
|
868
|
+
current_iteration_start_date?: string | null
|
|
869
|
+
schedule?: SurveySchedule | null
|
|
859
870
|
}
|
|
860
871
|
|
|
861
872
|
export type SurveyActionType = {
|
|
@@ -865,11 +876,12 @@ export type SurveyActionType = {
|
|
|
865
876
|
}
|
|
866
877
|
|
|
867
878
|
/** Sync with plugin-server/src/types.ts */
|
|
868
|
-
export
|
|
869
|
-
Contains
|
|
870
|
-
Exact
|
|
871
|
-
Regex
|
|
872
|
-
}
|
|
879
|
+
export const ActionStepStringMatching = {
|
|
880
|
+
Contains: 'contains',
|
|
881
|
+
Exact: 'exact',
|
|
882
|
+
Regex: 'regex',
|
|
883
|
+
} as const
|
|
884
|
+
export type ActionStepStringMatching = (typeof ActionStepStringMatching)[keyof typeof ActionStepStringMatching]
|
|
873
885
|
|
|
874
886
|
export type ActionStepType = {
|
|
875
887
|
event?: string
|