posthog-node 5.25.0 → 5.26.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/src/client.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { version } from './version'
2
2
 
3
3
  import {
4
- FeatureFlagDetail,
5
4
  FeatureFlagValue,
6
5
  isBlockedUA,
7
6
  isPlainObject,
@@ -26,9 +25,12 @@ import {
26
25
  OverrideFeatureFlagsOptions,
27
26
  PostHogOptions,
28
27
  SendFeatureFlagsOptions,
28
+ FlagEvaluationOptions,
29
+ AllFlagsOptions,
29
30
  } from './types'
30
31
  import {
31
32
  FeatureFlagsPoller,
33
+ type FeatureFlagEvaluationContext,
32
34
  RequiresServerEvaluation,
33
35
  InconclusiveMatchError,
34
36
  } from './extensions/feature-flags/feature-flags'
@@ -611,6 +613,16 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
611
613
  })
612
614
  }
613
615
 
616
+ private _resolveDistinctId<T>(
617
+ distinctIdOrOptions: string | T | undefined,
618
+ options: T | undefined
619
+ ): { distinctId: string | undefined; options: T | undefined } {
620
+ if (typeof distinctIdOrOptions === 'string') {
621
+ return { distinctId: distinctIdOrOptions, options }
622
+ }
623
+ return { distinctId: this.context?.get()?.distinctId, options: distinctIdOrOptions }
624
+ }
625
+
614
626
  /**
615
627
  * Internal method that handles feature flag evaluation with full details.
616
628
  * Used by getFeatureFlag, getFeatureFlagPayload, and getFeatureFlagResult.
@@ -663,6 +675,12 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
663
675
 
664
676
  personProperties = adjustedProperties.allPersonProperties
665
677
  groupProperties = adjustedProperties.allGroupProperties
678
+ const evaluationContext = this.createFeatureFlagEvaluationContext(
679
+ distinctId,
680
+ groups,
681
+ personProperties,
682
+ groupProperties
683
+ )
666
684
 
667
685
  // set defaults
668
686
  if (onlyEvaluateLocally == undefined) {
@@ -687,14 +705,9 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
687
705
  const flag = this.featureFlagsPoller?.featureFlagsByKey[key]
688
706
  if (flag) {
689
707
  try {
690
- const localResult = await this.featureFlagsPoller?.computeFlagAndPayloadLocally(
691
- flag,
692
- distinctId,
693
- groups,
694
- personProperties,
695
- groupProperties,
696
- matchValue
697
- )
708
+ const localResult = await this.featureFlagsPoller?.computeFlagAndPayloadLocally(flag, evaluationContext, {
709
+ matchValue,
710
+ })
698
711
  if (localResult) {
699
712
  flagWasLocallyEvaluated = true
700
713
  const value = localResult.value
@@ -721,10 +734,10 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
721
734
  // Fall back to remote evaluation if needed
722
735
  if (!flagWasLocallyEvaluated && !onlyEvaluateLocally) {
723
736
  const flagsResponse = await super.getFeatureFlagDetailsStateless(
724
- distinctId,
725
- groups,
726
- personProperties,
727
- groupProperties,
737
+ evaluationContext.distinctId,
738
+ evaluationContext.groups,
739
+ evaluationContext.personProperties,
740
+ evaluationContext.groupProperties,
728
741
  disableGeoip,
729
742
  [key]
730
743
  )
@@ -1008,21 +1021,30 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1008
1021
  * @param options - Optional configuration for flag evaluation
1009
1022
  * @returns Promise that resolves to the flag result or undefined
1010
1023
  */
