@rpcbase/server 0.520.0 → 0.522.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.js CHANGED
@@ -448,6 +448,36 @@ const getDefaultRandom = () => ({
448
448
  let defaultGenerator;
449
449
  const uuidv7 = () => uuidv7obj().toString();
450
450
  const uuidv7obj = () => (defaultGenerator || (defaultGenerator = new V7Generator())).generate();
451
+ var types_PostHogPersistedProperty = /* @__PURE__ */ (function(PostHogPersistedProperty) {
452
+ PostHogPersistedProperty["AnonymousId"] = "anonymous_id";
453
+ PostHogPersistedProperty["DistinctId"] = "distinct_id";
454
+ PostHogPersistedProperty["Props"] = "props";
455
+ PostHogPersistedProperty["EnablePersonProcessing"] = "enable_person_processing";
456
+ PostHogPersistedProperty["PersonMode"] = "person_mode";
457
+ PostHogPersistedProperty["FeatureFlagDetails"] = "feature_flag_details";
458
+ PostHogPersistedProperty["FeatureFlags"] = "feature_flags";
459
+ PostHogPersistedProperty["FeatureFlagPayloads"] = "feature_flag_payloads";
460
+ PostHogPersistedProperty["BootstrapFeatureFlagDetails"] = "bootstrap_feature_flag_details";
461
+ PostHogPersistedProperty["BootstrapFeatureFlags"] = "bootstrap_feature_flags";
462
+ PostHogPersistedProperty["BootstrapFeatureFlagPayloads"] = "bootstrap_feature_flag_payloads";
463
+ PostHogPersistedProperty["OverrideFeatureFlags"] = "override_feature_flags";
464
+ PostHogPersistedProperty["Queue"] = "queue";
465
+ PostHogPersistedProperty["OptedOut"] = "opted_out";
466
+ PostHogPersistedProperty["SessionId"] = "session_id";
467
+ PostHogPersistedProperty["SessionStartTimestamp"] = "session_start_timestamp";
468
+ PostHogPersistedProperty["SessionLastTimestamp"] = "session_timestamp";
469
+ PostHogPersistedProperty["PersonProperties"] = "person_properties";
470
+ PostHogPersistedProperty["GroupProperties"] = "group_properties";
471
+ PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
472
+ PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
473
+ PostHogPersistedProperty["SessionReplay"] = "session_replay";
474
+ PostHogPersistedProperty["SurveyLastSeenDate"] = "survey_last_seen_date";
475
+ PostHogPersistedProperty["SurveysSeen"] = "surveys_seen";
476
+ PostHogPersistedProperty["Surveys"] = "surveys";
477
+ PostHogPersistedProperty["RemoteConfig"] = "remote_config";
478
+ PostHogPersistedProperty["FlagsEndpointWasHit"] = "flags_endpoint_was_hit";
479
+ return PostHogPersistedProperty;
480
+ })({});
451
481
  const DEFAULT_BLOCKED_UA_STRS = [
452
482
  "amazonbot",
453
483
  "amazonproductbot",
@@ -535,36 +565,6 @@ const isBlockedUA = function(ua, customBlockedUserAgents = []) {
535
565
  return -1 !== uaLower.indexOf(blockedUaLower);
536
566
  });
537
567
  };
538
- var types_PostHogPersistedProperty = /* @__PURE__ */ (function(PostHogPersistedProperty) {
539
- PostHogPersistedProperty["AnonymousId"] = "anonymous_id";
540
- PostHogPersistedProperty["DistinctId"] = "distinct_id";
541
- PostHogPersistedProperty["Props"] = "props";
542
- PostHogPersistedProperty["EnablePersonProcessing"] = "enable_person_processing";
543
- PostHogPersistedProperty["PersonMode"] = "person_mode";
544
- PostHogPersistedProperty["FeatureFlagDetails"] = "feature_flag_details";
545
- PostHogPersistedProperty["FeatureFlags"] = "feature_flags";
546
- PostHogPersistedProperty["FeatureFlagPayloads"] = "feature_flag_payloads";
547
- PostHogPersistedProperty["BootstrapFeatureFlagDetails"] = "bootstrap_feature_flag_details";
548
- PostHogPersistedProperty["BootstrapFeatureFlags"] = "bootstrap_feature_flags";
549
- PostHogPersistedProperty["BootstrapFeatureFlagPayloads"] = "bootstrap_feature_flag_payloads";
550
- PostHogPersistedProperty["OverrideFeatureFlags"] = "override_feature_flags";
551
- PostHogPersistedProperty["Queue"] = "queue";
552
- PostHogPersistedProperty["OptedOut"] = "opted_out";
553
- PostHogPersistedProperty["SessionId"] = "session_id";
554
- PostHogPersistedProperty["SessionStartTimestamp"] = "session_start_timestamp";
555
- PostHogPersistedProperty["SessionLastTimestamp"] = "session_timestamp";
556
- PostHogPersistedProperty["PersonProperties"] = "person_properties";
557
- PostHogPersistedProperty["GroupProperties"] = "group_properties";
558
- PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
559
- PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
560
- PostHogPersistedProperty["SessionReplay"] = "session_replay";
561
- PostHogPersistedProperty["SurveyLastSeenDate"] = "survey_last_seen_date";
562
- PostHogPersistedProperty["SurveysSeen"] = "surveys_seen";
563
- PostHogPersistedProperty["Surveys"] = "surveys";
564
- PostHogPersistedProperty["RemoteConfig"] = "remote_config";
565
- PostHogPersistedProperty["FlagsEndpointWasHit"] = "flags_endpoint_was_hit";
566
- return PostHogPersistedProperty;
567
- })({});
568
568
  const nativeIsArray = Array.isArray;
569
569
  const ObjProto = Object.prototype;
570
570
  const type_utils_toString = ObjProto.toString;
@@ -2111,7 +2111,8 @@ class ErrorTracking {
2111
2111
  properties: {
2112
2112
  ...exceptionProperties,
2113
2113
  ...properties
2114
- }
2114
+ },
2115
+ _originatedFromCaptureException: true
2115
2116
  };
