posthog-node 4.2.0 → 4.2.2

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.cjs.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var rusha = require('rusha');
6
6
 
7
- var version = "4.2.0";
7
+ var version = "4.2.2";
8
8
 
9
9
  var PostHogPersistedProperty;
10
10
  (function (PostHogPersistedProperty) {
@@ -22,6 +22,7 @@ var PostHogPersistedProperty;
22
22
  PostHogPersistedProperty["GroupProperties"] = "group_properties";
23
23
  PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
24
24
  PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
25
+ PostHogPersistedProperty["SessionReplay"] = "session_replay";
25
26
  })(PostHogPersistedProperty || (PostHogPersistedProperty = {}));
26
27
 
27
28
  function assert(truthyValue, message) {
@@ -966,7 +967,7 @@ class PostHogCoreStateless {
966
967
  this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
967
968
  this.maxQueueSize = Math.max(this.flushAt, options?.maxQueueSize ?? 1000);
968
969
  this.flushInterval = options?.flushInterval ?? 10000;
969
- this.captureMode = options?.captureMode || 'form';
970
+ this.captureMode = options?.captureMode || 'json';
970
971
  // If enable is explicitly set to false we override the optout
971
972
  this.defaultOptIn = options?.defaultOptIn ?? true;
972
973
  this._retryOptions = {
@@ -983,11 +984,14 @@ class PostHogCoreStateless {
983
984
  this._initPromise = Promise.resolve();
984
985
  this._isInitialized = true;
985
986
  }
987
+ logMsgIfDebug(fn) {
988
+ if (this.isDebug) {
989
+ fn();
990
+ }
991
+ }
986
992
  wrap(fn) {
987
993
  if (this.disabled) {
988
- if (this.isDebug) {
989
- console.warn('[PostHog] The client is disabled');
990
- }
994
+ this.logMsgIfDebug(() => console.warn('[PostHog] The client is disabled'));
991
995
  return;
992
996
  }
993
997
  if (this._isInitialized) {
@@ -1031,6 +1035,9 @@ class PostHogCoreStateless {
1031
1035
  get isDebug() {
1032
1036
  return !!this.removeDebugCallback;
1033
1037
  }
1038
+ get isDisabled() {
1039
+ return this.disabled;
1040
+ }
1034
1041
  buildPayload(payload) {
1035
1042
  return {
1036
1043
  distinct_id: payload.distinct_id,
@@ -1227,7 +1234,7 @@ class PostHogCoreStateless {
1227
1234
  const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
1228
1235
  if (queue.length >= this.maxQueueSize) {
1229
1236
  queue.shift();
1230
- console.info('Queue is full, the oldest event is dropped.');
1237
+ this.logMsgIfDebug(() => console.info('Queue is full, the oldest event is dropped.'));
1231
1238
  }
1232
1239
  queue.push({ message });
1233
1240
  this.setPersistedProperty(PostHogPersistedProperty.Queue, queue);
@@ -1359,33 +1366,45 @@ class PostHogCoreStateless {
1359
1366
  }, { ...this._retryOptions, ...retryOptions });
1360
1367
  }
1361
1368
  async shutdown(shutdownTimeoutMs = 30000) {
1369
+ // A little tricky - we want to have a max shutdown time and enforce it, even if that means we have some
1370
+ // dangling promises. We'll keep track of the timeout and resolve/reject based on that.
1362
1371
  await this._initPromise;
1372
+ let hasTimedOut = false;
1363
1373
  this.clearFlushTimer();
1364
- try {
1365
- await Promise.all(Object.values(this.pendingPromises));
1366
- const startTimeWithDelay = Date.now() + shutdownTimeoutMs;
1367
- while (true) {
1368
- const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
1369
- if (queue.length === 0) {
1370
- break;
1371
- }
1372
- // flush again to make sure we send all events, some of which might've been added
1373
- // while we were waiting for the pending promises to resolve
1374
- // For example, see sendFeatureFlags in posthog-node/src/posthog-node.ts::capture
1375
- await this.flush();
1376
- // If we've been waiting for more than the shutdownTimeoutMs, stop it
1377
- const now = Date.now();
1378
- if (startTimeWithDelay < now) {
1379
- break;
1374
+ const doShutdown = async () => {
1375
+ try {
1376
+ await Promise.all(Object.values(this.pendingPromises));
1377
+ while (true) {
1378
+ const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
1379
+ if (queue.length === 0) {
1380
+ break;
1381
+ }
1382
+ // flush again to make sure we send all events, some of which might've been added
1383
+ // while we were waiting for the pending promises to resolve
1384
+ // For example, see sendFeatureFlags in posthog-node/src/posthog-node.ts::capture
1385
+ await this.flush();
1386
+ if (hasTimedOut) {
1387
+ break;
1388
+ }
1380
1389
  }
1381
1390
  }
1382
- }
1383
- catch (e) {
1384
- if (!isPostHogFetchError(e)) {
1385
- throw e;
1391
+ catch (e) {
1392
+ if (!isPostHogFetchError(e)) {
1393
+ throw e;
1394
+ }
1395
+ this.logMsgIfDebug(() => console.error('Error while shutting down PostHog', e));
1386
1396
  }
1387
- console.error('Error while shutting down PostHog', e);
1388
- }
1397
+ };
1398
+ return Promise.race([
1399
+ new Promise((_, reject) => {
1400
+ safeSetTimeout(() => {
1401
+ this.logMsgIfDebug(() => console.error('Timed out while shutting down PostHog'));
1402
+ hasTimedOut = true;
1403
+ reject('Timeout while shutting down PostHog. Some events may not have been sent.');
1404
+ }, shutdownTimeoutMs);
1405
+ }),
1406
+ doShutdown(),
1407
+ ]);
1389
1408
  }
1390
1409
  }
1391
1410
 
@@ -1409,14 +1428,13 @@ class PostHogMemoryStorage {
1409
1428
  * This is currently solved by using the global fetch if available instead.
1410
1429
  * See https://github.com/PostHog/posthog-js-lite/issues/127 for more info
1411
1430
  */
1412
- let _fetch = // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
1431
+ let _fetch =
1432
+ // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
1413
1433
  // @ts-ignore
1414
1434
  typeof fetch !== 'undefined' ? fetch : typeof global.fetch !== 'undefined' ? global.fetch : undefined;
1415
-
1416
1435
  if (!_fetch) {
1417
1436
  // eslint-disable-next-line @typescript-eslint/no-var-requires
1418
1437
  const axios = require('axios');
1419
-
1420
1438
  _fetch = async (url, options) => {
1421
1439
  const res = await axios.request({
1422
1440
  url,
@@ -1433,14 +1451,13 @@ if (!_fetch) {
1433
1451
  json: async () => res.data
1434
1452
  };
1435
1453
  };
1436
- } // NOTE: We have to export this as default, even though we prefer named exports as we are relying on detecting "fetch" in the global scope
1437
-
1438
-
1454
+ }
1455
+ // NOTE: We have to export this as default, even though we prefer named exports as we are relying on detecting "fetch" in the global scope
1439
1456
  var fetch$1 = _fetch;
1440
1457
 
1458
+ // eslint-disable-next-line
1441
1459
  const LONG_SCALE = 0xfffffffffffffff;
1442
1460
  const NULL_VALUES_ALLOWED_OPERATORS = ['is_not'];
1443
-
1444
1461
  class ClientError extends Error {
1445
1462
  constructor(message) {
1446
1463
  super();
@@ -1449,22 +1466,18 @@ class ClientError extends Error {
1449
1466
  this.message = message;
1450
1467
  Object.setPrototypeOf(this, ClientError.prototype);
1451
1468
  }
1452
-
1453
1469
  }
1454
-
1455
1470
  class InconclusiveMatchError extends Error {
1456
1471
  constructor(message) {
1457
1472
  super(message);
1458
1473
  this.name = this.constructor.name;
1459
- Error.captureStackTrace(this, this.constructor); // instanceof doesn't work in ES3 or ES5
1474
+ Error.captureStackTrace(this, this.constructor);
1475
+ // instanceof doesn't work in ES3 or ES5
1460
1476
  // https://www.dannyguo.com/blog/how-to-fix-instanceof-not-working-for-custom-errors-in-typescript/
1461
1477
  // this is the workaround
1462
-
1463
1478
  Object.setPrototypeOf(this, InconclusiveMatchError.prototype);
1464
1479
  }
1465
-
1466
1480
  }
1467
-
1468
1481
  class FeatureFlagsPoller {
1469
1482
  constructor({
1470
1483
  pollingInterval,
@@ -1486,81 +1499,69 @@ class FeatureFlagsPoller {
1486
1499
  this.timeout = timeout;
1487
1500
  this.projectApiKey = projectApiKey;
1488
1501
  this.host = host;
1489
- this.poller = undefined; // NOTE: as any is required here as the AbortSignal typing is slightly misaligned but works just fine
1490
-
1502
+ this.poller = undefined;
1503
+ // NOTE: as any is required here as the AbortSignal typing is slightly misaligned but works just fine
1491
1504
  this.fetch = options.fetch || fetch$1;
1492
1505
  this.onError = options.onError;
1493
1506
  this.customHeaders = customHeaders;
1494
1507
  void this.loadFeatureFlags();
1495
1508
  }
1496
-
1497
1509
  debug(enabled = true) {
1498
1510
  this.debugMode = enabled;
1499
1511
  }
1500
-
1512
+ logMsgIfDebug(fn) {
1513
+ if (this.debugMode) {
1514
+ fn();
1515
+ }
1516
+ }
1501
1517
  async getFeatureFlag(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}) {
1502
1518
  await this.loadFeatureFlags();
1503
1519
  let response = undefined;
1504
1520
  let featureFlag = undefined;
1505
-
1506
1521
  if (!this.loadedSuccessfullyOnce) {
1507
1522
  return response;
1508
1523
  }
1509
-
1510
1524
  for (const flag of this.featureFlags) {
1511
1525
  if (key === flag.key) {
1512
1526
  featureFlag = flag;
1513
1527
  break;
1514
1528
  }
1515
1529
  }
1516
-
1517
1530
  if (featureFlag !== undefined) {
1518
1531
  try {
1519
1532
  response = this.computeFlagLocally(featureFlag, distinctId, groups, personProperties, groupProperties);
1520
-
1521
- if (this.debugMode) {
1522
- console.debug(`Successfully computed flag locally: ${key} -> ${response}`);
1523
- }
1533
+ this.logMsgIfDebug(() => console.debug(`Successfully computed flag locally: ${key} -> ${response}`));
1524
1534
  } catch (e) {
1525
1535
  if (e instanceof InconclusiveMatchError) {
1526
- if (this.debugMode) {
1527
- console.debug(`InconclusiveMatchError when computing flag locally: ${key}: ${e}`);
1528
- }
1536
+ this.logMsgIfDebug(() => console.debug(`InconclusiveMatchError when computing flag locally: ${key}: ${e}`));
1529
1537
  } else if (e instanceof Error) {
1530
1538
  this.onError?.(new Error(`Error computing flag locally: ${key}: ${e}`));
1531
1539
  }
1532
1540
  }
1533
1541
  }
1534
-
1535
1542
  return response;
1536
1543
  }
1537
-
1538
1544
  async computeFeatureFlagPayloadLocally(key, matchValue) {
1539
1545
  await this.loadFeatureFlags();
1540
1546
  let response = undefined;
1541
-
1542
1547
  if (!this.loadedSuccessfullyOnce) {
1543
1548
  return undefined;
1544
1549
  }
1545
-
1546
1550
  if (typeof matchValue == 'boolean') {
1547
1551
  response = this.featureFlagsByKey?.[key]?.filters?.payloads?.[matchValue.toString()];
1548
1552
  } else if (typeof matchValue == 'string') {
1549
1553
  response = this.featureFlagsByKey?.[key]?.filters?.payloads?.[matchValue];
1550
- } // Undefined means a loading or missing data issue. Null means evaluation happened and there was no match
1551
-
1552
-
1554
+ }
1555
+ // Undefined means a loading or missing data issue. Null means evaluation happened and there was no match
1553
1556
  if (response === undefined || response === null) {
1554
1557
  return null;
1555
1558
  }
1556
-
1557
1559
  try {
1558
1560
  return JSON.parse(response);
1559
1561
  } catch {
1560
1562
  return response;
1561
1563
  }
1562
1564
  }
1563
-
1564
1565
  async getAllFlagsAndPayloads(distinctId, groups = {}, personProperties = {}, groupProperties = {}) {
1565
1566
  await this.loadFeatureFlags();
1566
1567
  const response = {};
@@ -1571,7 +1572,6 @@ class FeatureFlagsPoller {
1571
1572
  const matchValue = this.computeFlagLocally(flag, distinctId, groups, personProperties, groupProperties);
1572
1573
  response[flag.key] = matchValue;
1573
1574
  const matchPayload = await this.computeFeatureFlagPayloadLocally(flag.key, matchValue);
1574
-
1575
1575
  if (matchPayload) {
1576
1576
  payloads[flag.key] = matchPayload;
1577
1577
  }
@@ -1579,7 +1579,6 @@ class FeatureFlagsPoller {
1579
1579
  if (e instanceof InconclusiveMatchError) ; else if (e instanceof Error) {
1580
1580
  this.onError?.(new Error(`Error computing flag locally: ${flag.key}: ${e}`));
1581
1581
  }
1582
-
1583
1582
  fallbackToDecide = true;
1584
1583
  }
1585
1584
  });
@@ -1589,56 +1588,41 @@ class FeatureFlagsPoller {
1589
1588
  fallbackToDecide
1590
1589
  };
1591
1590
  }
1592
-
1593
1591
  computeFlagLocally(flag, distinctId, groups = {}, personProperties = {}, groupProperties = {}) {
1594
1592
  if (flag.ensure_experience_continuity) {
1595
1593
  throw new InconclusiveMatchError('Flag has experience continuity enabled');
1596
1594
  }
1597
-
1598
1595
  if (!flag.active) {
1599
1596
  return false;
1600
1597
  }
1601
-
1602
1598
  const flagFilters = flag.filters || {};
1603
1599
  const aggregation_group_type_index = flagFilters.aggregation_group_type_index;
1604
-
1605
1600
  if (aggregation_group_type_index != undefined) {
1606
1601
  const groupName = this.groupTypeMapping[String(aggregation_group_type_index)];
1607
-
1608
1602
  if (!groupName) {
1609
- if (this.debugMode) {
1610
- console.warn(`[FEATURE FLAGS] Unknown group type index ${aggregation_group_type_index} for feature flag ${flag.key}`);
1611
- }
1612
-
1603
+ this.logMsgIfDebug(() => console.warn(`[FEATURE FLAGS] Unknown group type index ${aggregation_group_type_index} for feature flag ${flag.key}`));
1613
1604
  throw new InconclusiveMatchError('Flag has unknown group type index');
1614
1605
  }
1615
-
1616
1606
  if (!(groupName in groups)) {
1617
- if (this.debugMode) {
1618
- console.warn(`[FEATURE FLAGS] Can't compute group feature flag: ${flag.key} without group names passed in`);
1619
- }
1620
-
1607
+ this.logMsgIfDebug(() => console.warn(`[FEATURE FLAGS] Can't compute group feature flag: ${flag.key} without group names passed in`));
1621
1608
  return false;
1622
1609
  }
1623
-
1624
1610
  const focusedGroupProperties = groupProperties[groupName];
1625
1611
  return this.matchFeatureFlagProperties(flag, groups[groupName], focusedGroupProperties);
1626
1612
  } else {
1627
1613
  return this.matchFeatureFlagProperties(flag, distinctId, personProperties);
1628
1614
  }
1629
1615
  }
1630
-
1631
1616
  matchFeatureFlagProperties(flag, distinctId, properties) {
1632
1617
  const flagFilters = flag.filters || {};
1633
1618
  const flagConditions = flagFilters.groups || [];
1634
1619
  let isInconclusive = false;
1635
- let result = undefined; // # Stable sort conditions with variant overrides to the top. This ensures that if overrides are present, they are
1620
+ let result = undefined;
1621
+ // # Stable sort conditions with variant overrides to the top. This ensures that if overrides are present, they are
1636
1622
  // # evaluated first, and the variant override is applied to the first matching condition.
1637
-
1638
1623
  const sortedFlagConditions = [...flagConditions].sort((conditionA, conditionB) => {
1639
1624
  const AHasVariantOverride = !!conditionA.variant;
1640
1625
  const BHasVariantOverride = !!conditionB.variant;
1641
-
1642
1626
  if (AHasVariantOverride && BHasVariantOverride) {
1643
1627
  return 0;
1644
1628
  } else if (AHasVariantOverride) {
@@ -1649,19 +1633,16 @@ class FeatureFlagsPoller {
1649
1633
  return 0;
1650
1634
  }
1651
1635
  });
1652
-
1653
1636
  for (const condition of sortedFlagConditions) {
1654
1637
  try {
1655
1638
  if (this.isConditionMatch(flag, distinctId, condition, properties)) {
1656
1639
  const variantOverride = condition.variant;
1657
1640
  const flagVariants = flagFilters.multivariate?.variants || [];
1658
-
1659
1641
  if (variantOverride && flagVariants.some(variant => variant.key === variantOverride)) {
1660
1642
  result = variantOverride;
1661
1643
  } else {
1662
1644
  result = this.getMatchingVariant(flag, distinctId) || true;
1663
1645
  }
1664
-
1665
1646
  break;
1666
1647
  }
1667
1648
  } catch (e) {
@@ -1672,68 +1653,51 @@ class FeatureFlagsPoller {
1672
1653
  }
1673
1654
  }
1674
1655
  }
1675
-
1676
1656
  if (result !== undefined) {
1677
1657
  return result;
1678
1658
  } else if (isInconclusive) {
1679
1659
  throw new InconclusiveMatchError("Can't determine if feature flag is enabled or not with given properties");
1680
- } // We can only return False when all conditions are False
1681
-
1682
-
1660
+ }
1661
+ // We can only return False when all conditions are False
1683
1662
  return false;
1684
1663
  }
1685
-
1686
1664
  isConditionMatch(flag, distinctId, condition, properties) {
1687
1665
  const rolloutPercentage = condition.rollout_percentage;
1688
-
1689
1666
  const warnFunction = msg => {
1690
- if (this.debugMode) {
1691
- console.warn(msg);
1692
- }
1667
+ this.logMsgIfDebug(() => console.warn(msg));
1693
1668
  };
1694
-
1695
1669
  if ((condition.properties || []).length > 0) {
1696
1670
  for (const prop of condition.properties) {
1697
1671
  const propertyType = prop.type;
1698
1672
  let matches = false;
1699
-
1700
1673
  if (propertyType === 'cohort') {
1701
1674
  matches = matchCohort(prop, properties, this.cohorts, this.debugMode);
1702
1675
  } else {
1703
1676
  matches = matchProperty(prop, properties, warnFunction);
1704
1677
  }
1705
-
1706
1678
  if (!matches) {
1707
1679
  return false;
1708
1680
  }
1709
1681
  }
1710
-
1711
1682
  if (rolloutPercentage == undefined) {
1712
1683
  return true;
1713
1684
  }
1714
1685
  }
1715
-
1716
1686
  if (rolloutPercentage != undefined && _hash(flag.key, distinctId) > rolloutPercentage / 100.0) {
1717
1687
  return false;
1718
1688
  }
1719
-
1720
1689
  return true;
1721
1690
  }
1722
-
1723
1691
  getMatchingVariant(flag, distinctId) {
1724
1692
  const hashValue = _hash(flag.key, distinctId, 'variant');
1725
-
1726
1693
  const matchingVariant = this.variantLookupTable(flag).find(variant => {
1727
1694
  return hashValue >= variant.valueMin && hashValue < variant.valueMax;
1728
1695
  });
1729
-
1730
1696
  if (matchingVariant) {
1731
1697
  return matchingVariant.key;
1732
1698
  }
1733
-
1734
1699
  return undefined;
1735
1700
  }
1736
-
1737
1701
  variantLookupTable(flag) {
1738
1702
  const lookupTable = [];
1739
1703
  let valueMin = 0;
@@ -1751,40 +1715,31 @@ class FeatureFlagsPoller {
1751
1715
  });
1752
1716
  return lookupTable;
1753
1717
  }
1754
-
1755
1718
  async loadFeatureFlags(forceReload = false) {
1756
1719
  if (!this.loadedSuccessfullyOnce || forceReload) {
1757
1720
  await this._loadFeatureFlags();
1758
1721
  }
1759
1722
  }
1760
-
1761
1723
  async _loadFeatureFlags() {
1762
1724
  if (this.poller) {
1763
1725
  clearTimeout(this.poller);
1764
1726
  this.poller = undefined;
1765
1727
  }
1766
-
1767
1728
  this.poller = setTimeout(() => this._loadFeatureFlags(), this.pollingInterval);
1768
-
1769
1729
  try {
1770
1730
  const res = await this._requestFeatureFlagDefinitions();
1771
-
1772
1731
  if (res && res.status === 401) {
1773
1732
  throw new ClientError(`Your personalApiKey is invalid. Are you sure you're not using your Project API key? More information: https://posthog.com/docs/api/overview`);
1774
1733
  }
1775
-
1776
1734
  if (res && res.status !== 200) {
1777
1735
  // something else went wrong, or the server is down.
1778
1736
  // In this case, don't override existing flags
1779
1737
  return;
1780
1738
  }
1781
-
1782
1739
  const responseJson = await res.json();
1783
-
1784
1740
  if (!('flags' in responseJson)) {
1785
1741
  this.onError?.(new Error(`Invalid response when getting feature flags: ${JSON.stringify(responseJson)}`));
1786
1742
  }
1787
-
1788
1743
  this.featureFlags = responseJson.flags || [];
1789
1744
  this.featureFlagsByKey = this.featureFlags.reduce((acc, curr) => (acc[curr.key] = curr, acc), {});
1790
1745
  this.groupTypeMapping = responseJson.group_type_mapping || {};
@@ -1798,18 +1753,17 @@ class FeatureFlagsPoller {
1798
1753
  }
1799
1754
  }
1800
1755
  }
1801
-
1802
1756
  async _requestFeatureFlagDefinitions() {
1803
1757
  const url = `${this.host}/api/feature_flag/local_evaluation?token=${this.projectApiKey}&send_cohorts`;
1804
1758
  const options = {
1805
1759
  method: 'GET',
1806
- headers: { ...this.customHeaders,
1760
+ headers: {
1761
+ ...this.customHeaders,
1807
1762
  'Content-Type': 'application/json',
1808
1763
  Authorization: `Bearer ${this.personalApiKey}`
1809
1764
  }
1810
1765
  };
1811
1766
  let abortTimeout = null;
1812
-
1813
1767
  if (this.timeout && typeof this.timeout === 'number') {
1814
1768
  const controller = new AbortController();
1815
1769
  abortTimeout = safeSetTimeout(() => {
@@ -1817,62 +1771,50 @@ class FeatureFlagsPoller {
1817
1771
  }, this.timeout);
1818
1772
  options.signal = controller.signal;
1819
1773
  }
1820
-
1821
1774
  try {
1822
1775
  return await this.fetch(url, options);
1823
1776
  } finally {
1824
1777
  clearTimeout(abortTimeout);
1825
1778
  }
1826
1779
  }
1827
-
1828
1780
  stopPoller() {
1829
1781
  clearTimeout(this.poller);
1830
1782
  }
1831
-
1832
- } // # This function takes a distinct_id and a feature flag key and returns a float between 0 and 1.
1783
+ }
1784
+ // # This function takes a distinct_id and a feature flag key and returns a float between 0 and 1.
1833
1785
  // # Given the same distinct_id and key, it'll always return the same float. These floats are
1834
1786
  // # uniformly distributed between 0 and 1, so if we want to show this feature to 20% of traffic
1835
1787
  // # we can do _hash(key, distinct_id) < 0.2
1836
-
1837
-
1838
1788
  function _hash(key, distinctId, salt = '') {
1839
1789
  // rusha is a fast sha1 implementation in pure javascript
1840
1790
  const sha1Hash = rusha.createHash();
1841
1791
  sha1Hash.update(`${key}.${distinctId}${salt}`);
1842
1792
  return parseInt(sha1Hash.digest('hex').slice(0, 15), 16) / LONG_SCALE;
1843
1793
  }
1844
-
1845
1794
  function matchProperty(property, propertyValues, warnFunction) {
1846
1795
  const key = property.key;
1847
1796
  const value = property.value;
1848
1797
  const operator = property.operator || 'exact';
1849
-
1850
1798
  if (!(key in propertyValues)) {
1851
1799
  throw new InconclusiveMatchError(`Property ${key} not found in propertyValues`);
1852
1800
  } else if (operator === 'is_not_set') {
1853
1801
  throw new InconclusiveMatchError(`Operator is_not_set is not supported`);
1854
1802
  }
1855
-
1856
1803
  const overrideValue = propertyValues[key];
1857
-
1858
1804
  if (overrideValue == null && !NULL_VALUES_ALLOWED_OPERATORS.includes(operator)) {
1859
1805
  // if the value is null, just fail the feature flag comparison
1860
1806
  // this isn't an InconclusiveMatchError because the property value was provided.
1861
1807
  if (warnFunction) {
1862
1808
  warnFunction(`Property ${key} cannot have a value of null/undefined with the ${operator} operator`);
1863
1809
  }
1864
-
1865
1810
  return false;
1866
1811
  }
1867
-
1868
1812
  function computeExactMatch(value, overrideValue) {
1869
1813
  if (Array.isArray(value)) {
1870
1814
  return value.map(val => String(val).toLowerCase()).includes(String(overrideValue).toLowerCase());
1871
1815
  }
1872
-
1873
1816
  return String(value).toLowerCase() === String(overrideValue).toLowerCase();
1874
1817
  }
1875
-
1876
1818
  function compare(lhs, rhs, operator) {
1877
1819
  if (operator === 'gt') {
1878
1820
  return lhs > rhs;
@@ -1886,29 +1828,21 @@ function matchProperty(property, propertyValues, warnFunction) {
1886
1828
  throw new Error(`Invalid operator: ${operator}`);
1887
1829
  }
1888
1830
  }
1889
-
1890
1831
  switch (operator) {
1891
1832
  case 'exact':
1892
1833
  return computeExactMatch(value, overrideValue);
1893
-
1894
1834
  case 'is_not':
1895
1835
  return !computeExactMatch(value, overrideValue);
1896
-
1897
1836
  case 'is_set':
1898
1837
  return key in propertyValues;
1899
-
1900
1838
  case 'icontains':
1901
1839
  return String(overrideValue).toLowerCase().includes(String(value).toLowerCase());
1902
-
1903
1840
  case 'not_icontains':
1904
1841
  return !String(overrideValue).toLowerCase().includes(String(value).toLowerCase());
1905
-
1906
1842
  case 'regex':
1907
1843
  return isValidRegex(String(value)) && String(overrideValue).match(String(value)) !== null;
1908
-
1909
1844
  case 'not_regex':
1910
1845
  return isValidRegex(String(value)) && String(overrideValue).match(String(value)) === null;
1911
-
1912
1846
  case 'gt':
1913
1847
  case 'gte':
1914
1848
  case 'lt':
@@ -1917,14 +1851,13 @@ function matchProperty(property, propertyValues, warnFunction) {
1917
1851
  // :TRICKY: We adjust comparison based on the override value passed in,
1918
1852
  // to make sure we handle both numeric and string comparisons appropriately.
1919
1853
  let parsedValue = typeof value === 'number' ? value : null;
1920
-
1921
1854
  if (typeof value === 'string') {
1922
1855
  try {
1923
1856
  parsedValue = parseFloat(value);
1924
- } catch (err) {// pass
1857
+ } catch (err) {
1858
+ // pass
1925
1859
  }
1926
1860
  }
1927
-
1928
1861
  if (parsedValue != null && overrideValue != null) {
1929
1862
  // check both null and undefined
1930
1863
  if (typeof overrideValue === 'string') {
@@ -1936,66 +1869,50 @@ function matchProperty(property, propertyValues, warnFunction) {
1936
1869
  return compare(String(overrideValue), String(value), operator);
1937
1870
  }
1938
1871
  }
1939
-
1940
1872
  case 'is_date_after':
1941
1873
  case 'is_date_before':
1942
1874
  {
1943
1875
  let parsedDate = relativeDateParseForFeatureFlagMatching(String(value));
1944
-
1945
1876
  if (parsedDate == null) {
1946
1877
  parsedDate = convertToDateTime(value);
1947
1878
  }
1948
-
1949
1879
  if (parsedDate == null) {
1950
1880
  throw new InconclusiveMatchError(`Invalid date: ${value}`);
1951
1881
  }
1952
-
1953
1882
  const overrideDate = convertToDateTime(overrideValue);
1954
-
1955
1883
  if (['is_date_before'].includes(operator)) {
1956
1884
  return overrideDate < parsedDate;
1957
1885
  }
1958
-
1959
1886
  return overrideDate > parsedDate;
1960
1887
  }
1961
-
1962
1888
  default:
1963
1889
  throw new InconclusiveMatchError(`Unknown operator: ${operator}`);
1964
1890
  }
1965
1891
  }
1966
-
1967
1892
  function matchCohort(property, propertyValues, cohortProperties, debugMode = false) {
1968
1893
  const cohortId = String(property.value);
1969
-
1970
1894
  if (!(cohortId in cohortProperties)) {
1971
1895
  throw new InconclusiveMatchError("can't match cohort without a given cohort property value");
1972
1896
  }
1973
-
1974
1897
  const propertyGroup = cohortProperties[cohortId];
1975
1898
  return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode);
1976
1899
  }
1977
-
1978
1900
  function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode = false) {
1979
1901
  if (!propertyGroup) {
1980
1902
  return true;
1981
1903
  }
1982
-
1983
1904
  const propertyGroupType = propertyGroup.type;
1984
1905
  const properties = propertyGroup.values;
1985
-
1986
1906
  if (!properties || properties.length === 0) {
1987
1907
  // empty groups are no-ops, always match
1988
1908
  return true;
1989
1909
  }
1990
-
1991
1910
  let errorMatchingLocally = false;
1992
-
1993
1911
  if ('values' in properties[0]) {
1994
1912
  // a nested property group
1995
1913
  for (const prop of properties) {
1996
1914
  try {
1997
1915
  const matches = matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode);
1998
-
1999
1916
  if (propertyGroupType === 'AND') {
2000
1917
  if (!matches) {
2001
1918
  return false;
@@ -2011,39 +1928,32 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
2011
1928
  if (debugMode) {
2012
1929
  console.debug(`Failed to compute property ${prop} locally: ${err}`);
2013
1930
  }
2014
-
2015
1931
  errorMatchingLocally = true;
2016
1932
  } else {
2017
1933
  throw err;
2018
1934
  }
2019
1935
  }
2020
1936
  }
2021
-
2022
1937
  if (errorMatchingLocally) {
2023
1938
  throw new InconclusiveMatchError("Can't match cohort without a given cohort property value");
2024
- } // if we get here, all matched in AND case, or none matched in OR case
2025
-
2026
-
1939
+ }
1940
+ // if we get here, all matched in AND case, or none matched in OR case
2027
1941
  return propertyGroupType === 'AND';
2028
1942
  } else {
2029
1943
  for (const prop of properties) {
2030
1944
  try {
2031
1945
  let matches;
2032
-
2033
1946
  if (prop.type === 'cohort') {
2034
1947
  matches = matchCohort(prop, propertyValues, cohortProperties, debugMode);
2035
1948
  } else {
2036
1949
  matches = matchProperty(prop, propertyValues);
2037
1950
  }
2038
-
2039
1951
  const negation = prop.negation || false;
2040
-
2041
1952
  if (propertyGroupType === 'AND') {
2042
1953
  // if negated property, do the inverse
2043
1954
  if (!matches && !negation) {
2044
1955
  return false;
2045
1956
  }
2046
-
2047
1957
  if (matches && negation) {
2048
1958
  return false;
2049
1959
  }
@@ -2052,7 +1962,6 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
2052
1962
  if (matches && !negation) {
2053
1963
  return true;
2054
1964
  }
2055
-
2056
1965
  if (!matches && negation) {
2057
1966
  return true;
2058
1967
  }
@@ -2062,23 +1971,19 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
2062
1971
  if (debugMode) {
2063
1972
  console.debug(`Failed to compute property ${prop} locally: ${err}`);
2064
1973
  }
2065
-
2066
1974
  errorMatchingLocally = true;
2067
1975
  } else {
2068
1976
  throw err;
2069
1977
  }
2070
1978
  }
2071
1979
  }
2072
-
2073
1980
  if (errorMatchingLocally) {
2074
1981
  throw new InconclusiveMatchError("can't match cohort without a given cohort property value");
2075
- } // if we get here, all matched in AND case, or none matched in OR case
2076
-
2077
-
1982
+ }
1983
+ // if we get here, all matched in AND case, or none matched in OR case
2078
1984
  return propertyGroupType === 'AND';
2079
1985
  }
2080
1986
  }
2081
-
2082
1987
  function isValidRegex(regex) {
2083
1988
  try {
2084
1989
  new RegExp(regex);
@@ -2087,42 +1992,33 @@ function isValidRegex(regex) {
2087
1992
  return false;
2088
1993
  }
2089
1994
  }
2090
-
2091
1995
  function convertToDateTime(value) {
2092
1996
  if (value instanceof Date) {
2093
1997
  return value;
2094
1998
  } else if (typeof value === 'string' || typeof value === 'number') {
2095
1999
  const date = new Date(value);
2096
-
2097
2000
  if (!isNaN(date.valueOf())) {
2098
2001
  return date;
2099
2002
  }
2100
-
2101
2003
  throw new InconclusiveMatchError(`${value} is in an invalid date format`);
2102
2004
  } else {
2103
2005
  throw new InconclusiveMatchError(`The date provided ${value} must be a string, number, or date object`);
2104
2006
  }
2105
2007
  }
2106
-
2107
2008
  function relativeDateParseForFeatureFlagMatching(value) {
2108
2009
  const regex = /^-?(?<number>[0-9]+)(?<interval>[a-z])$/;
2109
2010
  const match = value.match(regex);
2110
2011
  const parsedDt = new Date(new Date().toISOString());
2111
-
2112
2012
  if (match) {
2113
2013
  if (!match.groups) {
2114
2014
  return null;
2115
2015
  }
2116
-
2117
2016
  const number = parseInt(match.groups['number']);
2118
-
2119
2017
  if (number >= 10000) {
2120
2018
  // Guard against overflow, disallow numbers greater than 10_000
2121
2019
  return null;
2122
2020
  }
2123
-
2124
2021
  const interval = match.groups['interval'];
2125
-
2126
2022
  if (interval == 'h') {
2127
2023
  parsedDt.setUTCHours(parsedDt.getUTCHours() - number);
2128
2024
  } else if (interval == 'd') {
@@ -2136,7 +2032,6 @@ function relativeDateParseForFeatureFlagMatching(value) {
2136
2032
  } else {
2137
2033
  return null;
2138
2034
  }
2139
-
2140
2035
  return parsedDt;
2141
2036
  } else {
2142
2037
  return null;
@@ -2144,15 +2039,13 @@ function relativeDateParseForFeatureFlagMatching(value) {
2144
2039
  }
2145
2040
 
2146
2041
  const THIRTY_SECONDS = 30 * 1000;
2147
- const MAX_CACHE_SIZE = 50 * 1000; // The actual exported Nodejs API.
2148
-
2042
+ const MAX_CACHE_SIZE = 50 * 1000;
2043
+ // The actual exported Nodejs API.
2149
2044
  class PostHog extends PostHogCoreStateless {
2150
2045
  constructor(apiKey, options = {}) {
2151
- options.captureMode = options?.captureMode || 'json';
2152
2046
  super(apiKey, options);
2153
2047
  this._memoryStorage = new PostHogMemoryStorage();
2154
2048
  this.options = options;
2155
-
2156
2049
  if (options.personalApiKey) {
2157
2050
  this.featureFlagsPoller = new FeatureFlagsPoller({
2158
2051
  pollingInterval: typeof options.featureFlagsPollingInterval === 'number' ? options.featureFlagsPollingInterval : THIRTY_SECONDS,
@@ -2167,48 +2060,37 @@ class PostHog extends PostHogCoreStateless {
2167
2060
  customHeaders: this.getCustomHeaders()
2168
2061
  });
2169
2062
  }
2170
-
2171
2063
  this.distinctIdHasSentFlagCalls = {};
2172
2064
  this.maxCacheSize = options.maxCacheSize || MAX_CACHE_SIZE;
2173
2065
  }
2174
-
2175
2066
  getPersistedProperty(key) {
2176
2067
  return this._memoryStorage.getProperty(key);
2177
2068
  }
2178
-
2179
2069
  setPersistedProperty(key, value) {
2180
2070
  return this._memoryStorage.setProperty(key, value);
2181
2071
  }
2182
-
2183
2072
  fetch(url, options) {
2184
2073
  return this.options.fetch ? this.options.fetch(url, options) : fetch$1(url, options);
2185
2074
  }
2186
-
2187
2075
  getLibraryId() {
2188
2076
  return 'posthog-node';
2189
2077
  }
2190
-
2191
2078
  getLibraryVersion() {
2192
2079
  return version;
2193
2080
  }
2194
-
2195
2081
  getCustomUserAgent() {
2196
2082
  return `${this.getLibraryId()}/${this.getLibraryVersion()}`;
2197
2083
  }
2198
-
2199
2084
  enable() {
2200
2085
  return super.optIn();
2201
2086
  }
2202
-
2203
2087
  disable() {
2204
2088
  return super.optOut();
2205
2089
  }
2206
-
2207
2090
  debug(enabled = true) {
2208
2091
  super.debug(enabled);
2209
2092
  this.featureFlagsPoller?.debug(enabled);
2210
2093
  }
2211
-
2212
2094
  capture({
2213
2095
  distinctId,
2214
2096
  event,
@@ -2226,65 +2108,55 @@ class PostHog extends PostHogCoreStateless {
2226
2108
  uuid
2227
2109
  });
2228
2110
  };
2229
-
2230
2111
  const _getFlags = (distinctId, groups, disableGeoip) => {
2231
2112
  return super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip);
2232
- }; // :TRICKY: If we flush, or need to shut down, to not lose events we want this promise to resolve before we flush
2233
-
2234
-
2113
+ };
2114
+ // :TRICKY: If we flush, or need to shut down, to not lose events we want this promise to resolve before we flush
2235
2115
  const capturePromise = Promise.resolve().then(async () => {
2236
2116
  if (sendFeatureFlags) {
2237
2117
  // If we are sending feature flags, we need to make sure we have the latest flags
2238
2118
  // return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
2239
2119
  return await _getFlags(distinctId, groups, disableGeoip);
2240
2120
  }
2241
-
2242
2121
  if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
2243
2122
  // Otherwise we may as well check for the flags locally and include them if there
2244
2123
  const groupsWithStringValues = {};
2245
-
2246
2124
  for (const [key, value] of Object.entries(groups || {})) {
2247
2125
  groupsWithStringValues[key] = String(value);
2248
2126
  }
2249
-
2250
2127
  return await this.getAllFlags(distinctId, {
2251
2128
  groups: groupsWithStringValues,
2252
2129
  disableGeoip,
2253
2130
  onlyEvaluateLocally: true
2254
2131
  });
2255
2132
  }
2256
-
2257
2133
  return {};
2258
2134
  }).then(flags => {
2259
2135
  // Derive the relevant flag properties to add
2260
2136
  const additionalProperties = {};
2261
-
2262
2137
  if (flags) {
2263
2138
  for (const [feature, variant] of Object.entries(flags)) {
2264
2139
  additionalProperties[`$feature/${feature}`] = variant;
2265
2140
  }
2266
2141
  }
2267
-
2268
2142
  const activeFlags = Object.keys(flags || {}).filter(flag => flags?.[flag] !== false);
2269
-
2270
2143
  if (activeFlags.length > 0) {
2271
2144
  additionalProperties['$active_feature_flags'] = activeFlags;
2272
2145
  }
2273
-
2274
2146
  return additionalProperties;
2275
2147
  }).catch(() => {
2276
2148
  // Something went wrong getting the flag info - we should capture the event anyways
2277
2149
  return {};
2278
2150
  }).then(additionalProperties => {
2279
2151
  // No matter what - capture the event
2280
- _capture({ ...additionalProperties,
2152
+ _capture({
2153
+ ...additionalProperties,
2281
2154
  ...properties,
2282
2155
  $groups: groups
2283
2156
  });
2284
2157
  });
2285
2158
  this.addPendingPromise(capturePromise);
2286
2159
  }
2287
-
2288
2160
  identify({
2289
2161
  distinctId,
2290
2162
  properties,
@@ -2298,13 +2170,11 @@ class PostHog extends PostHogCoreStateless {
2298
2170
  disableGeoip
2299
2171
  });
2300
2172
  }
2301
-
2302
2173
  alias(data) {
2303
2174
  super.aliasStateless(data.alias, data.distinctId, undefined, {
2304
2175
  disableGeoip: data.disableGeoip
2305
2176
  });
2306
2177
  }
2307
-
2308
2178
  async getFeatureFlag(key, distinctId, options) {
2309
2179
  const {
2310
2180
  groups,
@@ -2318,36 +2188,29 @@ class PostHog extends PostHogCoreStateless {
2318
2188
  } = options || {};
2319
2189
  const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
2320
2190
  personProperties = adjustedProperties.allPersonProperties;
2321
- groupProperties = adjustedProperties.allGroupProperties; // set defaults
2322
-
2191
+ groupProperties = adjustedProperties.allGroupProperties;
2192
+ // set defaults
2323
2193
  if (onlyEvaluateLocally == undefined) {
2324
2194
  onlyEvaluateLocally = false;
2325
2195
  }
2326
-
2327
2196
  if (sendFeatureFlagEvents == undefined) {
2328
2197
  sendFeatureFlagEvents = true;
2329
2198
  }
2330
-
2331
2199
  let response = await this.featureFlagsPoller?.getFeatureFlag(key, distinctId, groups, personProperties, groupProperties);
2332
2200
  const flagWasLocallyEvaluated = response !== undefined;
2333
-
2334
2201
  if (!flagWasLocallyEvaluated && !onlyEvaluateLocally) {
2335
2202
  response = await super.getFeatureFlagStateless(key, distinctId, groups, personProperties, groupProperties, disableGeoip);
2336
2203
  }
2337
-
2338
2204
  const featureFlagReportedKey = `${key}_${response}`;
2339
-
2340
2205
  if (sendFeatureFlagEvents && (!(distinctId in this.distinctIdHasSentFlagCalls) || !this.distinctIdHasSentFlagCalls[distinctId].includes(featureFlagReportedKey))) {
2341
2206
  if (Object.keys(this.distinctIdHasSentFlagCalls).length >= this.maxCacheSize) {
2342
2207
  this.distinctIdHasSentFlagCalls = {};
2343
2208
  }
2344
-
2345
2209
  if (Array.isArray(this.distinctIdHasSentFlagCalls[distinctId])) {
2346
2210
  this.distinctIdHasSentFlagCalls[distinctId].push(featureFlagReportedKey);
2347
2211
  } else {
2348
2212
  this.distinctIdHasSentFlagCalls[distinctId] = [featureFlagReportedKey];
2349
2213
  }
2350
-
2351
2214
  this.capture({
2352
2215
  distinctId,
2353
2216
  event: '$feature_flag_called',
@@ -2361,10 +2224,8 @@ class PostHog extends PostHogCoreStateless {
2361
2224
  disableGeoip
2362
2225
  });
2363
2226
  }
2364
-
2365
2227
  return response;
2366
2228
  }
2367
-
2368
2229
  async getFeatureFlagPayload(key, distinctId, matchValue, options) {
2369
2230
  const {
2370
2231
  groups,
@@ -2379,56 +2240,45 @@ class PostHog extends PostHogCoreStateless {
2379
2240
  const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
2380
2241
  personProperties = adjustedProperties.allPersonProperties;
2381
2242
  groupProperties = adjustedProperties.allGroupProperties;
2382
- let response = undefined; // Try to get match value locally if not provided
2383
-
2243
+ let response = undefined;
2244
+ // Try to get match value locally if not provided
2384
2245
  if (!matchValue) {
2385
- matchValue = await this.getFeatureFlag(key, distinctId, { ...options,
2246
+ matchValue = await this.getFeatureFlag(key, distinctId, {
2247
+ ...options,
2386
2248
  onlyEvaluateLocally: true
2387
2249
  });
2388
2250
  }
2389
-
2390
2251
  if (matchValue) {
2391
2252
  response = await this.featureFlagsPoller?.computeFeatureFlagPayloadLocally(key, matchValue);
2392
- } // set defaults
2393
-
2394
-
2253
+ }
2254
+ // set defaults
2395
2255
  if (onlyEvaluateLocally == undefined) {
2396
2256
  onlyEvaluateLocally = false;
2397
2257
  }
2398
-
2399
2258
  if (sendFeatureFlagEvents == undefined) {
2400
2259
  sendFeatureFlagEvents = true;
2401
- } // set defaults
2402
-
2403
-
2260
+ }
2261
+ // set defaults
2404
2262
  if (onlyEvaluateLocally == undefined) {
2405
2263
  onlyEvaluateLocally = false;
2406
2264
  }
2407
-
2408
2265
  const payloadWasLocallyEvaluated = response !== undefined;
2409
-
2410
2266
  if (!payloadWasLocallyEvaluated && !onlyEvaluateLocally) {
2411
2267
  response = await super.getFeatureFlagPayloadStateless(key, distinctId, groups, personProperties, groupProperties, disableGeoip);
2412
2268
  }
2413
-
2414
2269
  return response;
2415
2270
  }
2416
-
2417
2271
  async isFeatureEnabled(key, distinctId, options) {
2418
2272
  const feat = await this.getFeatureFlag(key, distinctId, options);
2419
-
2420
2273
  if (feat === undefined) {
2421
2274
  return undefined;
2422
2275
  }
2423
-
2424
2276
  return !!feat || false;
2425
2277
  }
2426
-
2427
2278
  async getAllFlags(distinctId, options) {
2428
2279
  const response = await this.getAllFlagsAndPayloads(distinctId, options);
2429
2280
  return response.featureFlags;
2430
2281
  }
2431
-
2432
2282
  async getAllFlagsAndPayloads(distinctId, options) {
2433
2283
  const {
2434
2284
  groups,
@@ -2441,39 +2291,36 @@ class PostHog extends PostHogCoreStateless {
2441
2291
  } = options || {};
2442
2292
  const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
2443
2293
  personProperties = adjustedProperties.allPersonProperties;
2444
- groupProperties = adjustedProperties.allGroupProperties; // set defaults
2445
-
2294
+ groupProperties = adjustedProperties.allGroupProperties;
2295
+ // set defaults
2446
2296
  if (onlyEvaluateLocally == undefined) {
2447
2297
  onlyEvaluateLocally = false;
2448
2298
  }
2449
-
2450
2299
  const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(distinctId, groups, personProperties, groupProperties);
2451
2300
  let featureFlags = {};
2452
2301
  let featureFlagPayloads = {};
2453
2302
  let fallbackToDecide = true;
2454
-
2455
2303
  if (localEvaluationResult) {
2456
2304
  featureFlags = localEvaluationResult.response;
2457
2305
  featureFlagPayloads = localEvaluationResult.payloads;
2458
2306
  fallbackToDecide = localEvaluationResult.fallbackToDecide;
2459
2307
  }
2460
-
2461
2308
  if (fallbackToDecide && !onlyEvaluateLocally) {
2462
2309
  const remoteEvaluationResult = await super.getFeatureFlagsAndPayloadsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip);
2463
- featureFlags = { ...featureFlags,
2310
+ featureFlags = {
2311
+ ...featureFlags,
2464
2312
  ...(remoteEvaluationResult.flags || {})
2465
2313
  };
2466
- featureFlagPayloads = { ...featureFlagPayloads,
2314
+ featureFlagPayloads = {
2315
+ ...featureFlagPayloads,
2467
2316
  ...(remoteEvaluationResult.payloads || {})
2468
2317
  };
2469
2318
  }
2470
-
2471
2319
  return {
2472
2320
  featureFlags,
2473
2321
  featureFlagPayloads
2474
2322
  };
2475
2323
  }
2476
-
2477
2324
  groupIdentify({
2478
2325
  groupType,
2479
2326
  groupKey,
@@ -2485,23 +2332,19 @@ class PostHog extends PostHogCoreStateless {
2485
2332
  disableGeoip
2486
2333
  }, distinctId);
2487
2334
  }
2488
-
2489
2335
  async reloadFeatureFlags() {
2490
2336
  await this.featureFlagsPoller?.loadFeatureFlags(true);
2491
2337
  }
2492
-
2493
2338
  async shutdown(shutdownTimeoutMs) {
2494
2339
  this.featureFlagsPoller?.stopPoller();
2495
2340
  return super.shutdown(shutdownTimeoutMs);
2496
2341
  }
2497
-
2498
2342
  addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties) {
2499
2343
  const allPersonProperties = {
2500
2344
  distinct_id: distinctId,
2501
2345
  ...(personProperties || {})
2502
2346
  };
2503
2347
  const allGroupProperties = {};
2504
-
2505
2348
  if (groups) {
2506
2349
  for (const groupName of Object.keys(groups)) {
2507
2350
  allGroupProperties[groupName] = {
@@ -2510,13 +2353,11 @@ class PostHog extends PostHogCoreStateless {
2510
2353
  };
2511
2354
  }
2512
2355
  }
2513
-
2514
2356
  return {
2515
2357
  allPersonProperties,
2516
2358
  allGroupProperties
2517
2359
  };
2518
2360
  }
2519
-
2520
2361
  }
2521
2362
 
2522
2363
  /**
@@ -2547,26 +2388,21 @@ class PostHogSentryIntegration {
2547
2388
  this.name = 'posthog-node';
2548
2389
  this.posthogHost = posthog.options.host ?? 'https://us.i.posthog.com';
2549
2390
  }
2550
-
2551
2391
  setupOnce(addGlobalEventProcessor, getCurrentHub) {
2552
2392
  addGlobalEventProcessor(event => {
2553
2393
  if (event.exception?.values === undefined || event.exception.values.length === 0) {
2554
2394
  return event;
2555
2395
  }
2556
-
2557
2396
  if (!event.tags) {
2558
2397
  event.tags = {};
2559
2398
  }
2560
-
2561
- const sentry = getCurrentHub(); // Get the PostHog user ID from a specific tag, which users can set on their Sentry scope as they need.
2562
-
2399
+ const sentry = getCurrentHub();
2400
+ // Get the PostHog user ID from a specific tag, which users can set on their Sentry scope as they need.
2563
2401
  const userId = event.tags[PostHogSentryIntegration.POSTHOG_ID_TAG];
2564
-
2565
2402
  if (userId === undefined) {
2566
2403
  // If we can't find a user ID, don't bother linking the event. We won't be able to send anything meaningful to PostHog without it.
2567
2404
  return event;
2568
2405
  }
2569
-
2570
2406
  event.tags['PostHog Person URL'] = new URL(`/person/${userId}`, this.posthogHost).toString();
2571
2407
  const properties = {
2572
2408
  // PostHog Exception Properties
@@ -2581,11 +2417,9 @@ class PostHogSentryIntegration {
2581
2417
  $sentry_tags: event.tags
2582
2418
  };
2583
2419
  const projectId = sentry.getClient()?.getDsn()?.projectId;
2584
-
2585
2420
  if (this.organization !== undefined && projectId !== undefined && event.event_id !== undefined) {
2586
2421
  properties.$sentry_url = `${this.prefix ?? 'https://sentry.io/organizations'}/${this.organization}/issues/?project=${projectId}&query=${event.event_id}`;
2587
2422
  }
2588
-
2589
2423
  this.posthog.capture({
2590
2424
  event: '$exception',
2591
2425
  distinctId: userId,
@@ -2594,7 +2428,6 @@ class PostHogSentryIntegration {
2594
2428
  return event;
2595
2429
  });
2596
2430
  }
2597
-
2598
2431
  }
2599
2432
  PostHogSentryIntegration.POSTHOG_ID_TAG = 'posthog_distinct_id';
2600
2433