posthog-node 5.0.0-alpha.1 → 5.1.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/lib/edge/index.cjs +3455 -0
  3. package/lib/edge/index.cjs.map +1 -0
  4. package/lib/edge/index.mjs +3429 -0
  5. package/lib/edge/index.mjs.map +1 -0
  6. package/lib/index.d.ts +938 -810
  7. package/lib/{index.cjs.js → node/index.cjs} +2910 -3107
  8. package/lib/node/index.cjs.map +1 -0
  9. package/lib/{index.esm.js → node/index.mjs} +2911 -3105
  10. package/lib/node/index.mjs.map +1 -0
  11. package/package.json +33 -6
  12. package/index.ts +0 -3
  13. package/lib/index.cjs.js.map +0 -1
  14. package/lib/index.esm.js.map +0 -1
  15. package/lib/posthog-core/src/eventemitter.d.ts +0 -8
  16. package/lib/posthog-core/src/featureFlagUtils.d.ts +0 -34
  17. package/lib/posthog-core/src/index.d.ts +0 -242
  18. package/lib/posthog-core/src/lz-string.d.ts +0 -8
  19. package/lib/posthog-core/src/storage-memory.d.ts +0 -6
  20. package/lib/posthog-core/src/types.d.ts +0 -422
  21. package/lib/posthog-core/src/utils.d.ts +0 -19
  22. package/lib/posthog-core/src/vendor/uuidv7.d.ts +0 -179
  23. package/lib/posthog-node/index.d.ts +0 -3
  24. package/lib/posthog-node/src/crypto-helpers.d.ts +0 -3
  25. package/lib/posthog-node/src/crypto.d.ts +0 -2
  26. package/lib/posthog-node/src/error-tracking.d.ts +0 -12
  27. package/lib/posthog-node/src/extensions/error-tracking/autocapture.d.ts +0 -3
  28. package/lib/posthog-node/src/extensions/error-tracking/context-lines.d.ts +0 -6
  29. package/lib/posthog-node/src/extensions/error-tracking/error-conversion.d.ts +0 -2
  30. package/lib/posthog-node/src/extensions/error-tracking/reduceable-cache.d.ts +0 -12
  31. package/lib/posthog-node/src/extensions/error-tracking/stack-trace.d.ts +0 -15
  32. package/lib/posthog-node/src/extensions/error-tracking/type-checking.d.ts +0 -7
  33. package/lib/posthog-node/src/extensions/error-tracking/types.d.ts +0 -57
  34. package/lib/posthog-node/src/extensions/express.d.ts +0 -17
  35. package/lib/posthog-node/src/extensions/sentry-integration.d.ts +0 -51
  36. package/lib/posthog-node/src/feature-flags.d.ts +0 -84
  37. package/lib/posthog-node/src/lazy.d.ts +0 -23
  38. package/lib/posthog-node/src/posthog-node.d.ts +0 -91
  39. package/lib/posthog-node/src/types.d.ts +0 -203
  40. package/lib/posthog-node/test/test-utils.d.ts +0 -17
  41. package/src/crypto-helpers.ts +0 -36
  42. package/src/crypto.ts +0 -22
  43. package/src/error-tracking.ts +0 -67
  44. package/src/extensions/error-tracking/autocapture.ts +0 -65
  45. package/src/extensions/error-tracking/context-lines.ts +0 -425
  46. package/src/extensions/error-tracking/error-conversion.ts +0 -262
  47. package/src/extensions/error-tracking/reduceable-cache.ts +0 -39
  48. package/src/extensions/error-tracking/stack-trace.ts +0 -269
  49. package/src/extensions/error-tracking/type-checking.ts +0 -40
  50. package/src/extensions/error-tracking/types.ts +0 -65
  51. package/src/extensions/express.ts +0 -37
  52. package/src/extensions/sentry-integration.ts +0 -196
  53. package/src/feature-flags.ts +0 -863
  54. package/src/lazy.ts +0 -55
  55. package/src/posthog-node.ts +0 -567
  56. package/src/types.ts +0 -231
  57. package/test/crypto.spec.ts +0 -36
  58. package/test/extensions/error-conversion.spec.ts +0 -44
  59. package/test/extensions/sentry-integration.spec.ts +0 -162
  60. package/test/feature-flags.decide.spec.ts +0 -378
  61. package/test/feature-flags.spec.ts +0 -4681
  62. package/test/lazy.spec.ts +0 -71
  63. package/test/posthog-node.spec.ts +0 -1341
  64. package/test/test-utils.ts +0 -106
  65. package/tsconfig.json +0 -7
