posthog-node 5.0.0 → 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.
package/lib/index.d.ts CHANGED
@@ -79,11 +79,11 @@ declare enum PostHogPersistedProperty {
79
79
  InstalledAppBuild = "installed_app_build",
80
80
  InstalledAppVersion = "installed_app_version",
81
81
  SessionReplay = "session_replay",
82
- DecideEndpointWasHit = "decide_endpoint_was_hit",
83
82
  SurveyLastSeenDate = "survey_last_seen_date",
84
83
  SurveysSeen = "surveys_seen",
85
84
  Surveys = "surveys",
86
- RemoteConfig = "remote_config"
85
+ RemoteConfig = "remote_config",
86
+ FlagsEndpointWasHit = "flags_endpoint_was_hit"
87
87
  }
88
88
  type PostHogFetchOptions = {
89
89
  method: 'GET' | 'POST' | 'PUT' | 'PATCH';
@@ -132,7 +132,7 @@ type PostHogRemoteConfig = {
132
132
  hasFeatureFlags?: boolean;
133
133
  };
134
134
  type FeatureFlagValue = string | boolean;
135
- type PostHogDecideResponse = Omit<PostHogRemoteConfig, 'surveys' | 'hasFeatureFlags'> & {
135
+ type PostHogFlagsResponse = Omit<PostHogRemoteConfig, 'surveys' | 'hasFeatureFlags'> & {
136
136
  featureFlags: {
137
137
  [key: string]: FeatureFlagValue;
138
138
  };
@@ -178,13 +178,13 @@ type PartialWithRequired<T, K extends keyof T> = {
178
178
  [P in Exclude<keyof T, K>]?: T[P];
179
179
  };
180
180
  /**
181
- * These are the fields we care about from PostHogDecideResponse for feature flags.
181
+ * These are the fields we care about from PostHogFlagsResponse for feature flags.
182
182
  */
183
- type PostHogFeatureFlagDetails = PartialWithRequired<PostHogDecideResponse, 'flags' | 'featureFlags' | 'featureFlagPayloads' | 'requestId'>;
183
+ type PostHogFeatureFlagDetails = PartialWithRequired<PostHogFlagsResponse, 'flags' | 'featureFlags' | 'featureFlagPayloads' | 'requestId'>;
184
184
  /**
185
185
  * Models legacy flags and payloads return type for many public methods.
186
186
  */
187
- type PostHogFlagsAndPayloadsResponse = Partial<Pick<PostHogDecideResponse, 'featureFlags' | 'featureFlagPayloads'>>;
187
+ type PostHogFlagsAndPayloadsResponse = Partial<Pick<PostHogFlagsResponse, 'featureFlags' | 'featureFlagPayloads'>>;
188
188
  type JsonType = string | number | boolean | null | {
189
189
  [key: string]: JsonType;
190
190
  } | Array<JsonType> | JsonType[];
@@ -494,7 +494,7 @@ declare abstract class PostHogCoreStateless {
494
494
  /***
495
495
  *** FEATURE FLAGS
496
496
  ***/
497
- protected getDecide(distinctId: string, groups?: Record<string, string | number>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, extraPayload?: Record<string, any>): Promise<PostHogDecideResponse | undefined>;
497
+ protected getFlags(distinctId: string, groups?: Record<string, string | number>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, extraPayload?: Record<string, any>): Promise<PostHogFlagsResponse | undefined>;
498
498
  protected getFeatureFlagStateless(key: string, distinctId: string, groups?: Record<string, string>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean): Promise<{
499
499
  response: FeatureFlagValue | undefined;
500
500
  requestId: string | undefined;
@@ -504,16 +504,16 @@ declare abstract class PostHogCoreStateless {
504
504
  requestId: string | undefined;
505
505
  } | undefined>;
506
506
  protected getFeatureFlagPayloadStateless(key: string, distinctId: string, groups?: Record<string, string>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean): Promise<JsonType | undefined>;
507
- protected getFeatureFlagPayloadsStateless(distinctId: string, groups?: Record<string, string>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean, flagKeysToEvaluate?: string[]): Promise<PostHogDecideResponse['featureFlagPayloads'] | undefined>;
507
+ protected getFeatureFlagPayloadsStateless(distinctId: string, groups?: Record<string, string>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean, flagKeysToEvaluate?: string[]): Promise<PostHogFlagsResponse['featureFlagPayloads'] | undefined>;
508
508
  protected getFeatureFlagsStateless(distinctId: string, groups?: Record<string, string | number>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean, flagKeysToEvaluate?: string[]): Promise<{
509
- flags: PostHogDecideResponse['featureFlags'] | undefined;
510
- payloads: PostHogDecideResponse['featureFlagPayloads'] | undefined;
511
- requestId: PostHogDecideResponse['requestId'] | undefined;
509
+ flags: PostHogFlagsResponse['featureFlags'] | undefined;
510
+ payloads: PostHogFlagsResponse['featureFlagPayloads'] | undefined;
511
+ requestId: PostHogFlagsResponse['requestId'] | undefined;
512
512
  }>;
513
513
  protected getFeatureFlagsAndPayloadsStateless(distinctId: string, groups?: Record<string, string | number>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean, flagKeysToEvaluate?: string[]): Promise<{
514
- flags: PostHogDecideResponse['featureFlags'] | undefined;
515
- payloads: PostHogDecideResponse['featureFlagPayloads'] | undefined;
516
- requestId: PostHogDecideResponse['requestId'] | undefined;
514
+ flags: PostHogFlagsResponse['featureFlags'] | undefined;
515
+ payloads: PostHogFlagsResponse['featureFlagPayloads'] | undefined;
516
+ requestId: PostHogFlagsResponse['requestId'] | undefined;
517
517
  }>;
518
518
  protected getFeatureFlagDetailsStateless(distinctId: string, groups?: Record<string, string | number>, personProperties?: Record<string, string>, groupProperties?: Record<string, Record<string, string>>, disableGeoip?: boolean, flagKeysToEvaluate?: string[]): Promise<PostHogFeatureFlagDetails | undefined>;
519
519
  /***
@@ -1340,7 +1340,7 @@ function snipLine(line, colno) {
1340
1340
  return newLine;
1341
1341
  }
1342
1342
 
1343
- var version = "5.0.0";
1343
+ var version = "5.1.0";
1344
1344
 
1345
1345
  var PostHogPersistedProperty;
1346
1346
  (function (PostHogPersistedProperty) {
@@ -1364,11 +1364,11 @@ var PostHogPersistedProperty;
1364
1364
  PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
1365
1365
  PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
1366
1366
  PostHogPersistedProperty["SessionReplay"] = "session_replay";
1367
- PostHogPersistedProperty["DecideEndpointWasHit"] = "decide_endpoint_was_hit";
1368
1367
  PostHogPersistedProperty["SurveyLastSeenDate"] = "survey_last_seen_date";
1369
1368
  PostHogPersistedProperty["SurveysSeen"] = "surveys_seen";
1370
1369
  PostHogPersistedProperty["Surveys"] = "surveys";
1371
1370
  PostHogPersistedProperty["RemoteConfig"] = "remote_config";
1371
+ PostHogPersistedProperty["FlagsEndpointWasHit"] = "flags_endpoint_was_hit";
1372
1372
  })(PostHogPersistedProperty || (PostHogPersistedProperty = {}));
1373
1373
  // Any key prefixed with `attr__` can be added
1374
1374
  var Compression;
@@ -1436,27 +1436,27 @@ var ActionStepStringMatching;
1436
1436
  ActionStepStringMatching["Regex"] = "regex";
1437
1437
  })(ActionStepStringMatching || (ActionStepStringMatching = {}));
1438
1438
 
1439
- const normalizeDecideResponse = (decideResponse) => {
1440
- if ('flags' in decideResponse) {
1441
- // Convert v4 format to v3 format
1442
- const featureFlags = getFlagValuesFromFlags(decideResponse.flags);
1443
- const featureFlagPayloads = getPayloadsFromFlags(decideResponse.flags);
1439
+ const normalizeFlagsResponse = (flagsResponse) => {
1440
+ if ('flags' in flagsResponse) {
1441
+ // Convert v2 format to v1 format
1442
+ const featureFlags = getFlagValuesFromFlags(flagsResponse.flags);
1443
+ const featureFlagPayloads = getPayloadsFromFlags(flagsResponse.flags);
1444
1444
  return {
1445
- ...decideResponse,
1445
+ ...flagsResponse,
1446
1446
  featureFlags,
1447
1447
  featureFlagPayloads,
1448
1448
  };
1449
1449
  }
1450
1450
  else {
1451
- // Convert v3 format to v4 format
1452
- const featureFlags = decideResponse.featureFlags ?? {};
1453
- const featureFlagPayloads = Object.fromEntries(Object.entries(decideResponse.featureFlagPayloads || {}).map(([k, v]) => [k, parsePayload(v)]));
1451
+ // Convert v1 format to v2 format
1452
+ const featureFlags = flagsResponse.featureFlags ?? {};
1453
+ const featureFlagPayloads = Object.fromEntries(Object.entries(flagsResponse.featureFlagPayloads || {}).map(([k, v]) => [k, parsePayload(v)]));
1454
1454
  const flags = Object.fromEntries(Object.entries(featureFlags).map(([key, value]) => [
1455
1455
  key,
1456
1456
  getFlagDetailFromFlagAndPayload(key, value, featureFlagPayloads[key]),
1457
1457
  ]));
1458
1458
  return {
1459
- ...decideResponse,
1459
+ ...flagsResponse,
1460
1460
  featureFlags,
1461
1461
  featureFlagPayloads,
1462
1462
  flags,
@@ -1519,90 +1519,6 @@ const parsePayload = (response) => {
1519
1519
  }
1520
1520
  };
1521
1521
 
1522
- // Rollout constants
1523
- const NEW_FLAGS_ROLLOUT_PERCENTAGE = 1;
1524
- // The fnv1a hashes of the tokens that are explicitly excluded from the rollout
1525
- // see https://github.com/PostHog/posthog-js-lite/blob/main/posthog-core/src/utils.ts#L84
1526
- // are hashed API tokens from our top 10 for each category supported by this SDK.
1527
- const NEW_FLAGS_EXCLUDED_HASHES = new Set([
1528
- // Node
1529
- '61be3dd8',
1530
- '96f6df5f',
1531
- '8cfdba9b',
1532
- 'bf027177',
1533
- 'e59430a8',
1534
- '7fa5500b',
1535
- '569798e9',
1536
- '04809ff7',
1537
- '0ebc61a5',
1538
- '32de7f98',
1539
- '3beeb69a',
1540
- '12d34ad9',
1541
- '733853ec',
1542
- '0645bb64',
1543
- '5dcbee21',
1544
- 'b1f95fa3',
1545
- '2189e408',
1546
- '82b460c2',
1547
- '3a8cc979',
1548
- '29ef8843',
1549
- '2cdbf767',
1550
- '38084b54',
1551
- // React Native
1552
- '50f9f8de',
1553
- '41d0df91',
1554
- '5c236689',
1555
- 'c11aedd3',
1556
- 'ada46672',
1557
- 'f4331ee1',
1558
- '42fed62a',
1559
- 'c957462c',
1560
- 'd62f705a',
1561
- // Web (lots of teams per org, hence lots of API tokens)
1562
- 'e0162666',
1563
- '01b3e5cf',
1564
- '441cef7f',
1565
- 'bb9cafee',
1566
- '8f348eb0',
1567
- 'b2553f3a',
1568
- '97469d7d',
1569
- '39f21a76',
1570
- '03706dcc',
1571
- '27d50569',
1572
- '307584a7',
1573
- '6433e92e',
1574
- '150c7fbb',
1575
- '49f57f22',
1576
- '3772f65b',
1577
- '01eb8256',
1578
- '3c9e9234',
1579
- 'f853c7f7',
1580
- 'c0ac4b67',
1581
- 'cd609d40',
1582
- '10ca9b1a',
1583
- '8a87f11b',
1584
- '8e8e5216',
1585
- '1f6b63b3',
1586
- 'db7943dd',
1587
- '79b7164c',
1588
- '07f78e33',
1589
- '2d21b6fd',
1590
- '952db5ee',
1591
- 'a7d3b43f',
1592
- '1924dd9c',
1593
- '84e1b8f6',
1594
- 'dff631b6',
1595
- 'c5aa8a79',
1596
- 'fa133a95',
1597
- '498a4508',
1598
- '24748755',
1599
- '98f3d658',
1600
- '21bbda67',
1601
- '7dbfed69',
1602
- 'be3ec24c',
1603
- 'fc80b8e2',
1604
- '75cc0998',
1605
- ]);
1606
1522
  const STRING_FORMAT = 'utf8';
1607
1523
  function assert(truthyValue, message) {
1608
1524
  if (!truthyValue || typeof truthyValue !== 'string' || isEmpty(truthyValue)) {
@@ -1649,30 +1565,6 @@ function safeSetTimeout(fn, timeout) {
1649
1565
  t?.unref && t?.unref();
1650
1566
  return t;
1651
1567
  }
1652
- // FNV-1a hash function
1653
- // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
1654
- // I know, I know, I'm rolling my own hash function, but I didn't want to take on
1655
- // a crypto dependency and this is just temporary anyway
1656
- function fnv1a(str) {
1657
- let hash = 0x811c9dc5; // FNV offset basis
1658
- for (let i = 0; i < str.length; i++) {
1659
- hash ^= str.charCodeAt(i);
1660
- hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
1661
- }
1662
- // Convert to hex string, padding to 8 chars
1663
- return (hash >>> 0).toString(16).padStart(8, '0');
1664
- }
1665
- function isTokenInRollout(token, percentage = 0, excludedHashes) {
1666
- const tokenHash = fnv1a(token);
1667
- // Check excluded hashes (we're explicitly including these tokens from the rollout)
1668
- if (excludedHashes?.has(tokenHash)) {
1669
- return false;
1670
- }
1671
- // Convert hash to int and divide by max value to get number between 0-1
1672
- const hashInt = parseInt(tokenHash, 16);
1673
- const hashFloat = hashInt / 0xffffffff;
1674
- return hashFloat < percentage;
1675
- }
1676
1568
  function allSettled(promises) {
1677
1569
  return Promise.all(promises.map((p) => (p ?? Promise.resolve()).then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason }))));
1678
1570
  }
@@ -1997,13 +1889,9 @@ class PostHogCoreStateless {
1997
1889
  /***
1998
1890
  *** FEATURE FLAGS
1999
1891
  ***/
2000
- async getDecide(distinctId, groups = {}, personProperties = {}, groupProperties = {}, extraPayload = {}) {
1892
+ async getFlags(distinctId, groups = {}, personProperties = {}, groupProperties = {}, extraPayload = {}) {
2001
1893
  await this._initPromise;
2002
- // Check if the API token is in the new flags rollout
2003
- // This is a temporary measure to ensure that we can still use the old flags API
2004
- // while we migrate to the new flags API
2005
- const useFlags = isTokenInRollout(this.apiKey, NEW_FLAGS_ROLLOUT_PERCENTAGE, NEW_FLAGS_EXCLUDED_HASHES);
2006
- const url = useFlags ? `${this.host}/flags/?v=2` : `${this.host}/decide/?v=4`;
1894
+ const url = `${this.host}/flags/?v=2&config=true`;
2007
1895
  const fetchOptions = {
2008
1896
  method: 'POST',
2009
1897
  headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
@@ -2016,11 +1904,11 @@ class PostHogCoreStateless {
2016
1904
  ...extraPayload,
2017
1905
  }),
2018
1906
  };
2019
- this.logMsgIfDebug(() => console.log('PostHog Debug', 'Decide URL', url));
2020
- // Don't retry /decide API calls
1907
+ this.logMsgIfDebug(() => console.log('PostHog Debug', 'Flags URL', url));
1908
+ // Don't retry /flags API calls
2021
1909
  return this.fetchWithRetry(url, fetchOptions, { retryCount: 0 }, this.featureFlagsRequestTimeoutMs)
2022
1910
  .then((response) => response.json())
2023
- .then((response) => normalizeDecideResponse(response))
1911
+ .then((response) => normalizeFlagsResponse(response))
2024
1912
  .catch((error) => {
2025
1913
  this._events.emit('error', error);
2026
1914
  return undefined;
@@ -2049,15 +1937,15 @@ class PostHogCoreStateless {
2049
1937
  }
2050
1938
  async getFeatureFlagDetailStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
2051
1939
  await this._initPromise;
2052
- const decideResponse = await this.getFeatureFlagDetailsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [key]);
2053
- if (decideResponse === undefined) {
1940
+ const flagsResponse = await this.getFeatureFlagDetailsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [key]);
1941
+ if (flagsResponse === undefined) {
2054
1942
  return undefined;
2055
1943
  }
2056
- const featureFlags = decideResponse.flags;
1944
+ const featureFlags = flagsResponse.flags;
2057
1945
  const flagDetail = featureFlags[key];
2058
1946
  return {
2059
1947
  response: flagDetail,
2060
- requestId: decideResponse.requestId,
1948
+ requestId: flagsResponse.requestId,
2061
1949
  };
2062
1950
  }
2063
1951
  async getFeatureFlagPayloadStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
@@ -2107,26 +1995,26 @@ class PostHogCoreStateless {
2107
1995
  if (flagKeysToEvaluate) {
2108
1996
  extraPayload['flag_keys_to_evaluate'] = flagKeysToEvaluate;
2109
1997
  }
2110
- const decideResponse = await this.getDecide(distinctId, groups, personProperties, groupProperties, extraPayload);
2111
- if (decideResponse === undefined) {
1998
+ const flagsResponse = await this.getFlags(distinctId, groups, personProperties, groupProperties, extraPayload);
1999
+ if (flagsResponse === undefined) {
2112
2000
  // We probably errored out, so return undefined
2113
2001
  return undefined;
2114
2002
  }
2115
- // if there's an error on the decideResponse, log a console error, but don't throw an error
2116
- if (decideResponse.errorsWhileComputingFlags) {
2003
+ // if there's an error on the flagsResponse, log a console error, but don't throw an error
2004
+ if (flagsResponse.errorsWhileComputingFlags) {
2117
2005
  console.error('[FEATURE FLAGS] Error while computing feature flags, some flags may be missing or incorrect. Learn more at https://posthog.com/docs/feature-flags/best-practices');
2118
2006
  }
2119
2007
  // Add check for quota limitation on feature flags
2120
- if (decideResponse.quotaLimited?.includes(QuotaLimitedFeature.FeatureFlags)) {
2008
+ if (flagsResponse.quotaLimited?.includes(QuotaLimitedFeature.FeatureFlags)) {
2121
2009
  console.warn('[FEATURE FLAGS] Feature flags quota limit exceeded - feature flags unavailable. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts');
2122
2010
  return {
2123
2011
  flags: {},
2124
2012
  featureFlags: {},
2125
2013
  featureFlagPayloads: {},
2126
- requestId: decideResponse?.requestId,
2014
+ requestId: flagsResponse?.requestId,
2127
2015
  };
2128
2016
  }
2129
- return decideResponse;
2017
+ return flagsResponse;
2130
2018
  }
2131
2019
  /***
2132
2020
  *** SURVEYS
@@ -2730,7 +2618,7 @@ class FeatureFlagsPoller {
2730
2618
  await this.loadFeatureFlags();
2731
2619
  const response = {};
2732
2620
  const payloads = {};
2733
- let fallbackToDecide = this.featureFlags.length == 0;
2621
+ let fallbackToFlags = this.featureFlags.length == 0;
2734
2622
  await Promise.all(this.featureFlags.map(async flag => {
2735
2623
  try {
2736
2624
  const matchValue = await this.computeFlagLocally(flag, distinctId, groups, personProperties, groupProperties);
@@ -2743,13 +2631,13 @@ class FeatureFlagsPoller {
2743
2631
  if (e instanceof InconclusiveMatchError) ; else if (e instanceof Error) {
2744
2632
  this.onError?.(new Error(`Error computing flag locally: ${flag.key}: ${e}`));
2745
2633
  }
2746
- fallbackToDecide = true;
2634
+ fallbackToFlags = true;
2747
2635
  }
2748
2636
  }));
2749
2637
  return {
2750
2638
  response,
2751
2639
  payloads,
2752
- fallbackToDecide
2640
+ fallbackToFlags
2753
2641
  };
2754
2642
  }
2755
2643
  async computeFlagLocally(flag, distinctId, groups = {}, personProperties = {}, groupProperties = {}) {
@@ -3714,13 +3602,13 @@ class PostHogBackendClient extends PostHogCoreStateless {
3714
3602
  const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(distinctId, groups, personProperties, groupProperties);
3715
3603
  let featureFlags = {};
3716
3604
  let featureFlagPayloads = {};
3717
- let fallbackToDecide = true;
3605
+ let fallbackToFlags = true;
3718
3606
  if (localEvaluationResult) {
3719
3607
  featureFlags = localEvaluationResult.response;
3720
3608
  featureFlagPayloads = localEvaluationResult.payloads;
3721
- fallbackToDecide = localEvaluationResult.fallbackToDecide;
3609
+ fallbackToFlags = localEvaluationResult.fallbackToFlags;
3722
3610
  }
3723
- if (fallbackToDecide && !onlyEvaluateLocally) {
3611
+ if (fallbackToFlags && !onlyEvaluateLocally) {
3724
3612
  const remoteEvaluationResult = await super.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip);
3725
3613
  featureFlags = {
3726
3614
  ...featureFlags,