posthog-node 4.3.0 → 4.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posthog-node",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
4
4
  "description": "PostHog Node.js integration",
5
5
  "repository": {
6
6
  "type": "git",
@@ -294,59 +294,80 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
294
294
  disableGeoip?: boolean
295
295
  }
296
296
  ): Promise<JsonType | undefined> {
297
- const { groups, disableGeoip } = options || {}
298
- let { onlyEvaluateLocally, sendFeatureFlagEvents, personProperties, groupProperties } = options || {}
297
+ const { groups, disableGeoip, onlyEvaluateLocally = false, personProperties, groupProperties } = options || {}
299
298
 
300
- const adjustedProperties = this.addLocalPersonAndGroupProperties(
299
+ const { allPersonProperties, allGroupProperties } = this.addLocalPersonAndGroupProperties(
301
300
  distinctId,
302
301
  groups,
303
302
  personProperties,
304
303
  groupProperties
305
304
  )
306
305
 
307
- personProperties = adjustedProperties.allPersonProperties
308
- groupProperties = adjustedProperties.allGroupProperties
309
-
310
- let response = undefined
311
-
312
- // Try to get match value locally if not provided
313
- if (!matchValue) {
306
+ if (matchValue === undefined) {
314
307
  matchValue = await this.getFeatureFlag(key, distinctId, {
315
308
  ...options,
316
309
  onlyEvaluateLocally: true,
310
+ sendFeatureFlagEvents: false,
317
311
  })
318
312
  }
319
313
 
320
- if (matchValue) {
321
- response = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue)
322
- }
314
+ let response: string | boolean | undefined
315
+ let payload: JsonType | undefined
323
316
 
324
- // set defaults
325
- if (onlyEvaluateLocally == undefined) {
326
- onlyEvaluateLocally = false
327
- }
328
- if (sendFeatureFlagEvents == undefined) {
329
- sendFeatureFlagEvents = true
317
+ if (matchValue) {
318
+ response = matchValue
319
+ payload = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue)
320
+ } else {
321
+ response = undefined
322
+ payload = undefined
330
323
  }
331
324
 
332
- // set defaults
333
- if (onlyEvaluateLocally == undefined) {
334
- onlyEvaluateLocally = false
335
- }
325
+ // Determine if the payload was evaluated locally
326
+ const payloadWasLocallyEvaluated = payload !== undefined
336
327
 
337
- const payloadWasLocallyEvaluated = response !== undefined
328
+ // Fetch final flags and payloads either locally or from the remote server
329
+ let fetchedOrLocalFlags: Record<string, string | boolean> | undefined
330
+ let fetchedOrLocalPayloads: Record<string, JsonType | undefined> | undefined
338
331
 
339
- if (!payloadWasLocallyEvaluated && !onlyEvaluateLocally) {
340
- response = await super.getFeatureFlagPayloadStateless(
341
- key,
332
+ if (payloadWasLocallyEvaluated || onlyEvaluateLocally) {
333
+ if (response !== undefined) {
334
+ fetchedOrLocalFlags = { [key]: response }
335
+ fetchedOrLocalPayloads = { [key]: payload }
336
+ } else {
337
+ fetchedOrLocalFlags = {}
338
+ fetchedOrLocalPayloads = {}
339
+ }
340
+ } else {
341
+ const fetchedData = await super.getFeatureFlagsAndPayloadsStateless(
342
342
  distinctId,
343
343
  groups,
344
- personProperties,
345
- groupProperties,
344
+ allPersonProperties,
345
+ allGroupProperties,
346
346
  disableGeoip
347
347
  )
348
+ fetchedOrLocalFlags = fetchedData.flags || {}
349
+ fetchedOrLocalPayloads = fetchedData.payloads || {}
348
350
  }
349
- return response
351
+
352
+ const finalResponse = fetchedOrLocalFlags[key]
353
+ const finalPayload = fetchedOrLocalPayloads[key]
354
+ const finalLocallyEvaluated = payloadWasLocallyEvaluated
355
+
356
+ this.capture({
357
+ distinctId,
358
+ event: '$feature_flag_called',
359
+ properties: {
360
+ $feature_flag: key,
361
+ $feature_flag_response: finalResponse,
362
+ $feature_flag_payload: finalPayload,
363
+ locally_evaluated: finalLocallyEvaluated,
364
+ [`$feature/${key}`]: finalResponse,
365
+ },
366
+ groups,
367
+ disableGeoip,
368
+ })
369
+
370
+ return finalPayload
350
371
  }
351
372
 
352
373
  async isFeatureEnabled(
@@ -897,13 +897,18 @@ describe('PostHog Node.js', () => {
897
897
  rollout_percentage: 100,
898
898
  },
899
899
  ],
900
+ payloads: { true: { variant: 'A' } },
900
901
  },
901
902
  },
902
903
  ],
903
904
  }
904
905
 
905
906
  mockedFetch.mockImplementation(
906
- apiImplementation({ localFlags: flags, decideFlags: { 'decide-flag': 'decide-value' } })
907
+ apiImplementation({
908
+ localFlags: flags,
909
+ decideFlags: { 'decide-flag': 'decide-value' },
910
+ decideFlagPayloads: { 'beta-feature': { variant: 'A' } },
911
+ })
907
912
  )
908
913
 
909
914
  posthog = new PostHog('TEST_API_KEY', {
@@ -945,6 +950,34 @@ describe('PostHog Node.js', () => {
945
950
  )
946
951
  mockedFetch.mockClear()
947
952
 
953
+ expect(
954
+ await posthog.getFeatureFlagPayload('beta-feature', 'some-distinct-id', undefined, {
955
+ personProperties: { region: 'USA', name: 'Aloha' },
956
+ })
957
+ ).toEqual({ variant: 'A' })
958
+
959
+ // TRICKY: There's now an extra step before events are queued, so need to wait for that to resolve
960
+ jest.runOnlyPendingTimers()
961
+ await waitForPromises()
962
+ await posthog.flush()
963
+
964
+ expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
965
+
966
+ expect(getLastBatchEvents()?.[0]).toEqual(
967
+ expect.objectContaining({
968
+ distinct_id: 'some-distinct-id',
969
+ event: '$feature_flag_called',
970
+ properties: expect.objectContaining({
971
+ $feature_flag: 'beta-feature',
972
+ $feature_flag_response: true,
973
+ $feature_flag_payload: { variant: 'A' },
974
+ locally_evaluated: true,
975
+ [`$feature/${'beta-feature'}`]: true,
976
+ }),
977
+ })
978
+ )
979
+ mockedFetch.mockClear()
980
+
948
981
  // # called again for same user, shouldn't call capture again
949
982
  expect(
950
983
  await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
@@ -1081,7 +1114,7 @@ describe('PostHog Node.js', () => {
1081
1114
  expect(mockedFetch).toHaveBeenCalledTimes(0)
1082
1115
 
1083
1116
  await expect(posthog.getFeatureFlagPayload('false-flag', '123', true)).resolves.toEqual(300)
1084
- expect(mockedFetch).toHaveBeenCalledTimes(0)
1117
+ expect(mockedFetch).toHaveBeenCalledTimes(1) // this now calls the server, because in this case the flag is not locally evaluated but we have a payload that we need to calculate
1085
1118
  })
1086
1119
 
1087
1120
  it('should not double parse json with getFeatureFlagPayloads and server eval', async () => {