package/src/types.ts DELETED
@@ -1,231 +0,0 @@
1
- import { FeatureFlagValue, JsonType } from '../../posthog-core/src'
2
-
3
- export interface IdentifyMessage {
4
- distinctId: string
5
- properties?: Record<string | number, any>
6
- disableGeoip?: boolean
7
- }
8
-
9
- export interface EventMessage extends IdentifyMessage {
10
- event: string
11
- groups?: Record<string, string | number> // Mapping of group type to group id
12
- sendFeatureFlags?: boolean
13
- timestamp?: Date
14
- uuid?: string
15
- }
16
-
17
- export interface GroupIdentifyMessage {
18
- groupType: string
19
- groupKey: string // Unique identifier for the group
20
- properties?: Record<string | number, any>
21
- distinctId?: string // optional distinctId to associate message with a person
22
- disableGeoip?: boolean
23
- }
24
-
25
- export type PropertyGroup = {
26
- type: 'AND' | 'OR'
27
- values: PropertyGroup[] | FlagProperty[]
28
- }
29
-
30
- export type FlagProperty = {
31
- key: string
32
- type?: string
33
- value: string | number | (string | number)[]
34
- operator?: string
35
- negation?: boolean
36
- }
37
-
38
- export type FeatureFlagCondition = {
39
- properties: FlagProperty[]
40
- rollout_percentage?: number
41
- variant?: string
42
- }
43
-
44
- export type PostHogFeatureFlag = {
45
- id: number
46
- name: string
47
- key: string
48
- filters?: {
49
- aggregation_group_type_index?: number
50
- groups?: FeatureFlagCondition[]
51
- multivariate?: {
52
- variants: {
53
- key: string
54
- rollout_percentage: number
55
- }[]
56
- }
57
- payloads?: Record<string, string>
58
- }
59
- deleted: boolean
60
- active: boolean
61
- /** @deprecated This field will be removed in a future version. **/
62
- is_simple_flag: boolean
63
- rollout_percentage: null | number
64
- ensure_experience_continuity: boolean
65
- experiment_set: number[]
66
- }
67
-
68
- export type PostHogNodeV1 = {
69
- /**
70
- * @description Capture allows you to capture anything a user does within your system,
71
- * which you can later use in PostHog to find patterns in usage,
72
- * work out which features to improve or where people are giving up.
73
- * A capture call requires:
74
- * @param distinctId which uniquely identifies your user
75
- * @param event We recommend using [verb] [noun], like movie played or movie updated to easily identify what your events mean later on.
76
- * @param properties OPTIONAL | which can be a object with any information you'd like to add
77
- * @param groups OPTIONAL | object of what groups are related to this event, example: { company: 'id:5' }. Can be used to analyze companies instead of users.
78
- * @param sendFeatureFlags OPTIONAL | Used with experiments. Determines whether to send feature flag values with the event.
79
- */
80
- capture({ distinctId, event, properties, groups, sendFeatureFlags }: EventMessage): void
81
-
82
- /**
83
- * @description Identify lets you add metadata on your users so you can more easily identify who they are in PostHog,
84
- * and even do things like segment users by these properties.
85
- * An identify call requires:
86
- * @param distinctId which uniquely identifies your user
87
- * @param properties with a dict with any key: value pairs
88
- */
89
- identify({ distinctId, properties }: IdentifyMessage): void
90
-
91
- /**
92
- * @description To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call.
93
- * This will allow you to answer questions like "Which marketing channels leads to users churning after a month?"
94
- * or "What do users do on our website before signing up?"
95
- * In a purely back-end implementation, this means whenever an anonymous user does something, you'll want to send a session ID with the capture call.
96
- * Then, when that users signs up, you want to do an alias call with the session ID and the newly created user ID.
97
- * The same concept applies for when a user logs in. If you're using PostHog in the front-end and back-end,
98
- * doing the identify call in the frontend will be enough.:
99
- * @param distinctId the current unique id
100
- * @param alias the unique ID of the user before
101
- */
102
- alias(data: { distinctId: string; alias: string }): void
103
-
104
- /**
105
- * @description PostHog feature flags (https://posthog.com/docs/features/feature-flags)
106
- * allow you to safely deploy and roll back new features. Once you've created a feature flag in PostHog,
107
- * you can use this method to check if the flag is on for a given user, allowing you to create logic to turn
108
- * features on and off for different user groups or individual users.
109
- * @param key the unique key of your feature flag
110
- * @param distinctId the current unique id
111
- * @param options: dict with optional parameters below
112
- * @param groups optional - what groups are currently active (group analytics). Required if the flag depends on groups.
113
- * @param personProperties optional - what person properties are known. Used to compute flags locally, if personalApiKey is present.
114
- * @param groupProperties optional - what group properties are known. Used to compute flags locally, if personalApiKey is present.
115
- * @param onlyEvaluateLocally optional - whether to only evaluate the flag locally. Defaults to false.
116
- * @param sendFeatureFlagEvents optional - whether to send feature flag events. Used for Experiments. Defaults to true.
117
- *
118
- * @returns true if the flag is on, false if the flag is off, undefined if there was an error.
119
- */
120
- isFeatureEnabled(
121
- key: string,
122
- distinctId: string,
123
- options?: {
124
- groups?: Record<string, string>
125
- personProperties?: Record<string, string>
126
- groupProperties?: Record<string, Record<string, string>>
127
- onlyEvaluateLocally?: boolean
128
- sendFeatureFlagEvents?: boolean
129
- }
130
- ): Promise<boolean | undefined>
131
-
132
- /**
133
- * @description PostHog feature flags (https://posthog.com/docs/features/feature-flags)
134
- * allow you to safely deploy and roll back new features. Once you've created a feature flag in PostHog,
135
- * you can use this method to check if the flag is on for a given user, allowing you to create logic to turn
136
- * features on and off for different user groups or individual users.
137
- * @param key the unique key of your feature flag
138
- * @param distinctId the current unique id
139
- * @param options: dict with optional parameters below
140
- * @param groups optional - what groups are currently active (group analytics). Required if the flag depends on groups.
141
- * @param personProperties optional - what person properties are known. Used to compute flags locally, if personalApiKey is present.
142
- * @param groupProperties optional - what group properties are known. Used to compute flags locally, if personalApiKey is present.
143
- * @param onlyEvaluateLocally optional - whether to only evaluate the flag locally. Defaults to false.
144
- * @param sendFeatureFlagEvents optional - whether to send feature flag events. Used for Experiments. Defaults to true.
145
- *
146
- * @returns true or string(for multivariates) if the flag is on, false if the flag is off, undefined if there was an error.
147
- */
148
- getFeatureFlag(
149
- key: string,
150
- distinctId: string,
151
- options?: {
152
- groups?: Record<string, string>
153
- personProperties?: Record<string, string>
154
- groupProperties?: Record<string, Record<string, string>>
155
- onlyEvaluateLocally?: boolean
156
- sendFeatureFlagEvents?: boolean
157
- }
158
- ): Promise<FeatureFlagValue | undefined>
159
-
160
- /**
161
- * @description Retrieves payload associated with the specified flag and matched value that is passed in.
162
- *
163
- * IMPORTANT: The `matchValue` parameter should be the value you previously obtained from `getFeatureFlag()`.
164
- * If matchValue isn't passed (or is undefined), this method will automatically call `getFeatureFlag()`
165
- * internally to fetch the flag value, which could result in a network call to the PostHog server if this flag can
166
- * not be evaluated locally. This means that omitting `matchValue` will potentially:
167
- * - Bypass local evaluation
168
- * - Count as an additional flag evaluation against your quota
169
- * - Impact performance due to the extra network request
170
- *
171
- * Example usage:
172
- * ```js
173
- * const flagValue = await client.getFeatureFlag('my-flag', distinctId);
174
- * const payload = await client.getFeatureFlagPayload('my-flag', distinctId, flagValue);
175
- * ```
176
- *
177
- * @param key the unique key of your feature flag
178
- * @param distinctId the current unique id
179
- * @param matchValue The flag value previously obtained from calling `getFeatureFlag()`. Can be a string or boolean.
180
- * To avoid extra network calls, pass this parameter when you can.
181
- * @param options: dict with optional parameters below
182
- * @param onlyEvaluateLocally optional - whether to only evaluate the flag locally. Defaults to false.
183
- *
184
- * @returns payload of a json type object
185
- */
186
- getFeatureFlagPayload(
187
- key: string,
188
- distinctId: string,
189
- matchValue?: FeatureFlagValue,
190
- options?: {
191
- onlyEvaluateLocally?: boolean
192
- }
193
- ): Promise<JsonType | undefined>
194
-
195
- /**
196
- * @description Sets a groups properties, which allows asking questions like "Who are the most active companies"
197
- * using my product in PostHog.
198
- *
199
- * @param groupType Type of group (ex: 'company'). Limited to 5 per project
200
- * @param groupKey Unique identifier for that type of group (ex: 'id:5')
201
- * @param properties OPTIONAL | which can be a object with any information you'd like to add
202
- */
203
- groupIdentify({ groupType, groupKey, properties }: GroupIdentifyMessage): void
204
-
205
- /**
206
- * @description Force an immediate reload of the polled feature flags. Please note that they are
207
- * already polled automatically at a regular interval.
208
- */
209
- reloadFeatureFlags(): Promise<void>
210
-
211
- /**
212
- * @description Flushes the events still in the queue and clears the feature flags poller to allow for
213
- * a clean shutdown.
214
- *
215
- * @param shutdownTimeoutMs The shutdown timeout, in milliseconds. Defaults to 30000 (30s).
216
- */
217
- shutdown(shutdownTimeoutMs?: number): void
218
-
219
- /**
220
- * @description Waits for local evaluation to be ready, with an optional timeout.
221
- * @param timeoutMs - Maximum time to wait in milliseconds. Defaults to 30 seconds.
222
- * @returns A promise that resolves to true if local evaluation is ready, false if the timeout was reached.
223
- */
224
- waitForLocalEvaluationReady(timeoutMs?: number): Promise<boolean>
225
-
226
- /**
227
- * @description Returns true if local evaluation is ready, false if it's not.
228
- * @returns true if local evaluation is ready, false if it's not.
229
- */
230
- isLocalEvaluationReady(): boolean
231
- }
@@ -1,36 +0,0 @@
1
- import * as crypto from '../src/crypto'
2
- import * as cryptoHelpers from '../src/crypto-helpers'
3
-
4
- describe('crypto', () => {
5
- describe('hashSHA1', () => {
6
- const testString = 'some-flag.some_distinct_id'
7
- const expectedHash = 'e4ce124e800a818c63099f95fa085dc2b620e173'
8
-
9
- afterEach(() => {
10
- jest.restoreAllMocks() // <- Reset all mocks after each test
11
- })
12
-
13
- it('should hash correctly using Node.js crypto', async () => {
14
- jest.spyOn(cryptoHelpers, 'getWebCrypto').mockResolvedValue(undefined)
15
-
16
- const hash = await crypto.hashSHA1(testString)
17
- expect(hash).toBe(expectedHash)
18
- })
19
-
20
- it('should hash correctly using Web Crypto API', async () => {
21
- jest.spyOn(cryptoHelpers, 'getNodeCrypto').mockResolvedValue(undefined)
22
-
23
- const hash = await crypto.hashSHA1(testString)
24
- expect(hash).toBe(expectedHash)
25
- })
26
-
27
- it('should throw if no crypto implementation is available', async () => {
28
- jest.spyOn(cryptoHelpers, 'getNodeCrypto').mockResolvedValue(undefined)
29
- jest.spyOn(cryptoHelpers, 'getWebCrypto').mockResolvedValue(undefined)
30
-
31
- await expect(crypto.hashSHA1(testString)).rejects.toThrow(
32
- 'No crypto implementation available. Tried Node Crypto API and Web SubtleCrypto API'
33
- )
34
- })
35
- })
36
- })
@@ -1,44 +0,0 @@
1
- import { propertiesFromUnknownInput } from '../../src/extensions/error-tracking/error-conversion'
2
- import { defaultStackParser } from '../../src/extensions/error-tracking/stack-trace'
3
- import { ErrorProperties } from '../../src/extensions/error-tracking/types'
4
-
5
- describe('error conversion', () => {
6
- async function getExceptionList(error: unknown): Promise<ErrorProperties['$exception_list']> {
7
- const syntheticException = new Error('PostHog syntheticException')
8
- const exceptionProperties = await propertiesFromUnknownInput(defaultStackParser, error, {
9
- syntheticException,
10
- })
11
- return exceptionProperties.$exception_list
12
- }
13
-
14
- it('should create an exception list from a string', async () => {
15
- const exceptionList = await getExceptionList('My string error')
16
- expect(exceptionList.length).toEqual(1)
17
- expect(exceptionList[0].value).toEqual('My string error')
18
- })
19
-
20
- it('should use the error key in object', async () => {
21
- const errorObject = { error: new Error('My special error') }
22
- const exceptionList = await getExceptionList(errorObject)
23
- expect(exceptionList.length).toEqual(1)
24
- expect(exceptionList[0].value).toEqual('My special error')
25
- })
26
-
27
- it('should create an exception list from an error cause', async () => {
28
- const originalError = new Error('original error')
29
- const error = new Error('test error', { cause: originalError })
30
- const exceptionList = await getExceptionList(error)
31
- expect(exceptionList.length).toEqual(2)
32
- expect(exceptionList[0].value).toEqual('test error')
33
- expect(exceptionList[1].value).toEqual('original error')
34
- })
35
-
36
- it('should create an exception list from a non error cause', async () => {
37
- const originalError = { error_code: 'XASKJASK' }
38
- const error = new Error('test error', { cause: originalError })
39
- const exceptionList = await getExceptionList(error)
40
- expect(exceptionList.length).toEqual(2)
41
- expect(exceptionList[0].value).toEqual('test error')
42
- expect(exceptionList[1].value).toEqual('Object captured as exception with keys: error_code')
43
- })
44
- })
@@ -1,162 +0,0 @@
1
- // import { PostHog } from '../'
2
- import { PostHog as PostHog } from '../../src/posthog-node'
3
- import { PostHogSentryIntegration } from '../../src/extensions/sentry-integration'
4
- import { waitForPromises } from 'posthog-core/test/test-utils/test-utils'
5
-
6
- jest.mock('../../package.json', () => ({ version: '1.2.3' }))
7
-
8
- const mockedFetch = jest.spyOn(globalThis, 'fetch').mockImplementation()
9
-
10
- const getLastBatchEvents = (): any[] | undefined => {
11
- expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.objectContaining({ method: 'POST' }))
12
-
13
- // reverse mock calls array to get the last call
14
- const call = mockedFetch.mock.calls.reverse().find((x) => (x[0] as string).includes('/batch/'))
15
- if (!call) {
16
- return undefined
17
- }
18
- return JSON.parse((call[1] as any).body as any).batch
19
- }
20
-
21
- const createMockSentryException = (): any => ({
22
- exception: {
23
- values: [
24
- {
25
- type: 'Error',
26
- value: 'example error',
27
- stacktrace: {
28
- frames: [],
29
- },
30
- mechanism: { type: 'generic', handled: true },
31
- },
32
- ],
33
- },
34
- event_id: '80a7023ac32c47f7acb0adaed600d149',
35
- platform: 'node',
36
- contexts: {},
37
- server_name: 'localhost',
38
- timestamp: 1704203482.356,
39
- environment: 'production',
40
- level: 'error',
41
- tags: { posthog_distinct_id: 'EXAMPLE_APP_GLOBAL' },
42
- breadcrumbs: [
43
- {
44
- timestamp: 1704203481.422,
45
- category: 'console',
46
- level: 'log',
47
- message: '⚡: Server is running at http://localhost:8010',
48
- },
49
- {
50
- timestamp: 1704203481.658,
51
- category: 'console',
52
- level: 'log',
53
- message:
54
- "PostHog Debug error [ClientError: Your personalApiKey is invalid. Are you sure you're not using your Project API key? More information: https://posthog.com/docs/api/overview]",
55
- },
56
- ],
57
- sdkProcessingMetadata: {
58
- propagationContext: { traceId: 'ea26146e5a354cb0b3b1daebb3f90e33', spanId: '8d642089c3daa272' },
59
- },
60
- })
61
-
62
- describe('PostHogSentryIntegration', () => {
63
- let posthog: PostHog
64
- let posthogSentry: PostHogSentryIntegration
65
-
66
- jest.useFakeTimers()
67
-
68
- beforeEach(() => {
69
- posthog = new PostHog('TEST_API_KEY', {
70
- host: 'http://example.com',
71
- fetchRetryCount: 0,
72
- })
73
-
74
- posthogSentry = new PostHogSentryIntegration(posthog)
75
-
76
- mockedFetch.mockResolvedValue({
77
- status: 200,
78
- text: () => Promise.resolve('ok'),
79
- json: () =>
80
- Promise.resolve({
81
- status: 'ok',
82
- }),
83
- } as any)
84
- })
85
-
86
- afterEach(async () => {
87
- // ensure clean shutdown & no test interdependencies
88
- await posthog.shutdown()
89
- })
90
-
91
- it('should forward sentry exceptions to posthog', async () => {
92
- expect(mockedFetch).toHaveBeenCalledTimes(0)
93
-
94
- const mockSentry = {
95
- getClient: () => ({
96
- getDsn: () => ({
97
- projectId: 123,
98
- }),
99
- }),
100
- }
101
-
102
- let processorFunction: any
103
-
104
- posthogSentry.setupOnce(
105
- (fn) => (processorFunction = fn),
106
- () => mockSentry
107
- )
108
-
109
- processorFunction(createMockSentryException())
110
-
111
- await waitForPromises() // First flush
112
- jest.runOnlyPendingTimers() // Flush timer
113
- await waitForPromises() // Second flush
114
- const batchEvents = getLastBatchEvents()
115
-
116
- expect(batchEvents).toEqual([
117
- {
118
- distinct_id: 'EXAMPLE_APP_GLOBAL',
119
- event: '$exception',
120
- properties: {
121
- $exception_level: 'error',
122
- $exception_list: [
123
- {
124
- mechanism: { handled: true, type: 'generic' },
125
- stacktrace: { frames: [], type: 'raw' },
126
- type: 'Error',
127
- value: 'example error',
128
- },
129
- ],
130
- $exception_message: 'example error',
131
- $exception_type: 'Error',
132
- $exception_personURL: 'http://example.com/project/TEST_API_KEY/person/EXAMPLE_APP_GLOBAL',
133
- $sentry_event_id: '80a7023ac32c47f7acb0adaed600d149',
134
- $sentry_exception: {
135
- values: [
136
- {
137
- type: 'Error',
138
- value: 'example error',
139
- stacktrace: { frames: [] },
140
- mechanism: { type: 'generic', handled: true },
141
- },
142
- ],
143
- },
144
- $sentry_exception_message: 'example error',
145
- $sentry_exception_type: 'Error',
146
- $sentry_tags: {
147
- posthog_distinct_id: 'EXAMPLE_APP_GLOBAL',
148
- 'PostHog Person URL': 'http://example.com/project/TEST_API_KEY/person/EXAMPLE_APP_GLOBAL',
149
- },
150
- $lib: 'posthog-node',
151
- $lib_version: '1.2.3',
152
- $geoip_disable: true,
153
- },
154
- type: 'capture',
155
- library: 'posthog-node',
156
- library_version: '1.2.3',
157
- timestamp: expect.any(String),
158
- uuid: expect.any(String),
159
- },
160
- ])
161
- })
162
- })