2116
2117
  }
2117
2118
  startAutocaptureIfEnabled() {
@@ -2146,7 +2147,7 @@ class ErrorTracking {
2146
2147
  this._rateLimiter.stop();
2147
2148
  }
2148
2149
  }
2149
- const version = "5.24.11";
2150
+ const version = "5.26.0";
2150
2151
  const FeatureFlagError = {
2151
2152
  ERRORS_WHILE_COMPUTING: "errors_while_computing_flags",
2152
2153
  FLAG_MISSING: "flag_missing",
@@ -2220,32 +2221,47 @@ class FeatureFlagsPoller {
2220
2221
  logMsgIfDebug(fn) {
2221
2222
  if (this.debugMode) fn();
2222
2223
  }
2224
+ createEvaluationContext(distinctId, groups = {}, personProperties = {}, groupProperties = {}, evaluationCache = {}) {
2225
+ return {
2226
+ distinctId,
2227
+ groups,
2228
+ personProperties,
2229
+ groupProperties,
2230
+ evaluationCache
2231
+ };
2232
+ }
2223
2233
  async getFeatureFlag(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}) {
2224
2234
  await this.loadFeatureFlags();
2225
2235
  let response;
2226
2236
  let featureFlag;
2227
2237
  if (!this.loadedSuccessfullyOnce) return response;
2228
2238
  featureFlag = this.featureFlagsByKey[key];
2229
- if (void 0 !== featureFlag) try {
2230
- const result = await this.computeFlagAndPayloadLocally(featureFlag, distinctId, groups, personProperties, groupProperties);
2231
- response = result.value;
2232
- this.logMsgIfDebug(() => console.debug(`Successfully computed flag locally: ${key} -> ${response}`));
2233
- } catch (e) {
2234
- if (e instanceof RequiresServerEvaluation || e instanceof InconclusiveMatchError) this.logMsgIfDebug(() => console.debug(`${e.name} when computing flag locally: ${key}: ${e.message}`));
2235
- else if (e instanceof Error) this.onError?.(new Error(`Error computing flag locally: ${key}: ${e}`));
2239
+ if (void 0 !== featureFlag) {
2240
+ const evaluationContext = this.createEvaluationContext(distinctId, groups, personProperties, groupProperties);
2241
+ try {
2242
+ const result = await this.computeFlagAndPayloadLocally(featureFlag, evaluationContext);
2243
+ response = result.value;
2244
+ this.logMsgIfDebug(() => console.debug(`Successfully computed flag locally: ${key} -> ${response}`));
2245
+ } catch (e) {
2246
+ if (e instanceof RequiresServerEvaluation || e instanceof InconclusiveMatchError) this.logMsgIfDebug(() => console.debug(`${e.name} when computing flag locally: ${key}: ${e.message}`));
2247
+ else if (e instanceof Error) this.onError?.(new Error(`Error computing flag locally: ${key}: ${e}`));
2248
+ }
2236
2249
  }
2237
2250
  return response;
2238
2251
  }
2239
- async getAllFlagsAndPayloads(distinctId, groups = {}, personProperties = {}, groupProperties = {}, flagKeysToExplicitlyEvaluate) {
2252
+ async getAllFlagsAndPayloads(evaluationContext, flagKeysToExplicitlyEvaluate) {
2240
2253
  await this.loadFeatureFlags();
2241
2254
  const response = {};
2242
2255
  const payloads = {};
2243
2256
  let fallbackToFlags = 0 == this.featureFlags.length;
2244
2257
  const flagsToEvaluate = flagKeysToExplicitlyEvaluate ? flagKeysToExplicitlyEvaluate.map((key) => this.featureFlagsByKey[key]).filter(Boolean) : this.featureFlags;
2245
- const sharedEvaluationCache = {};
2258
+ const sharedEvaluationContext = {
2259
+ ...evaluationContext,
2260
+ evaluationCache: evaluationContext.evaluationCache ?? {}
2261
+ };
2246
2262
  await Promise.all(flagsToEvaluate.map(async (flag) => {
2247
2263
  try {
2248
- const { value: matchValue, payload: matchPayload } = await this.computeFlagAndPayloadLocally(flag, distinctId, groups, personProperties, groupProperties, void 0, sharedEvaluationCache);
2264
+ const { value: matchValue, payload: matchPayload } = await this.computeFlagAndPayloadLocally(flag, sharedEvaluationContext);
2249
2265
  response[flag.key] = matchValue;
2250
2266
  if (matchPayload) payloads[flag.key] = matchPayload;
2251
2267
  } catch (e) {
@@ -2260,27 +2276,28 @@ class FeatureFlagsPoller {
2260
2276
  fallbackToFlags
2261
2277
  };
2262
2278
  }
2263
- async computeFlagAndPayloadLocally(flag, distinctId, groups = {}, personProperties = {}, groupProperties = {}, matchValue, evaluationCache, skipLoadCheck = false) {
2279
+ async computeFlagAndPayloadLocally(flag, evaluationContext, options = {}) {
2280
+ const { matchValue, skipLoadCheck = false } = options;
2264
2281
  if (!skipLoadCheck) await this.loadFeatureFlags();
2265
2282
  if (!this.loadedSuccessfullyOnce) return {
2266
2283
  value: false,
2267
2284
  payload: null
2268
2285
  };
2269
2286
  let flagValue;
2270
- flagValue = void 0 !== matchValue ? matchValue : await this.computeFlagValueLocally(flag, distinctId, groups, personProperties, groupProperties, evaluationCache);
2287
+ flagValue = void 0 !== matchValue ? matchValue : await this.computeFlagValueLocally(flag, evaluationContext);
2271
2288
  const payload = this.getFeatureFlagPayload(flag.key, flagValue);
2272
2289
  return {
2273
2290
  value: flagValue,
2274
2291
  payload
2275
2292
  };
2276
2293
  }
2277
- async computeFlagValueLocally(flag, distinctId, groups = {}, personProperties = {}, groupProperties = {}, evaluationCache = {}) {
2294
+ async computeFlagValueLocally(flag, evaluationContext) {
2295
+ const { distinctId, groups, personProperties, groupProperties } = evaluationContext;
2278
2296
  if (flag.ensure_experience_continuity) throw new InconclusiveMatchError("Flag has experience continuity enabled");
2279
2297
  if (!flag.active) return false;
2280
2298
  const flagFilters = flag.filters || {};
2281
2299
  const aggregation_group_type_index = flagFilters.aggregation_group_type_index;
2282
- if (void 0 == aggregation_group_type_index) return await this.matchFeatureFlagProperties(flag, distinctId, personProperties, evaluationCache);
2283
- {
2300
+ if (void 0 != aggregation_group_type_index) {
2284
2301
  const groupName = this.groupTypeMapping[String(aggregation_group_type_index)];
2285
2302
  if (!groupName) {
2286
2303
  this.logMsgIfDebug(() => console.warn(`[FEATURE FLAGS] Unknown group type index ${aggregation_group_type_index} for feature flag ${flag.key}`));
@@ -2290,9 +2307,27 @@ class FeatureFlagsPoller {
2290
2307
  this.logMsgIfDebug(() => console.warn(`[FEATURE FLAGS] Can't compute group feature flag: ${flag.key} without group names passed in`));
2291
2308
  return false;
2292
2309
  }
2310
+ if ("device_id" === flag.bucketing_identifier && (personProperties?.$device_id === void 0 || personProperties?.$device_id === null || personProperties?.$device_id === "")) this.logMsgIfDebug(() => console.warn(`[FEATURE FLAGS] Ignoring bucketing_identifier for group flag: ${flag.key}`));
2293
2311
  const focusedGroupProperties = groupProperties[groupName];
2294
- return await this.matchFeatureFlagProperties(flag, groups[groupName], focusedGroupProperties, evaluationCache);
2312
+ return await this.matchFeatureFlagProperties(flag, groups[groupName], focusedGroupProperties, evaluationContext);
2295
2313
  }
2314
+ {
2315
+ const bucketingValue = this.getBucketingValueForFlag(flag, distinctId, personProperties);
2316
+ if (void 0 === bucketingValue) {
2317
+ this.logMsgIfDebug(() => console.warn(`[FEATURE FLAGS] Can't compute feature flag: ${flag.key} without $device_id, falling back to server evaluation`));
2318
+ throw new InconclusiveMatchError(`Can't compute feature flag: ${flag.key} without $device_id`);
2319
+ }
2320
+ return await this.matchFeatureFlagProperties(flag, bucketingValue, personProperties, evaluationContext);
2321
+ }
2322
+ }
2323
+ getBucketingValueForFlag(flag, distinctId, properties) {
2324
+ if (flag.filters?.aggregation_group_type_index != void 0) return distinctId;
2325
+ if ("device_id" === flag.bucketing_identifier) {
2326
+ const deviceId = properties?.$device_id;
2327
+ if (null == deviceId || "" === deviceId) return;
2328
+ return deviceId;
2329
+ }
2330
+ return distinctId;
2296
2331
  }
2297
2332
  getFeatureFlagPayload(key, flagValue) {
2298
2333
  let payload = null;
@@ -2310,7 +2345,8 @@ class FeatureFlagsPoller {
2310
2345
  }
2311
2346
  return null;
2312
2347
  }
2313
- async evaluateFlagDependency(property, distinctId, properties, evaluationCache) {
2348
+ async evaluateFlagDependency(property, properties, evaluationContext) {
2349
+ const { evaluationCache } = evaluationContext;
2314
2350
  const targetFlagKey = property.key;
2315
2351
  if (!this.featureFlagsByKey) throw new InconclusiveMatchError("Feature flags not available for dependency evaluation");
2316
2352
  if (!("dependency_chain" in property)) throw new InconclusiveMatchError(`Flag dependency property for '${targetFlagKey}' is missing required 'dependency_chain' field`);
@@ -2321,7 +2357,7 @@ class FeatureFlagsPoller {
2321
2357
  if (!(depFlagKey in evaluationCache)) {
2322
2358
  const depFlag = this.featureFlagsByKey[depFlagKey];
2323
2359
  if (depFlag) if (depFlag.active) try {
2324
- const depResult = await this.matchFeatureFlagProperties(depFlag, distinctId, properties, evaluationCache);
2360
+ const depResult = await this.computeFlagValueLocally(depFlag, evaluationContext);
2325
2361
  evaluationCache[depFlagKey] = depResult;
2326
2362
  } catch (error) {
2327
2363
  throw new InconclusiveMatchError(`Error evaluating flag dependency '${depFlagKey}' for flag '${targetFlagKey}': ${error}`);
@@ -2340,16 +2376,16 @@ class FeatureFlagsPoller {
2340
2376
  if ("string" == typeof expectedValue) return flagValue === expectedValue;
2341
2377
  return false;
2342
2378
  }
2343
- async matchFeatureFlagProperties(flag, distinctId, properties, evaluationCache = {}) {
2379
+ async matchFeatureFlagProperties(flag, bucketingValue, properties, evaluationContext) {
2344
2380
  const flagFilters = flag.filters || {};
2345
2381
  const flagConditions = flagFilters.groups || [];
2346
2382
  let isInconclusive = false;
2347
2383
  let result;
2348
2384
  for (const condition of flagConditions) try {
2349
- if (await this.isConditionMatch(flag, distinctId, condition, properties, evaluationCache)) {
2385
+ if (await this.isConditionMatch(flag, bucketingValue, condition, properties, evaluationContext)) {
2350
2386
  const variantOverride = condition.variant;
2351
2387
  const flagVariants = flagFilters.multivariate?.variants || [];
2352
- result = variantOverride && flagVariants.some((variant) => variant.key === variantOverride) ? variantOverride : await this.getMatchingVariant(flag, distinctId) || true;
2388
+ result = variantOverride && flagVariants.some((variant) => variant.key === variantOverride) ? variantOverride : await this.getMatchingVariant(flag, bucketingValue) || true;
2353
2389
  break;
2354
2390
  }
2355
2391
  } catch (e) {
@@ -2361,7 +2397,7 @@ class FeatureFlagsPoller {
2361
2397
  if (isInconclusive) throw new InconclusiveMatchError("Can't determine if feature flag is enabled or not with given properties");
2362
2398
  return false;
2363
2399
  }
2364
- async isConditionMatch(flag, distinctId, condition, properties, evaluationCache = {}) {
2400
+ async isConditionMatch(flag, bucketingValue, condition, properties, evaluationContext) {
2365
2401
  const rolloutPercentage = condition.rollout_percentage;
2366
2402
  const warnFunction = (msg) => {
2367
2403
  this.logMsgIfDebug(() => console.warn(msg));
@@ -2370,16 +2406,16 @@ class FeatureFlagsPoller {
2370
2406
  for (const prop of condition.properties) {
2371
2407
  const propertyType = prop.type;
2372
2408
  let matches = false;
2373
- matches = "cohort" === propertyType ? matchCohort(prop, properties, this.cohorts, this.debugMode) : "flag" === propertyType ? await this.evaluateFlagDependency(prop, distinctId, properties, evaluationCache) : matchProperty(prop, properties, warnFunction);
2409
+ matches = "cohort" === propertyType ? matchCohort(prop, properties, this.cohorts, this.debugMode) : "flag" === propertyType ? await this.evaluateFlagDependency(prop, properties, evaluationContext) : matchProperty(prop, properties, warnFunction);
2374
2410
  if (!matches) return false;
2375
2411
  }
2376
2412
  if (void 0 == rolloutPercentage) return true;
2377
2413
  }
2378
- if (void 0 != rolloutPercentage && await _hash(flag.key, distinctId) > rolloutPercentage / 100) return false;
2414
+ if (void 0 != rolloutPercentage && await _hash(flag.key, bucketingValue) > rolloutPercentage / 100) return false;
2379
2415
  return true;
2380
2416
  }
2381
- async getMatchingVariant(flag, distinctId) {
2382
- const hashValue = await _hash(flag.key, distinctId, "variant");
2417
+ async getMatchingVariant(flag, bucketingValue) {
2418
+ const hashValue = await _hash(flag.key, bucketingValue, "variant");
2383
2419
  const matchingVariant = this.variantLookupTable(flag).find((variant) => hashValue >= variant.valueMin && hashValue < variant.valueMax);
2384
2420
  if (matchingVariant) return matchingVariant.key;
2385
2421
  }
@@ -2567,8 +2603,8 @@ class FeatureFlagsPoller {
2567
2603
  }
2568
2604
  }
2569
2605
  }
2570
- async function _hash(key, distinctId, salt = "") {
2571
- const hashString = await hashSHA1(`${key}.${distinctId}${salt}`);
2606
+ async function _hash(key, bucketingValue, salt = "") {
2607
+ const hashString = await hashSHA1(`${key}.${bucketingValue}${salt}`);
2572
2608
  return parseInt(hashString.slice(0, 15), 16) / LONG_SCALE;
2573
2609
  }
2574
2610
  function matchProperty(property, propertyValues, warnFunction) {
@@ -2804,6 +2840,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
2804
2840
  }
2805
2841
  capture(props) {
2806
2842
  if ("string" == typeof props) this._logger.warn("Called capture() with a string as the first argument when an object was expected.");
2843
+ if ("$exception" === props.event && !props._originatedFromCaptureException) this._logger.warn("Using `posthog.capture('$exception')` is unreliable because it does not attach required metadata. Use `posthog.captureException(error)` instead, which attaches required metadata automatically.");
2807
2844
  this.addPendingPromise(this.prepareEventMessage(props).then(({ distinctId, event, properties, options }) => super.captureStateless(distinctId, event, properties, {
2808
2845
  timestamp: options.timestamp,
2809
2846
  disableGeoip: options.disableGeoip,
@@ -2814,6 +2851,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
2814
2851
  }
2815
2852
  async captureImmediate(props) {
2816
2853
  if ("string" == typeof props) this._logger.warn("Called captureImmediate() with a string as the first argument when an object was expected.");
2854
+ if ("$exception" === props.event && !props._originatedFromCaptureException) this._logger.warn("Capturing a `$exception` event via `posthog.captureImmediate('$exception')` is unreliable because it does not attach required metadata. Use `posthog.captureExceptionImmediate(error)` instead, which attaches this metadata by default.");
2817
2855
  return this.addPendingPromise(this.prepareEventMessage(props).then(({ distinctId, event, properties, options }) => super.captureStatelessImmediate(distinctId, event, properties, {
2818
2856
  timestamp: options.timestamp,
2819
2857
  disableGeoip: options.disableGeoip,
@@ -2876,6 +2914,16 @@ class PostHogBackendClient extends PostHogCoreStateless {
2876
2914
  });
2877
2915
  });
2878
2916
  }
2917
+ _resolveDistinctId(distinctIdOrOptions, options) {
2918
+ if ("string" == typeof distinctIdOrOptions) return {
2919
+ distinctId: distinctIdOrOptions,
2920
+ options
2921
+ };
2922
+ return {
2923
+ distinctId: this.context?.get()?.distinctId,
2924
+ options: distinctIdOrOptions
2925
+ };
2926
+ }
2879
2927
  async _getFeatureFlagResult(key, distinctId, options = {}, matchValue) {
2880
2928
  const sendFeatureFlagEvents = options.sendFeatureFlagEvents ?? true;
2881
2929
  if (void 0 !== this._flagOverrides && key in this._flagOverrides) {
@@ -2894,6 +2942,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
2894
2942
  const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
2895
2943
  personProperties = adjustedProperties.allPersonProperties;
2896
2944
  groupProperties = adjustedProperties.allGroupProperties;
2945
+ const evaluationContext = this.createFeatureFlagEvaluationContext(distinctId, groups, personProperties, groupProperties);
2897
2946
  if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = this.options.strictLocalEvaluation ?? false;
2898
2947
  let result;
2899
2948
  let flagWasLocallyEvaluated = false;
@@ -2908,7 +2957,9 @@ class PostHogBackendClient extends PostHogCoreStateless {
2908
2957
  await this.featureFlagsPoller?.loadFeatureFlags();
2909
2958
  const flag = this.featureFlagsPoller?.featureFlagsByKey[key];
2910
2959
  if (flag) try {
2911
- const localResult = await this.featureFlagsPoller?.computeFlagAndPayloadLocally(flag, distinctId, groups, personProperties, groupProperties, matchValue);
2960
+ const localResult = await this.featureFlagsPoller?.computeFlagAndPayloadLocally(flag, evaluationContext, {
2961
+ matchValue
2962
+ });
2912
2963
  if (localResult) {
2913
2964
  flagWasLocallyEvaluated = true;
2914
2965
  const value = localResult.value;
@@ -2927,7 +2978,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
2927
2978
  }
2928
2979
  }
2929
2980
  if (!flagWasLocallyEvaluated && !onlyEvaluateLocally) {
2930
- const flagsResponse = await super.getFeatureFlagDetailsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [
2981
+ const flagsResponse = await super.getFeatureFlagDetailsStateless(evaluationContext.distinctId, evaluationContext.groups, evaluationContext.personProperties, evaluationContext.groupProperties, disableGeoip, [
2931
2982
  key
2932
2983
  ]);
2933
2984
  if (void 0 === flagsResponse) featureFlagError = FeatureFlagError.UNKNOWN_ERROR;
@@ -3013,10 +3064,12 @@ class PostHogBackendClient extends PostHogCoreStateless {
3013
3064
  if (void 0 === result) return;
3014
3065
  return result.payload ?? null;
3015
3066
  }
3016
- async getFeatureFlagResult(key, distinctId, options) {
3017
- return this._getFeatureFlagResult(key, distinctId, {
3018
- ...options,
3019
- sendFeatureFlagEvents: options?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true
3067
+ async getFeatureFlagResult(key, distinctIdOrOptions, options) {
3068
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(distinctIdOrOptions, options);
3069
+ if (!resolvedDistinctId) return void this._logger.warn("[PostHog] distinctId is required — pass it explicitly or use withContext()");
3070
+ return this._getFeatureFlagResult(key, resolvedDistinctId, {
3071
+ ...resolvedOptions,
3072
+ sendFeatureFlagEvents: resolvedOptions?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true
3020
3073
  });
3021
3074
  }
3022
3075
  async getRemoteConfigPayload(flagKey) {
@@ -3035,18 +3088,32 @@ class PostHogBackendClient extends PostHogCoreStateless {
3035
3088
  if (void 0 === feat) return;
3036
3089
  return !!feat || false;
3037
3090
  }
3038
- async getAllFlags(distinctId, options) {
3039
- const response = await this.getAllFlagsAndPayloads(distinctId, options);
3091
+ async getAllFlags(distinctIdOrOptions, options) {
3092
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(distinctIdOrOptions, options);
3093
+ if (!resolvedDistinctId) {
3094
+ this._logger.warn("[PostHog] distinctId is required to get feature flags — pass it explicitly or use withContext()");
3095
+ return {};
3096
+ }
3097
+ const response = await this.getAllFlagsAndPayloads(resolvedDistinctId, resolvedOptions);
3040
3098
  return response.featureFlags || {};
3041
3099
  }
3042
- async getAllFlagsAndPayloads(distinctId, options) {
3043
- const { groups, disableGeoip, flagKeys } = options || {};
3044
- let { onlyEvaluateLocally, personProperties, groupProperties } = options || {};
3045
- const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
3100
+ async getAllFlagsAndPayloads(distinctIdOrOptions, options) {
3101
+ const { distinctId: resolvedDistinctId, options: resolvedOptions } = this._resolveDistinctId(distinctIdOrOptions, options);
3102
+ if (!resolvedDistinctId) {
3103
+ this._logger.warn("[PostHog] distinctId is required to get feature flags and payloads — pass it explicitly or use withContext()");
3104
+ return {
3105
+ featureFlags: {},
3106
+ featureFlagPayloads: {}
3107
+ };
3108
+ }
3109
+ const { groups, disableGeoip, flagKeys } = resolvedOptions || {};
3110
+ let { onlyEvaluateLocally, personProperties, groupProperties } = resolvedOptions || {};
3111
+ const adjustedProperties = this.addLocalPersonAndGroupProperties(resolvedDistinctId, groups, personProperties, groupProperties);
3046
3112
  personProperties = adjustedProperties.allPersonProperties;
3047
3113
  groupProperties = adjustedProperties.allGroupProperties;
3114
+ const evaluationContext = this.createFeatureFlagEvaluationContext(resolvedDistinctId, groups, personProperties, groupProperties);
3048
3115
  if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = this.options.strictLocalEvaluation ?? false;
3049
- const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(distinctId, groups, personProperties, groupProperties, flagKeys);
3116
+ const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(evaluationContext, flagKeys);
3050
3117
  let featureFlags = {};
3051
3118
  let featureFlagPayloads = {};
3052
3119
  let fallbackToFlags = true;
@@ -3056,7 +3123,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
3056
3123
  fallbackToFlags = localEvaluationResult.fallbackToFlags;
3057
3124
  }
3058
3125
  if (fallbackToFlags && !onlyEvaluateLocally) {
3059
- const remoteEvaluationResult = await super.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, flagKeys);
3126
+ const remoteEvaluationResult = await super.getFeatureFlagsAndPayloadsStateless(evaluationContext.distinctId, evaluationContext.groups, evaluationContext.personProperties, evaluationContext.groupProperties, disableGeoip, flagKeys);
3060
3127
  featureFlags = {
3061
3128
  ...featureFlags,
3062
3129
  ...remoteEvaluationResult.flags || {}
@@ -3141,6 +3208,9 @@ class PostHogBackendClient extends PostHogCoreStateless {
3141
3208
  getContext() {
3142
3209
  return this.context?.get();
3143
3210
  }
3211
+ enterContext(data, options) {
3212
+ this.context?.enter(data, options);
3213
+ }
3144
3214
  async _shutdown(shutdownTimeoutMs) {
3145
3215
  this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
3146
3216
  this.errorTracking.shutdown();
@@ -3238,6 +3308,15 @@ class PostHogBackendClient extends PostHogCoreStateless {
3238
3308
  allGroupProperties
3239
3309
  };
3240
3310
  }
3311
+ createFeatureFlagEvaluationContext(distinctId, groups, personProperties, groupProperties) {
3312
+ return {
3313
+ distinctId,
3314
+ groups: groups || {},
3315
+ personProperties: personProperties || {},
3316
+ groupProperties: groupProperties || {},
3317
+ evaluationCache: {}
3318
+ };
3319
+ }
3241
3320
  captureException(error, distinctId, additionalProperties, uuid) {
3242
3321
  if (!ErrorTracking.isPreviouslyCapturedError(error)) {
3243
3322
  const syntheticException = new Error("PostHog syntheticException");
@@ -3262,6 +3341,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
3262
3341
  const contextData = this.context?.get();
3263
3342
  let mergedDistinctId = distinctId || contextData?.distinctId;
3264
3343
  const mergedProperties = {
3344
+ ...this.props,
3265
3345
  ...contextData?.properties || {},
3266
3346
  ...properties || {}
3267
3347
  };
@@ -3348,20 +3428,22 @@ class PostHogContext {
3348
3428
  return this.storage.getStore();
3349
3429
  }
3350
3430
  run(context, fn, options) {
3351
- const fresh = options?.fresh === true;
3352
- if (fresh) return this.storage.run(context, fn);
3353
- {
3354
- const currentContext = this.get() || {};
3355
- const mergedContext = {
3356
- distinctId: context.distinctId ?? currentContext.distinctId,
3357
- sessionId: context.sessionId ?? currentContext.sessionId,
3358
- properties: {
3359
- ...currentContext.properties || {},
3360
- ...context.properties || {}
3361
- }
3362
- };
3363
- return this.storage.run(mergedContext, fn);
3364
- }
3431
+ return this.storage.run(this.resolve(context, options), fn);
3432
+ }
3433
+ enter(context, options) {
3434
+ this.storage.enterWith(this.resolve(context, options));
3435
+ }
3436
+ resolve(context, options) {
3437
+ if (options?.fresh === true) return context;
3438
+ const current = this.get() || {};
3439
+ return {
3440
+ distinctId: context.distinctId ?? current.distinctId,
3441
+ sessionId: context.sessionId ?? current.sessionId,
3442
+ properties: {
3443
+ ...current.properties || {},
3444
+ ...context.properties || {}
3445
+ }
3446
+ };
3365
3447
  }
3366
3448
  }
3367
3449
  function setupExpressErrorHandler(_posthog, app) {