posthog-node 3.4.0 → 3.5.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 +5 -0
- package/lib/index.cjs.js +182 -72
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.esm.js +182 -72
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +2 -1
- package/lib/posthog-node/src/posthog-node.d.ts +1 -0
- package/package.json +1 -1
- package/src/feature-flags.ts +10 -6
- package/src/posthog-node.ts +99 -19
- package/test/extensions/sentry-integration.spec.ts +2 -0
- package/test/feature-flags.spec.ts +17 -2
- package/test/posthog-node.spec.ts +407 -10
|
@@ -12,9 +12,9 @@ export declare abstract class PostHogCoreStateless {
|
|
|
12
12
|
private captureMode;
|
|
13
13
|
private removeDebugCallback?;
|
|
14
14
|
private debugMode;
|
|
15
|
-
private pendingPromises;
|
|
16
15
|
private disableGeoip;
|
|
17
16
|
private _optoutOverride;
|
|
17
|
+
private pendingPromises;
|
|
18
18
|
protected _events: SimpleEventEmitter;
|
|
19
19
|
protected _flushTimer?: any;
|
|
20
20
|
protected _retryOptions: RetriableOptions;
|
|
@@ -32,6 +32,7 @@ export declare abstract class PostHogCoreStateless {
|
|
|
32
32
|
on(event: string, cb: (...args: any[]) => void): () => void;
|
|
33
33
|
debug(enabled?: boolean): void;
|
|
34
34
|
private buildPayload;
|
|
35
|
+
protected addPendingPromise(promise: Promise<any>): void;
|
|
35
36
|
/***
|
|
36
37
|
*** TRACKING
|
|
37
38
|
***/
|
package/package.json
CHANGED
package/src/feature-flags.ts
CHANGED
|
@@ -121,7 +121,7 @@ class FeatureFlagsPoller {
|
|
|
121
121
|
console.debug(`InconclusiveMatchError when computing flag locally: ${key}: ${e}`)
|
|
122
122
|
}
|
|
123
123
|
} else if (e instanceof Error) {
|
|
124
|
-
|
|
124
|
+
this.onError?.(new Error(`Error computing flag locally: ${key}: ${e}`))
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
@@ -211,14 +211,18 @@ class FeatureFlagsPoller {
|
|
|
211
211
|
const groupName = this.groupTypeMapping[String(aggregation_group_type_index)]
|
|
212
212
|
|
|
213
213
|
if (!groupName) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
if (this.debugMode) {
|
|
215
|
+
console.warn(
|
|
216
|
+
`[FEATURE FLAGS] Unknown group type index ${aggregation_group_type_index} for feature flag ${flag.key}`
|
|
217
|
+
)
|
|
218
|
+
}
|
|
217
219
|
throw new InconclusiveMatchError('Flag has unknown group type index')
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
if (!(groupName in groups)) {
|
|
221
|
-
|
|
223
|
+
if (this.debugMode) {
|
|
224
|
+
console.warn(`[FEATURE FLAGS] Can't compute group feature flag: ${flag.key} without group names passed in`)
|
|
225
|
+
}
|
|
222
226
|
return false
|
|
223
227
|
}
|
|
224
228
|
|
|
@@ -383,7 +387,7 @@ class FeatureFlagsPoller {
|
|
|
383
387
|
|
|
384
388
|
const responseJson = await res.json()
|
|
385
389
|
if (!('flags' in responseJson)) {
|
|
386
|
-
|
|
390
|
+
this.onError?.(new Error(`Invalid response when getting feature flags: ${JSON.stringify(responseJson)}`))
|
|
387
391
|
}
|
|
388
392
|
|
|
389
393
|
this.featureFlags = responseJson.flags || []
|
package/src/posthog-node.ts
CHANGED
|
@@ -105,26 +105,54 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
105
105
|
super.captureStateless(distinctId, event, props, { timestamp, disableGeoip })
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
// :TRICKY: If we flush, or need to shut down, to not lose events we want this promise to resolve before we flush
|
|
109
|
+
const capturePromise = Promise.resolve()
|
|
110
|
+
.then(async () => {
|
|
111
|
+
if (sendFeatureFlags) {
|
|
112
|
+
// If we are sending feature flags, we need to make sure we have the latest flags
|
|
113
|
+
return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
|
|
117
|
+
// Otherwise we may as well check for the flags locally and include them if there
|
|
118
|
+
const groupsWithStringValues: Record<string, string> = {}
|
|
119
|
+
for (const [key, value] of Object.entries(groups || {})) {
|
|
120
|
+
groupsWithStringValues[key] = String(value)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return await this.getAllFlags(distinctId, {
|
|
124
|
+
groups: groupsWithStringValues,
|
|
125
|
+
disableGeoip,
|
|
126
|
+
onlyEvaluateLocally: true,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
return {}
|
|
130
|
+
})
|
|
131
|
+
.then((flags) => {
|
|
132
|
+
// Derive the relevant flag properties to add
|
|
133
|
+
const additionalProperties: Record<string, any> = {}
|
|
111
134
|
if (flags) {
|
|
112
135
|
for (const [feature, variant] of Object.entries(flags)) {
|
|
113
|
-
|
|
114
|
-
featureVariantProperties[`$feature/${feature}`] = variant
|
|
115
|
-
}
|
|
136
|
+
additionalProperties[`$feature/${feature}`] = variant
|
|
116
137
|
}
|
|
117
138
|
}
|
|
118
139
|
const activeFlags = Object.keys(flags || {}).filter((flag) => flags?.[flag] !== false)
|
|
119
|
-
|
|
120
|
-
$active_feature_flags
|
|
121
|
-
...featureVariantProperties,
|
|
140
|
+
if (activeFlags.length > 0) {
|
|
141
|
+
additionalProperties['$active_feature_flags'] = activeFlags
|
|
122
142
|
}
|
|
123
|
-
|
|
143
|
+
|
|
144
|
+
return additionalProperties
|
|
124
145
|
})
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
146
|
+
.catch(() => {
|
|
147
|
+
// Something went wrong getting the flag info - we should capture the event anyways
|
|
148
|
+
return {}
|
|
149
|
+
})
|
|
150
|
+
.then((additionalProperties) => {
|
|
151
|
+
// No matter what - capture the event
|
|
152
|
+
_capture({ ...additionalProperties, ...properties, $groups: groups })
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
this.addPendingPromise(capturePromise)
|
|
128
156
|
}
|
|
129
157
|
|
|
130
158
|
identify({ distinctId, properties, disableGeoip }: IdentifyMessageV1): void {
|
|
@@ -156,8 +184,18 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
156
184
|
disableGeoip?: boolean
|
|
157
185
|
}
|
|
158
186
|
): Promise<string | boolean | undefined> {
|
|
159
|
-
const { groups,
|
|
160
|
-
let { onlyEvaluateLocally, sendFeatureFlagEvents } = options || {}
|
|
187
|
+
const { groups, disableGeoip } = options || {}
|
|
188
|
+
let { onlyEvaluateLocally, sendFeatureFlagEvents, personProperties, groupProperties } = options || {}
|
|
189
|
+
|
|
190
|
+
const adjustedProperties = this.addLocalPersonAndGroupProperties(
|
|
191
|
+
distinctId,
|
|
192
|
+
groups,
|
|
193
|
+
personProperties,
|
|
194
|
+
groupProperties
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
personProperties = adjustedProperties.allPersonProperties
|
|
198
|
+
groupProperties = adjustedProperties.allGroupProperties
|
|
161
199
|
|
|
162
200
|
// set defaults
|
|
163
201
|
if (onlyEvaluateLocally == undefined) {
|
|
@@ -232,8 +270,19 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
232
270
|
disableGeoip?: boolean
|
|
233
271
|
}
|
|
234
272
|
): Promise<JsonType | undefined> {
|
|
235
|
-
const { groups,
|
|
236
|
-
let { onlyEvaluateLocally, sendFeatureFlagEvents } = options || {}
|
|
273
|
+
const { groups, disableGeoip } = options || {}
|
|
274
|
+
let { onlyEvaluateLocally, sendFeatureFlagEvents, personProperties, groupProperties } = options || {}
|
|
275
|
+
|
|
276
|
+
const adjustedProperties = this.addLocalPersonAndGroupProperties(
|
|
277
|
+
distinctId,
|
|
278
|
+
groups,
|
|
279
|
+
personProperties,
|
|
280
|
+
groupProperties
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
personProperties = adjustedProperties.allPersonProperties
|
|
284
|
+
groupProperties = adjustedProperties.allGroupProperties
|
|
285
|
+
|
|
237
286
|
let response = undefined
|
|
238
287
|
|
|
239
288
|
// Try to get match value locally if not provided
|
|
@@ -324,8 +373,18 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
324
373
|
disableGeoip?: boolean
|
|
325
374
|
}
|
|
326
375
|
): Promise<PosthogFlagsAndPayloadsResponse> {
|
|
327
|
-
const { groups,
|
|
328
|
-
let { onlyEvaluateLocally } = options || {}
|
|
376
|
+
const { groups, disableGeoip } = options || {}
|
|
377
|
+
let { onlyEvaluateLocally, personProperties, groupProperties } = options || {}
|
|
378
|
+
|
|
379
|
+
const adjustedProperties = this.addLocalPersonAndGroupProperties(
|
|
380
|
+
distinctId,
|
|
381
|
+
groups,
|
|
382
|
+
personProperties,
|
|
383
|
+
groupProperties
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
personProperties = adjustedProperties.allPersonProperties
|
|
387
|
+
groupProperties = adjustedProperties.allGroupProperties
|
|
329
388
|
|
|
330
389
|
// set defaults
|
|
331
390
|
if (onlyEvaluateLocally == undefined) {
|
|
@@ -385,4 +444,25 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
385
444
|
this.featureFlagsPoller?.stopPoller()
|
|
386
445
|
return super.shutdownAsync()
|
|
387
446
|
}
|
|
447
|
+
|
|
448
|
+
private addLocalPersonAndGroupProperties(
|
|
449
|
+
distinctId: string,
|
|
450
|
+
groups?: Record<string, string>,
|
|
451
|
+
personProperties?: Record<string, string>,
|
|
452
|
+
groupProperties?: Record<string, Record<string, string>>
|
|
453
|
+
): { allPersonProperties: Record<string, string>; allGroupProperties: Record<string, Record<string, string>> } {
|
|
454
|
+
const allPersonProperties = { $current_distinct_id: distinctId, ...(personProperties || {}) }
|
|
455
|
+
|
|
456
|
+
const allGroupProperties: Record<string, Record<string, string>> = {}
|
|
457
|
+
if (groups) {
|
|
458
|
+
for (const groupName of Object.keys(groups)) {
|
|
459
|
+
allGroupProperties[groupName] = {
|
|
460
|
+
$group_key: groups[groupName],
|
|
461
|
+
...(groupProperties?.[groupName] || {}),
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return { allPersonProperties, allGroupProperties }
|
|
467
|
+
}
|
|
388
468
|
}
|
|
@@ -3,6 +3,7 @@ import { PostHog as PostHog } from '../../src/posthog-node'
|
|
|
3
3
|
import { PostHogSentryIntegration } from '../../src/extensions/sentry-integration'
|
|
4
4
|
jest.mock('../../src/fetch')
|
|
5
5
|
import fetch from '../../src/fetch'
|
|
6
|
+
import { waitForPromises } from 'posthog-core/test/test-utils/test-utils'
|
|
6
7
|
|
|
7
8
|
jest.mock('../../package.json', () => ({ version: '1.2.3' }))
|
|
8
9
|
|
|
@@ -108,6 +109,7 @@ describe('PostHogSentryIntegration', () => {
|
|
|
108
109
|
|
|
109
110
|
processorFunction(createMockSentryException())
|
|
110
111
|
|
|
112
|
+
await waitForPromises()
|
|
111
113
|
jest.runOnlyPendingTimers()
|
|
112
114
|
const batchEvents = getLastBatchEvents()
|
|
113
115
|
|
|
@@ -50,6 +50,17 @@ export const apiImplementation = ({
|
|
|
50
50
|
}) as any
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
if ((url as any).includes('batch/')) {
|
|
54
|
+
return Promise.resolve({
|
|
55
|
+
status: 200,
|
|
56
|
+
text: () => Promise.resolve('ok'),
|
|
57
|
+
json: () =>
|
|
58
|
+
Promise.resolve({
|
|
59
|
+
status: 'ok',
|
|
60
|
+
}),
|
|
61
|
+
}) as any
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
return Promise.resolve({
|
|
54
65
|
status: 400,
|
|
55
66
|
text: () => Promise.resolve('ok'),
|
|
@@ -342,7 +353,11 @@ describe('local evaluation', () => {
|
|
|
342
353
|
token: 'TEST_API_KEY',
|
|
343
354
|
distinct_id: 'some-distinct-id_outside_rollout?',
|
|
344
355
|
groups: {},
|
|
345
|
-
person_properties: {
|
|
356
|
+
person_properties: {
|
|
357
|
+
$current_distinct_id: 'some-distinct-id_outside_rollout?',
|
|
358
|
+
region: 'USA',
|
|
359
|
+
email: 'a@b.com',
|
|
360
|
+
},
|
|
346
361
|
group_properties: {},
|
|
347
362
|
geoip_disable: true,
|
|
348
363
|
}),
|
|
@@ -361,7 +376,7 @@ describe('local evaluation', () => {
|
|
|
361
376
|
token: 'TEST_API_KEY',
|
|
362
377
|
distinct_id: 'some-distinct-id',
|
|
363
378
|
groups: {},
|
|
364
|
-
person_properties: { doesnt_matter: '1' },
|
|
379
|
+
person_properties: { $current_distinct_id: 'some-distinct-id', doesnt_matter: '1' },
|
|
365
380
|
group_properties: {},
|
|
366
381
|
geoip_disable: true,
|
|
367
382
|
}),
|