1024
+ async getFeatureFlagResult(key: string, options?: FlagEvaluationOptions): Promise<FeatureFlagResult | undefined>
1011
1025
  async getFeatureFlagResult(
1012
1026
  key: string,
1013
1027
  distinctId: string,
1014
- options?: {
1015
- groups?: Record<string, string>
1016
- personProperties?: Record<string, string>
1017
- groupProperties?: Record<string, Record<string, string>>
1018
- onlyEvaluateLocally?: boolean
1019
- sendFeatureFlagEvents?: boolean
1020
- disableGeoip?: boolean
1021
- }
1028
+ options?: FlagEvaluationOptions
1029
+ ): Promise<FeatureFlagResult | undefined>
1030
+ async getFeatureFlagResult(
1031
+ key: string,
1032
+ distinctIdOrOptions?: string | FlagEvaluationOptions,
1033
+ options?: FlagEvaluationOptions
1022
1034
  ): Promise<FeatureFlagResult | undefined> {
1023
- return this._getFeatureFlagResult(key, distinctId, {
1024
- ...options,
1025
- sendFeatureFlagEvents: options?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true,
1035
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(
1036
+ distinctIdOrOptions,
1037
+ options
1038
+ )
1039
+
1040
+ if (!resolvedDistinctId) {
1041
+ this._logger.warn('[PostHog] distinctId is required — pass it explicitly or use withContext()')
1042
+ return undefined
1043
+ }
1044
+
1045
+ return this._getFeatureFlagResult(key, resolvedDistinctId, {
1046
+ ...resolvedOptions,
1047
+ sendFeatureFlagEvents: resolvedOptions?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true,
1026
1048
  })
1027
1049
  }
1028
1050
 
@@ -1155,18 +1177,24 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1155
1177
  * @param options - Optional configuration for flag evaluation
1156
1178
  * @returns Promise that resolves to a record of flag keys and their values
1157
1179
  */
1180
+ async getAllFlags(options?: AllFlagsOptions): Promise<Record<string, FeatureFlagValue>>
1181
+ async getAllFlags(distinctId: string, options?: AllFlagsOptions): Promise<Record<string, FeatureFlagValue>>
1158
1182
  async getAllFlags(
1159
- distinctId: string,
1160
- options?: {
1161
- groups?: Record<string, string>
1162
- personProperties?: Record<string, string>
1163
- groupProperties?: Record<string, Record<string, string>>
1164
- onlyEvaluateLocally?: boolean
1165
- disableGeoip?: boolean
1166
- flagKeys?: string[]
1167
- }
1183
+ distinctIdOrOptions?: string | AllFlagsOptions,
1184
+ options?: AllFlagsOptions
1168
1185
  ): Promise<Record<string, FeatureFlagValue>> {
1169
- const response = await this.getAllFlagsAndPayloads(distinctId, options)
1186
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(
1187
+ distinctIdOrOptions,
1188
+ options
1189
+ )
1190
+ if (!resolvedDistinctId) {
1191
+ this._logger.warn(
1192
+ '[PostHog] distinctId is required to get feature flags — pass it explicitly or use withContext()'
1193
+ )
1194
+ return {}
1195
+ }
1196
+
1197
+ const response = await this.getAllFlagsAndPayloads(resolvedDistinctId, resolvedOptions)
1170
1198
  return response.featureFlags || {}
1171
1199
  }
1172
1200
 
@@ -1203,22 +1231,28 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1203
1231
  * @param options - Optional configuration for flag evaluation
1204
1232
  * @returns Promise that resolves to flags and payloads
1205
1233
  */
1234
+ async getAllFlagsAndPayloads(options?: AllFlagsOptions): Promise<PostHogFlagsAndPayloadsResponse>
1235
+ async getAllFlagsAndPayloads(distinctId: string, options?: AllFlagsOptions): Promise<PostHogFlagsAndPayloadsResponse>
1206
1236
  async getAllFlagsAndPayloads(
1207
- distinctId: string,
1208
- options?: {
1209
- groups?: Record<string, string>
1210
- personProperties?: Record<string, string>
1211
- groupProperties?: Record<string, Record<string, string>>
1212
- onlyEvaluateLocally?: boolean
1213
- disableGeoip?: boolean
1214
- flagKeys?: string[]
1215
- }
1237
+ distinctIdOrOptions?: string | AllFlagsOptions,
1238
+ options?: AllFlagsOptions
1216
1239
  ): Promise<PostHogFlagsAndPayloadsResponse> {
1217
- const { groups, disableGeoip, flagKeys } = options || {}
1218
- let { onlyEvaluateLocally, personProperties, groupProperties } = options || {}
1240
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(
1241
+ distinctIdOrOptions,
1242
+ options
1243
+ )
1244
+ if (!resolvedDistinctId) {
1245
+ this._logger.warn(
1246
+ '[PostHog] distinctId is required to get feature flags and payloads — pass it explicitly or use withContext()'
1247
+ )
1248
+ return { featureFlags: {}, featureFlagPayloads: {} }
1249
+ }
1250
+
1251
+ const { groups, disableGeoip, flagKeys } = resolvedOptions || {}
1252
+ let { onlyEvaluateLocally, personProperties, groupProperties } = resolvedOptions || {}
1219
1253
 
1220
1254
  const adjustedProperties = this.addLocalPersonAndGroupProperties(
1221
- distinctId,
1255
+ resolvedDistinctId,
1222
1256
  groups,
1223
1257
  personProperties,
1224
1258
  groupProperties
@@ -1226,19 +1260,19 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1226
1260
 
1227
1261
  personProperties = adjustedProperties.allPersonProperties
1228
1262
  groupProperties = adjustedProperties.allGroupProperties
1263
+ const evaluationContext = this.createFeatureFlagEvaluationContext(
1264
+ resolvedDistinctId,
1265
+ groups,
1266
+ personProperties,
1267
+ groupProperties
1268
+ )
1229
1269
 
1230
1270
  // set defaults
1231
1271
  if (onlyEvaluateLocally == undefined) {
1232
1272
  onlyEvaluateLocally = this.options.strictLocalEvaluation ?? false
1233
1273
  }
1234
1274
 
1235
- const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(
1236
- distinctId,
1237
- groups,
1238
- personProperties,
1239
- groupProperties,
1240
- flagKeys
1241
- )
1275
+ const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(evaluationContext, flagKeys)
1242
1276
 
1243
1277
  let featureFlags = {}
1244
1278
  let featureFlagPayloads = {}
@@ -1251,10 +1285,10 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1251
1285
 
1252
1286
  if (fallbackToFlags && !onlyEvaluateLocally) {
1253
1287
  const remoteEvaluationResult = await super.getFeatureFlagsAndPayloadsStateless(
1254
- distinctId,
1255
- groups,
1256
- personProperties,
1257
- groupProperties,
1288
+ evaluationContext.distinctId,
1289
+ evaluationContext.groups,
1290
+ evaluationContext.personProperties,
1291
+ evaluationContext.groupProperties,
1258
1292
  disableGeoip,
1259
1293
  flagKeys
1260
1294
  )
@@ -1510,6 +1544,24 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1510
1544
  return this.context?.get()
1511
1545
  }
1512
1546
 
1547
+ /**
1548
+ * Set context without a callback wrapper.
1549
+ *
1550
+ * Uses `AsyncLocalStorage.enterWith()` to attach context to the current
1551
+ * async execution context. The context lives until that async context ends.
1552
+ *
1553
+ * Must be called in the same async scope that makes PostHog calls.
1554
+ * Calling this outside a request-scoped async context will leak context
1555
+ * across unrelated work. Prefer `withContext()` when you can wrap code
1556
+ * in a callback — it creates an isolated scope that cleans up automatically.
1557
+ *
1558
+ * @param data - Context data to apply (distinctId, sessionId, properties)
1559
+ * @param options - Context options (fresh: true to start with clean context instead of inheriting)
1560
+ */
1561
+ enterContext(data: Partial<ContextData>, options?: ContextOptions): void {
1562
+ this.context?.enter(data as ContextData, options)
1563
+ }
1564
+
1513
1565
  /**
1514
1566
  * Shutdown the PostHog client gracefully.
1515
1567
  *
@@ -1691,6 +1743,21 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen
1691
1743
  return { allPersonProperties, allGroupProperties }
1692
1744
  }
1693
1745
 
1746
+ private createFeatureFlagEvaluationContext(
1747
+ distinctId: string,
1748
+ groups?: Record<string, string>,
1749
+ personProperties?: Record<string, any>,
1750
+ groupProperties?: Record<string, Record<string, any>>
1751
+ ): FeatureFlagEvaluationContext {
1752
+ return {
1753
+ distinctId,
1754
+ groups: groups || {},
1755
+ personProperties: personProperties || {},
1756
+ groupProperties: groupProperties || {},
1757
+ evaluationCache: {},
1758
+ }
1759
+ }
1760
+
1694
1761
  /**
1695
1762
  * Capture an error exception as an event.
1696
1763
  *
@@ -13,21 +13,26 @@ export class PostHogContext implements IPostHogContext {
13
13
  }
14
14
 
15
15
  run<T>(context: ContextData, fn: () => T, options?: ContextOptions): T {
16
- const fresh = options?.fresh === true
16
+ return this.storage.run(this.resolve(context, options), fn)
17
+ }
18
+
19
+ enter(context: ContextData, options?: ContextOptions): void {
20
+ this.storage.enterWith(this.resolve(context, options))
21
+ }
22
+
23
+ private resolve(context: ContextData, options?: ContextOptions): ContextData {
24
+ if (options?.fresh === true) {
25
+ return context
26
+ }
17
27
 
18
- if (fresh) {
19
- return this.storage.run(context, fn)
20
- } else {
21
- const currentContext = this.get() || {}
22
- const mergedContext: ContextData = {
23
- distinctId: context.distinctId ?? currentContext.distinctId,
24
- sessionId: context.sessionId ?? currentContext.sessionId,
25
- properties: {
26
- ...(currentContext.properties || {}),
27
- ...(context.properties || {}),
28
- },
29
- }
30
- return this.storage.run(mergedContext, fn)
28
+ const current = this.get() || {}
29
+ return {
30
+ distinctId: context.distinctId ?? current.distinctId,
31
+ sessionId: context.sessionId ?? current.sessionId,
32
+ properties: {
33
+ ...(current.properties || {}),
34
+ ...(context.properties || {}),
35
+ },
31
36
  }
32
37
  }
33
38
  }
@@ -16,4 +16,5 @@ export interface ContextOptions {
16
16
  export interface IPostHogContext {
17
17
  get(): ContextData | undefined
18
18
  run<T>(context: ContextData, fn: () => T, options?: ContextOptions): T
19
+ enter(context: ContextData, options?: ContextOptions): void
19
20
  }
@@ -58,6 +58,19 @@ type FeatureFlagsPollerOptions = {
58
58
  strictLocalEvaluation?: boolean
59
59
  }
60
60
 
61
+ export type FeatureFlagEvaluationContext = {
62
+ distinctId: string
63
+ groups: Record<string, string>
64
+ personProperties: Record<string, any>
65
+ groupProperties: Record<string, Record<string, any>>
66
+ evaluationCache: Record<string, FeatureFlagValue>
67
+ }
68
+
69
+ type ComputeFlagAndPayloadOptions = {
70
+ matchValue?: FeatureFlagValue
71
+ skipLoadCheck?: boolean
72
+ }
73
+
61
74
  class FeatureFlagsPoller {
62
75
  pollingInterval: number
63
76
  personalApiKey: string
@@ -122,6 +135,22 @@ class FeatureFlagsPoller {
122
135
  }
123
136
  }
124
137
 
138
+ private createEvaluationContext(
139
+ distinctId: string,
140
+ groups: Record<string, string> = {},
141
+ personProperties: Record<string, any> = {},
142
+ groupProperties: Record<string, Record<string, any>> = {},
143
+ evaluationCache: Record<string, FeatureFlagValue> = {}
144
+ ): FeatureFlagEvaluationContext {
145
+ return {
146
+ distinctId,
147
+ groups,
148
+ personProperties,
149
+ groupProperties,
150
+ evaluationCache,
151
+ }
152
+ }
153
+
125
154
  async getFeatureFlag(
126
155
  key: string,
127
156
  distinctId: string,
@@ -141,14 +170,9 @@ class FeatureFlagsPoller {
141
170
  featureFlag = this.featureFlagsByKey[key]
142
171
 
143
172
  if (featureFlag !== undefined) {
173
+ const evaluationContext = this.createEvaluationContext(distinctId, groups, personProperties, groupProperties)
144
174
  try {
145
- const result = await this.computeFlagAndPayloadLocally(
146
- featureFlag,
147
- distinctId,
148
- groups,
149
- personProperties,
150
- groupProperties
151
- )
175
+ const result = await this.computeFlagAndPayloadLocally(featureFlag, evaluationContext)
152
176
  response = result.value
153
177
  this.logMsgIfDebug(() => console.debug(`Successfully computed flag locally: ${key} -> ${response}`))
154
178
  } catch (e) {
@@ -164,10 +188,7 @@ class FeatureFlagsPoller {
164
188
  }
165
189
 
166
190
  async getAllFlagsAndPayloads(
167
- distinctId: string,
168
- groups: Record<string, string> = {},
169
- personProperties: Record<string, any> = {},
170
- groupProperties: Record<string, Record<string, any>> = {},
191
+ evaluationContext: FeatureFlagEvaluationContext,
171
192
  flagKeysToExplicitlyEvaluate?: string[]
172
193
  ): Promise<{
173
194
  response: Record<string, FeatureFlagValue>
@@ -184,20 +205,17 @@ class FeatureFlagsPoller {
184
205
  ? flagKeysToExplicitlyEvaluate.map((key) => this.featureFlagsByKey[key]).filter(Boolean)
185
206
  : this.featureFlags
186
207
 
187
- // Create a shared evaluation cache to prevent memory leaks when processing many flags
188
- const sharedEvaluationCache: Record<string, FeatureFlagValue> = {}
208
+ const sharedEvaluationContext = {
209
+ ...evaluationContext,
210
+ evaluationCache: evaluationContext.evaluationCache ?? {},
211
+ }
189
212
 
190
213
  await Promise.all(
191
214
  flagsToEvaluate.map(async (flag) => {
192
215
  try {
193
216
  const { value: matchValue, payload: matchPayload } = await this.computeFlagAndPayloadLocally(
194
217
  flag,
195
- distinctId,
196
- groups,
197
- personProperties,
198
- groupProperties,
199
- undefined /* matchValue */,
200
- sharedEvaluationCache
218
+ sharedEvaluationContext
201
219
  )
202
220
  response[flag.key] = matchValue
203
221
  if (matchPayload) {
@@ -219,17 +237,14 @@ class FeatureFlagsPoller {
219
237
 
220
238
  async computeFlagAndPayloadLocally(
221
239
  flag: PostHogFeatureFlag,
222
- distinctId: string,
223
- groups: Record<string, string> = {},
224
- personProperties: Record<string, any> = {},
225
- groupProperties: Record<string, Record<string, any>> = {},
226
- matchValue?: FeatureFlagValue,
227
- evaluationCache?: Record<string, FeatureFlagValue>,
228
- skipLoadCheck: boolean = false
240
+ evaluationContext: FeatureFlagEvaluationContext,
241
+ options: ComputeFlagAndPayloadOptions = {}
229
242
  ): Promise<{
230
243
  value: FeatureFlagValue
231
244
  payload: JsonType | null
232
245
  }> {
246
+ const { matchValue, skipLoadCheck = false } = options
247
+
233
248
  // Only load flags if not already loaded and not skipping the check
234
249
  if (!skipLoadCheck) {
235
250
  await this.loadFeatureFlags()
@@ -245,14 +260,7 @@ class FeatureFlagsPoller {
245
260
  if (matchValue !== undefined) {
246
261
  flagValue = matchValue
247
262
  } else {
248
- flagValue = await this.computeFlagValueLocally(
249
- flag,
250
- distinctId,
251
- groups,
252
- personProperties,
253
- groupProperties,
254
- evaluationCache
255
- )
263
+ flagValue = await this.computeFlagValueLocally(flag, evaluationContext)
256
264
  }
257
265
 
258
266
  // Always compute payload based on the final flagValue (whether provided or computed)
@@ -263,12 +271,10 @@ class FeatureFlagsPoller {
263
271
 
264
272
  private async computeFlagValueLocally(
265
273
  flag: PostHogFeatureFlag,
266
- distinctId: string,
267
- groups: Record<string, string> = {},
268
- personProperties: Record<string, any> = {},
269
- groupProperties: Record<string, Record<string, any>> = {},
270
- evaluationCache: Record<string, FeatureFlagValue> = {}
274
+ evaluationContext: FeatureFlagEvaluationContext
271
275
  ): Promise<FeatureFlagValue> {
276
+ const { distinctId, groups, personProperties, groupProperties } = evaluationContext
277
+
272
278
  if (flag.ensure_experience_continuity) {
273
279
  throw new InconclusiveMatchError('Flag has experience continuity enabled')
274
280
  }
@@ -299,32 +305,30 @@ class FeatureFlagsPoller {
299
305
  return false
300
306
  }
301
307
 
308
+ if (
309
+ flag.bucketing_identifier === 'device_id' &&
310
+ (personProperties?.$device_id === undefined ||
311
+ personProperties?.$device_id === null ||
312
+ personProperties?.$device_id === '')
313
+ ) {
314
+ this.logMsgIfDebug(() =>
315
+ console.warn(`[FEATURE FLAGS] Ignoring bucketing_identifier for group flag: ${flag.key}`)
316
+ )
317
+ }
318
+
302
319
  const focusedGroupProperties = groupProperties[groupName]
303
- return await this.matchFeatureFlagProperties(
304
- flag,
305
- groups[groupName],
306
- focusedGroupProperties,
307
- evaluationCache,
308
- distinctId,
309
- groups,
310
- personProperties,
311
- groupProperties
312
- )
320
+ return await this.matchFeatureFlagProperties(flag, groups[groupName], focusedGroupProperties, evaluationContext)
313
321
  } else {
314
322
  const bucketingValue = this.getBucketingValueForFlag(flag, distinctId, personProperties)
315
323
  if (bucketingValue === undefined) {
324
+ this.logMsgIfDebug(() =>
325
+ console.warn(
326
+ `[FEATURE FLAGS] Can't compute feature flag: ${flag.key} without $device_id, falling back to server evaluation`
327
+ )
328
+ )
316
329
  throw new InconclusiveMatchError(`Can't compute feature flag: ${flag.key} without $device_id`)
317
330
  }
318
- return await this.matchFeatureFlagProperties(
319
- flag,
320
- bucketingValue,
321
- personProperties,
322
- evaluationCache,
323
- distinctId,
324
- groups,
325
- personProperties,
326
- groupProperties
327
- )
331
+ return await this.matchFeatureFlagProperties(flag, bucketingValue, personProperties, evaluationContext)
328
332
  }
329
333
  }
330
334
 
@@ -384,13 +388,10 @@ class FeatureFlagsPoller {
384
388
 
385
389
  private async evaluateFlagDependency(
386
390
  property: FlagProperty,
387
- distinctId: string,
388
- groups: Record<string, string>,
389
- personProperties: Record<string, any>,
390
- groupProperties: Record<string, Record<string, any>>,
391
391
  properties: Record<string, any>,
392
- evaluationCache: Record<string, FeatureFlagValue>
392
+ evaluationContext: FeatureFlagEvaluationContext
393
393
  ): Promise<boolean> {
394
+ const { evaluationCache } = evaluationContext
394
395
  const targetFlagKey = property.key
395
396
 
396
397
  if (!this.featureFlagsByKey) {
@@ -434,14 +435,7 @@ class FeatureFlagsPoller {
434
435
  } else {
435
436
  // Reuse full flag evaluation so dependencies respect person vs group bucketing rules.
436
437
  try {
437
- const depResult = await this.computeFlagValueLocally(
438
- depFlag,
439
- distinctId,
440
- groups,
441
- personProperties,
442
- groupProperties,
443
- evaluationCache
444
- )
438
+ const depResult = await this.computeFlagValueLocally(depFlag, evaluationContext)
445
439
  evaluationCache[depFlagKey] = depResult
446
440
  } catch (error) {
447
441
  throw new InconclusiveMatchError(
@@ -486,11 +480,7 @@ class FeatureFlagsPoller {
486
480
  flag: PostHogFeatureFlag,
487
481
  bucketingValue: string,
488
482
  properties: Record<string, any>,
489
- evaluationCache: Record<string, FeatureFlagValue> = {},
490
- distinctId: string = bucketingValue,
491
- groups: Record<string, string> = {},
492
- personProperties: Record<string, any> = {},
493
- groupProperties: Record<string, Record<string, any>> = {}
483
+ evaluationContext: FeatureFlagEvaluationContext
494
484
  ): Promise<FeatureFlagValue> {
495
485
  const flagFilters = flag.filters || {}
496
486
  const flagConditions = flagFilters.groups || []
@@ -499,19 +489,7 @@ class FeatureFlagsPoller {
499
489
 
500
490
  for (const condition of flagConditions) {
501
491
  try {
502
- if (
503
- await this.isConditionMatch(
504
- flag,
505
- bucketingValue,
506
- condition,
507
- properties,
508
- evaluationCache,
509
- distinctId,
510
- groups,
511
- personProperties,
512
- groupProperties
513
- )
514
- ) {
492
+ if (await this.isConditionMatch(flag, bucketingValue, condition, properties, evaluationContext)) {
515
493
  const variantOverride = condition.variant
516
494
  const flagVariants = flagFilters.multivariate?.variants || []
517
495
  if (variantOverride && flagVariants.some((variant) => variant.key === variantOverride)) {
@@ -551,11 +529,7 @@ class FeatureFlagsPoller {
551
529
  bucketingValue: string,
552
530
  condition: FeatureFlagCondition,
553
531
  properties: Record<string, any>,
554
- evaluationCache: Record<string, FeatureFlagValue> = {},
555
- distinctId: string = bucketingValue,
556
- groups: Record<string, string> = {},
557
- personProperties: Record<string, any> = {},
558
- groupProperties: Record<string, Record<string, any>> = {}
532
+ evaluationContext: FeatureFlagEvaluationContext
559
533
  ): Promise<boolean> {
560
534
  const rolloutPercentage = condition.rollout_percentage
561
535
  const warnFunction = (msg: string): void => {
@@ -569,15 +543,7 @@ class FeatureFlagsPoller {
569
543
  if (propertyType === 'cohort') {
570
544
  matches = matchCohort(prop, properties, this.cohorts, this.debugMode)
571
545
  } else if (propertyType === 'flag') {
572
- matches = await this.evaluateFlagDependency(
573
- prop,
574
- distinctId,
575
- groups,
576
- personProperties,
577
- groupProperties,
578
- properties,
579
- evaluationCache
580
- )
546
+ matches = await this.evaluateFlagDependency(prop, properties, evaluationContext)
581
547
  } else {
582
548
  matches = matchProperty(prop, properties, warnFunction)
583
549
  }