posthog-node 2.0.2 → 2.2.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
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  declare type PosthogCoreOptions = {
2
3
  host?: string;
3
4
  flushAt?: number;
@@ -5,10 +6,30 @@ declare type PosthogCoreOptions = {
5
6
  enable?: boolean;
6
7
  sendFeatureFlagEvent?: boolean;
7
8
  preloadFeatureFlags?: boolean;
9
+ bootstrap?: {
10
+ distinctId?: string;
11
+ isIdentifiedId?: boolean;
12
+ featureFlags?: Record<string, boolean | string>;
13
+ };
8
14
  fetchRetryCount?: number;
9
15
  fetchRetryDelay?: number;
10
16
  sessionExpirationTimeSeconds?: number;
11
17
  captureMode?: 'json' | 'form';
18
+ };
19
+ declare type PostHogFetchOptions = {
20
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH';
21
+ mode?: 'no-cors';
22
+ credentials?: 'omit';
23
+ headers: {
24
+ [key: string]: string;
25
+ };
26
+ body?: string;
27
+ signal?: AbortSignal;
28
+ };
29
+ declare type PostHogFetchResponse = {
30
+ status: number;
31
+ text: () => Promise<string>;
32
+ json: () => Promise<any>;
12
33
  };
13
34
 
14
35
  interface IdentifyMessageV1 {
@@ -134,6 +155,7 @@ declare type PostHogOptions = PosthogCoreOptions & {
134
155
  featureFlagsPollingInterval?: number;
135
156
  requestTimeout?: number;
136
157
  maxCacheSize?: number;
158
+ fetch?: (url: string, options: PostHogFetchOptions) => Promise<PostHogFetchResponse>;
137
159
  };
138
160
  declare class PostHog implements PostHogNodeV1 {
139
161
  private _sharedClient;
package/lib/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import undici, { request } from 'undici';
2
1
  import { createHash } from 'crypto';
2
+ import axios from 'axios';
3
3
 
4
4
  /******************************************************************************
5
5
  Copyright (c) Microsoft Corporation.
@@ -43,6 +43,18 @@ var __assign = function () {
43
43
  };
44
44
  return __assign.apply(this, arguments);
45
45
  };
46
+ function __rest(s, e) {
47
+ var t = {};
48
+ for (var p in s)
49
+ if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
50
+ t[p] = s[p];
51
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
52
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
53
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
54
+ t[p[i]] = s[p[i]];
55
+ }
56
+ return t;
57
+ }
46
58
  function __awaiter(thisArg, _arguments, P, generator) {
47
59
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
48
60
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -143,7 +155,7 @@ function __spreadArray(to, from, pack) {
143
155
  return to.concat(ar || Array.prototype.slice.call(from));
144
156
  }
145
157
 
146
- var version = "2.0.2";
158
+ var version = "2.2.0";
147
159
 
148
160
  var PostHogPersistedProperty;
149
161
  (function (PostHogPersistedProperty) {
@@ -740,6 +752,26 @@ var PostHogCore = /** @class */ (function () {
740
752
  }
741
753
  return __assign({ $lib: this.getLibraryId(), $lib_version: this.getLibraryVersion(), $active_feature_flags: featureFlags ? Object.keys(featureFlags) : undefined }, featureVariantProperties);
742
754
  };
755
+ PostHogCore.prototype.setupBootstrap = function (options) {
756
+ var _a, _b, _c, _d;
757
+ if ((_a = options === null || options === void 0 ? void 0 : options.bootstrap) === null || _a === void 0 ? void 0 : _a.distinctId) {
758
+ if ((_b = options === null || options === void 0 ? void 0 : options.bootstrap) === null || _b === void 0 ? void 0 : _b.isIdentifiedId) {
759
+ this.setPersistedProperty(PostHogPersistedProperty.DistinctId, options.bootstrap.distinctId);
760
+ }
761
+ else {
762
+ this.setPersistedProperty(PostHogPersistedProperty.AnonymousId, options.bootstrap.distinctId);
763
+ }
764
+ }
765
+ if ((_c = options === null || options === void 0 ? void 0 : options.bootstrap) === null || _c === void 0 ? void 0 : _c.featureFlags) {
766
+ var activeFlags = Object.keys(((_d = options.bootstrap) === null || _d === void 0 ? void 0 : _d.featureFlags) || {})
767
+ .filter(function (flag) { var _a, _b; return !!((_b = (_a = options.bootstrap) === null || _a === void 0 ? void 0 : _a.featureFlags) === null || _b === void 0 ? void 0 : _b[flag]); })
768
+ .reduce(function (res, key) {
769
+ var _a, _b;
770
+ return ((res[key] = ((_b = (_a = options.bootstrap) === null || _a === void 0 ? void 0 : _a.featureFlags) === null || _b === void 0 ? void 0 : _b[key]) || false), res);
771
+ }, {});
772
+ this.setKnownFeatureFlags(activeFlags);
773
+ }
774
+ };
743
775
  Object.defineProperty(PostHogCore.prototype, "props", {
744
776
  // NOTE: Props are lazy loaded from localstorage hence the complex getter setter logic
745
777
  get: function () {
@@ -983,8 +1015,7 @@ var PostHogCore = /** @class */ (function () {
983
1015
  .then(function (r) { return r.json(); })
984
1016
  .then(function (res) {
985
1017
  if (res.featureFlags) {
986
- _this.setPersistedProperty(PostHogPersistedProperty.FeatureFlags, res.featureFlags);
987
- _this._events.emit('featureflags', res.featureFlags);
1018
+ _this.setKnownFeatureFlags(res.featureFlags);
988
1019
  }
989
1020
  return res;
990
1021
  })
@@ -995,6 +1026,10 @@ var PostHogCore = /** @class */ (function () {
995
1026
  });
996
1027
  });
997
1028
  };
1029
+ PostHogCore.prototype.setKnownFeatureFlags = function (featureFlags) {
1030
+ this.setPersistedProperty(PostHogPersistedProperty.FeatureFlags, featureFlags);
1031
+ this._events.emit('featureflags', featureFlags);
1032
+ };
998
1033
  PostHogCore.prototype.getFeatureFlag = function (key) {
999
1034
  var featureFlags = this.getFeatureFlags();
1000
1035
  if (!featureFlags) {
@@ -1222,6 +1257,42 @@ var PostHogMemoryStorage = /** @class */ (function () {
1222
1257
  return PostHogMemoryStorage;
1223
1258
  }());
1224
1259
 
1260
+ // So that alternative implementations can be used if desired
1261
+
1262
+ var fetch = function (url, options) {
1263
+ return __awaiter(void 0, void 0, void 0, function () {
1264
+ var res;
1265
+ return __generator(this, function (_a) {
1266
+ switch (_a.label) {
1267
+ case 0:
1268
+ return [4
1269
+ /*yield*/
1270
+ , axios.request({
1271
+ url: url,
1272
+ headers: options.headers,
1273
+ method: options.method.toLowerCase(),
1274
+ data: options.body,
1275
+ signal: options.signal
1276
+ })];
1277
+
1278
+ case 1:
1279
+ res = _a.sent();
1280
+ return [2
1281
+ /*return*/
1282
+ , {
1283
+ status: res.status,
1284
+ text: function () {
1285
+ return res.data;
1286
+ },
1287
+ json: function () {
1288
+ return res.data;
1289
+ }
1290
+ }];
1291
+ }
1292
+ });
1293
+ });
1294
+ };
1295
+
1225
1296
  var LONG_SCALE = 0xfffffffffffffff;
1226
1297
 
1227
1298
  var ClientError =
@@ -1270,7 +1341,9 @@ function () {
1270
1341
  personalApiKey = _a.personalApiKey,
1271
1342
  projectApiKey = _a.projectApiKey,
1272
1343
  timeout = _a.timeout,
1273
- host = _a.host;
1344
+ host = _a.host,
1345
+ options = __rest(_a, ["pollingInterval", "personalApiKey", "projectApiKey", "timeout", "host"]);
1346
+
1274
1347
  this.pollingInterval = pollingInterval;
1275
1348
  this.personalApiKey = personalApiKey;
1276
1349
  this.featureFlags = [];
@@ -1279,7 +1352,9 @@ function () {
1279
1352
  this.timeout = timeout;
1280
1353
  this.projectApiKey = projectApiKey;
1281
1354
  this.host = host;
1282
- this.poller = undefined;
1355
+ this.poller = undefined; // NOTE: as any is required here as the AbortSignal typing is slightly misaligned but works just fine
1356
+
1357
+ this.fetch = options.fetch || fetch;
1283
1358
  void this.loadFeatureFlags();
1284
1359
  }
1285
1360
 
@@ -1445,16 +1520,44 @@ function () {
1445
1520
  };
1446
1521
 
1447
1522
  FeatureFlagsPoller.prototype.matchFeatureFlagProperties = function (flag, distinctId, properties) {
1448
- var _this = this;
1523
+ var _a;
1449
1524
 
1450
1525
  var flagFilters = flag.filters || {};
1451
1526
  var flagConditions = flagFilters.groups || [];
1452
1527
  var isInconclusive = false;
1453
- var result = undefined;
1454
- flagConditions.forEach(function (condition) {
1528
+ var result = undefined; // # Stable sort conditions with variant overrides to the top. This ensures that if overrides are present, they are
1529
+ // # evaluated first, and the variant override is applied to the first matching condition.
1530
+
1531
+ var sortedFlagConditions = __spreadArray([], flagConditions, true).sort(function (conditionA, conditionB) {
1532
+ var AHasVariantOverride = !!conditionA.variant;
1533
+ var BHasVariantOverride = !!conditionB.variant;
1534
+
1535
+ if (AHasVariantOverride && BHasVariantOverride) {
1536
+ return 0;
1537
+ } else if (AHasVariantOverride) {
1538
+ return -1;
1539
+ } else if (BHasVariantOverride) {
1540
+ return 1;
1541
+ } else {
1542
+ return 0;
1543
+ }
1544
+ });
1545
+
1546
+ var _loop_1 = function (condition) {
1455
1547
  try {
1456
- if (_this.isConditionMatch(flag, distinctId, condition, properties)) {
1457
- result = _this.getMatchingVariant(flag, distinctId) || true;
1548
+ if (this_1.isConditionMatch(flag, distinctId, condition, properties)) {
1549
+ var variantOverride_1 = condition.variant;
1550
+ var flagVariants = ((_a = flagFilters.multivariate) === null || _a === void 0 ? void 0 : _a.variants) || [];
1551
+
1552
+ if (variantOverride_1 && flagVariants.some(function (variant) {
1553
+ return variant.key === variantOverride_1;
1554
+ })) {
1555
+ result = variantOverride_1;
1556
+ } else {
1557
+ result = this_1.getMatchingVariant(flag, distinctId) || true;
1558
+ }
1559
+
1560
+ return "break";
1458
1561
  }
1459
1562
  } catch (e) {
1460
1563
  if (e instanceof InconclusiveMatchError) {
@@ -1463,7 +1566,17 @@ function () {
1463
1566
  throw e;
1464
1567
  }
1465
1568
  }
1466
- });
1569
+ };
1570
+
1571
+ var this_1 = this;
1572
+
1573
+ for (var _i = 0, sortedFlagConditions_1 = sortedFlagConditions; _i < sortedFlagConditions_1.length; _i++) {
1574
+ var condition = sortedFlagConditions_1[_i];
1575
+
1576
+ var state_1 = _loop_1(condition);
1577
+
1578
+ if (state_1 === "break") break;
1579
+ }
1467
1580
 
1468
1581
  if (result !== undefined) {
1469
1582
  return result;
@@ -1591,13 +1704,13 @@ function () {
1591
1704
  case 2:
1592
1705
  res = _a.sent();
1593
1706
 
1594
- if (res && res.statusCode === 401) {
1707
+ if (res && res.status === 401) {
1595
1708
  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");
1596
1709
  }
1597
1710
 
1598
1711
  return [4
1599
1712
  /*yield*/
1600
- , res.body.json()];
1713
+ , res.json()];
1601
1714
 
1602
1715
  case 3:
1603
1716
  responseJson = _a.sent();
@@ -1636,48 +1749,57 @@ function () {
1636
1749
 
1637
1750
  FeatureFlagsPoller.prototype._requestFeatureFlagDefinitions = function () {
1638
1751
  return __awaiter(this, void 0, void 0, function () {
1639
- var url, headers, options, res, err_2;
1752
+ var url, options, abortTimeout, controller_1, err_2;
1640
1753
  return __generator(this, function (_a) {
1641
1754
  switch (_a.label) {
1642
1755
  case 0:
1643
1756
  url = "".concat(this.host, "/api/feature_flag/local_evaluation?token=").concat(this.projectApiKey);
1644
- headers = {
1645
- 'Content-Type': 'application/json',
1646
- Authorization: "Bearer ".concat(this.personalApiKey),
1647
- 'user-agent': "posthog-node/".concat(version)
1648
- };
1649
1757
  options = {
1650
1758
  method: 'GET',
1651
- headers: headers
1759
+ headers: {
1760
+ 'Content-Type': 'application/json',
1761
+ Authorization: "Bearer ".concat(this.personalApiKey),
1762
+ 'user-agent': "posthog-node/".concat(version)
1763
+ }
1652
1764
  };
1765
+ abortTimeout = null;
1653
1766
 
1654
1767
  if (this.timeout && typeof this.timeout === 'number') {
1655
- options.bodyTimeout = this.timeout;
1768
+ controller_1 = new AbortController();
1769
+ abortTimeout = safeSetTimeout(function () {
1770
+ controller_1.abort();
1771
+ }, this.timeout);
1772
+ options.signal = controller_1.signal;
1656
1773
  }
1657
1774
 
1658
1775
  _a.label = 1;
1659
1776
 
1660
1777
  case 1:
1661
- _a.trys.push([1, 3,, 4]);
1778
+ _a.trys.push([1, 3, 4, 5]);
1662
1779
 
1663
1780
  return [4
1664
1781
  /*yield*/
1665
- , request(url, options)];
1782
+ , this.fetch(url, options)];
1666
1783
 
1667
1784
  case 2:
1668
- res = _a.sent();
1669
- return [3
1670
- /*break*/
1671
- , 4];
1785
+ return [2
1786
+ /*return*/
1787
+ , _a.sent()];
1672
1788
 
1673
1789
  case 3:
1674
1790
  err_2 = _a.sent();
1675
1791
  throw new Error("Request failed with error: ".concat(err_2));
1676
1792
 
1677
1793
  case 4:
1794
+ clearTimeout(abortTimeout);
1795
+ return [7
1796
+ /*endfinally*/
1797
+ ];
1798
+
1799
+ case 5:
1678
1800
  return [2
1679
1801
  /*return*/
1680
- , res];
1802
+ ];
1681
1803
  }
1682
1804
  });
1683
1805
  });
@@ -1751,6 +1873,17 @@ function matchProperty(property, propertyValues) {
1751
1873
  case 'lte':
1752
1874
  return typeof overrideValue == typeof value && overrideValue <= value;
1753
1875
 
1876
+ case 'is_date_after':
1877
+ case 'is_date_before':
1878
+ var parsedDate = convertToDateTime(value);
1879
+ var overrideDate = convertToDateTime(overrideValue);
1880
+
1881
+ if (operator === 'is_date_before') {
1882
+ return overrideDate < parsedDate;
1883
+ }
1884
+
1885
+ return overrideDate > parsedDate;
1886
+
1754
1887
  default:
1755
1888
  console.error("Unknown operator: ".concat(operator));
1756
1889
  return false;
@@ -1766,6 +1899,22 @@ function isValidRegex(regex) {
1766
1899
  }
1767
1900
  }
1768
1901
 
1902
+ function convertToDateTime(value) {
1903
+ if (value instanceof Date) {
1904
+ return value;
1905
+ } else if (typeof value === 'string' || typeof value === 'number') {
1906
+ var date = new Date(value);
1907
+
1908
+ if (!isNaN(date.valueOf())) {
1909
+ return date;
1910
+ }
1911
+
1912
+ throw new InconclusiveMatchError("".concat(value, " is in an invalid date format"));
1913
+ } else {
1914
+ throw new InconclusiveMatchError("The date provided ".concat(value, " must be a string, number, or date object"));
1915
+ }
1916
+ }
1917
+
1769
1918
  var THIRTY_SECONDS = 30 * 1000;
1770
1919
  var MAX_CACHE_SIZE = 50 * 1000;
1771
1920
 
@@ -1787,6 +1936,7 @@ function (_super) {
1787
1936
  options.sendFeatureFlagEvent = false; // Let `posthog-node` handle this on its own, since we're dealing with multiple distinctIDs
1788
1937
 
1789
1938
  _this = _super.call(this, apiKey, options) || this;
1939
+ _this.options = options;
1790
1940
  _this._memoryStorage = new PostHogMemoryStorage();
1791
1941
  return _this;
1792
1942
  }
@@ -1805,7 +1955,7 @@ function (_super) {
1805
1955
  };
1806
1956
 
1807
1957
  PostHogClient.prototype.fetch = function (url, options) {
1808
- return undici.fetch(url, options);
1958
+ return this.options.fetch ? this.options.fetch(url, options) : fetch(url, options);
1809
1959
  };
1810
1960
 
1811
1961
  PostHogClient.prototype.getLibraryId = function () {
@@ -1840,7 +1990,8 @@ function () {
1840
1990
  personalApiKey: options.personalApiKey,
1841
1991
  projectApiKey: apiKey,
1842
1992
  timeout: options.requestTimeout,
1843
- host: this._sharedClient.host
1993
+ host: this._sharedClient.host,
1994
+ fetch: options.fetch
1844
1995
  });
1845
1996
  }
1846
1997