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/CHANGELOG.md +11 -1
- package/lib/index.cjs.js +183 -32
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +22 -0
- package/lib/index.esm.js +182 -31
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +2 -0
- package/lib/posthog-core/src/types.d.ts +8 -1
- package/lib/posthog-node/src/feature-flags.d.ts +5 -3
- package/lib/posthog-node/src/fetch.d.ts +2 -0
- package/lib/posthog-node/src/posthog-node.d.ts +2 -1
- package/lib/posthog-node/src/types.d.ts +1 -0
- package/package.json +4 -4
- package/src/feature-flags.ts +84 -22
- package/src/fetch.ts +20 -0
- package/src/posthog-node.ts +6 -3
- package/src/types.ts +1 -0
- package/test/feature-flags.spec.ts +442 -115
- package/test/posthog-node.spec.ts +41 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# 2.2.0 - 2022-11-18
|
|
2
|
+
|
|
3
|
+
1. Add support for variant overrides for feature flag local evaluation.
|
|
4
|
+
2. Add support for date operators in feature flag local evaluation.
|
|
5
|
+
|
|
6
|
+
# 2.1.0 - 2022-09-08
|
|
7
|
+
|
|
8
|
+
1. Swaps `unidici` for `axios` in order to support older versions of Node
|
|
9
|
+
2. The `fetch` implementation can be overridden as an option for those who wish to use an alternative implementation
|
|
10
|
+
3. Fixes the minimum Node version to >=14.17.0
|
|
11
|
+
|
|
1
12
|
# 2.0.2 - 2022-08-23
|
|
2
13
|
|
|
3
14
|
1. Removes references to `cli.js`
|
|
@@ -13,7 +24,6 @@ Breaking changes:
|
|
|
13
24
|
4. The `callback` parameter passed as an optional last argument to most of the methods is no longer supported
|
|
14
25
|
5. The CLI is no longer supported
|
|
15
26
|
|
|
16
|
-
|
|
17
27
|
What's new:
|
|
18
28
|
|
|
19
29
|
1. You can now evaluate feature flags locally (i.e. without sending a request to your PostHog servers) by setting a personal API key, and passing in groups and person properties to `isFeatureEnabled` and `getFeatureFlag` calls.
|
package/lib/index.cjs.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var undici = require('undici');
|
|
6
5
|
var crypto = require('crypto');
|
|
6
|
+
var axios = require('axios');
|
|
7
7
|
|
|
8
8
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
11
11
|
|
|
12
12
|
/******************************************************************************
|
|
13
13
|
Copyright (c) Microsoft Corporation.
|
|
@@ -51,6 +51,18 @@ var __assign = function () {
|
|
|
51
51
|
};
|
|
52
52
|
return __assign.apply(this, arguments);
|
|
53
53
|
};
|
|
54
|
+
function __rest(s, e) {
|
|
55
|
+
var t = {};
|
|
56
|
+
for (var p in s)
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
58
|
+
t[p] = s[p];
|
|
59
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
60
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
61
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
62
|
+
t[p[i]] = s[p[i]];
|
|
63
|
+
}
|
|
64
|
+
return t;
|
|
65
|
+
}
|
|
54
66
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
55
67
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
56
68
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -151,7 +163,7 @@ function __spreadArray(to, from, pack) {
|
|
|
151
163
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
152
164
|
}
|
|
153
165
|
|
|
154
|
-
var version = "2.0
|
|
166
|
+
var version = "2.2.0";
|
|
155
167
|
|
|
156
168
|
var PostHogPersistedProperty;
|
|
157
169
|
(function (PostHogPersistedProperty) {
|
|
@@ -748,6 +760,26 @@ var PostHogCore = /** @class */ (function () {
|
|
|
748
760
|
}
|
|
749
761
|
return __assign({ $lib: this.getLibraryId(), $lib_version: this.getLibraryVersion(), $active_feature_flags: featureFlags ? Object.keys(featureFlags) : undefined }, featureVariantProperties);
|
|
750
762
|
};
|
|
763
|
+
PostHogCore.prototype.setupBootstrap = function (options) {
|
|
764
|
+
var _a, _b, _c, _d;
|
|
765
|
+
if ((_a = options === null || options === void 0 ? void 0 : options.bootstrap) === null || _a === void 0 ? void 0 : _a.distinctId) {
|
|
766
|
+
if ((_b = options === null || options === void 0 ? void 0 : options.bootstrap) === null || _b === void 0 ? void 0 : _b.isIdentifiedId) {
|
|
767
|
+
this.setPersistedProperty(PostHogPersistedProperty.DistinctId, options.bootstrap.distinctId);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
this.setPersistedProperty(PostHogPersistedProperty.AnonymousId, options.bootstrap.distinctId);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if ((_c = options === null || options === void 0 ? void 0 : options.bootstrap) === null || _c === void 0 ? void 0 : _c.featureFlags) {
|
|
774
|
+
var activeFlags = Object.keys(((_d = options.bootstrap) === null || _d === void 0 ? void 0 : _d.featureFlags) || {})
|
|
775
|
+
.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]); })
|
|
776
|
+
.reduce(function (res, key) {
|
|
777
|
+
var _a, _b;
|
|
778
|
+
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);
|
|
779
|
+
}, {});
|
|
780
|
+
this.setKnownFeatureFlags(activeFlags);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
751
783
|
Object.defineProperty(PostHogCore.prototype, "props", {
|
|
752
784
|
// NOTE: Props are lazy loaded from localstorage hence the complex getter setter logic
|
|
753
785
|
get: function () {
|
|
@@ -991,8 +1023,7 @@ var PostHogCore = /** @class */ (function () {
|
|
|
991
1023
|
.then(function (r) { return r.json(); })
|
|
992
1024
|
.then(function (res) {
|
|
993
1025
|
if (res.featureFlags) {
|
|
994
|
-
_this.
|
|
995
|
-
_this._events.emit('featureflags', res.featureFlags);
|
|
1026
|
+
_this.setKnownFeatureFlags(res.featureFlags);
|
|
996
1027
|
}
|
|
997
1028
|
return res;
|
|
998
1029
|
})
|
|
@@ -1003,6 +1034,10 @@ var PostHogCore = /** @class */ (function () {
|
|
|
1003
1034
|
});
|
|
1004
1035
|
});
|
|
1005
1036
|
};
|
|
1037
|
+
PostHogCore.prototype.setKnownFeatureFlags = function (featureFlags) {
|
|
1038
|
+
this.setPersistedProperty(PostHogPersistedProperty.FeatureFlags, featureFlags);
|
|
1039
|
+
this._events.emit('featureflags', featureFlags);
|
|
1040
|
+
};
|
|
1006
1041
|
PostHogCore.prototype.getFeatureFlag = function (key) {
|
|
1007
1042
|
var featureFlags = this.getFeatureFlags();
|
|
1008
1043
|
if (!featureFlags) {
|
|
@@ -1230,6 +1265,42 @@ var PostHogMemoryStorage = /** @class */ (function () {
|
|
|
1230
1265
|
return PostHogMemoryStorage;
|
|
1231
1266
|
}());
|
|
1232
1267
|
|
|
1268
|
+
// So that alternative implementations can be used if desired
|
|
1269
|
+
|
|
1270
|
+
var fetch = function (url, options) {
|
|
1271
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
1272
|
+
var res;
|
|
1273
|
+
return __generator(this, function (_a) {
|
|
1274
|
+
switch (_a.label) {
|
|
1275
|
+
case 0:
|
|
1276
|
+
return [4
|
|
1277
|
+
/*yield*/
|
|
1278
|
+
, axios__default["default"].request({
|
|
1279
|
+
url: url,
|
|
1280
|
+
headers: options.headers,
|
|
1281
|
+
method: options.method.toLowerCase(),
|
|
1282
|
+
data: options.body,
|
|
1283
|
+
signal: options.signal
|
|
1284
|
+
})];
|
|
1285
|
+
|
|
1286
|
+
case 1:
|
|
1287
|
+
res = _a.sent();
|
|
1288
|
+
return [2
|
|
1289
|
+
/*return*/
|
|
1290
|
+
, {
|
|
1291
|
+
status: res.status,
|
|
1292
|
+
text: function () {
|
|
1293
|
+
return res.data;
|
|
1294
|
+
},
|
|
1295
|
+
json: function () {
|
|
1296
|
+
return res.data;
|
|
1297
|
+
}
|
|
1298
|
+
}];
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1233
1304
|
var LONG_SCALE = 0xfffffffffffffff;
|
|
1234
1305
|
|
|
1235
1306
|
var ClientError =
|
|
@@ -1278,7 +1349,9 @@ function () {
|
|
|
1278
1349
|
personalApiKey = _a.personalApiKey,
|
|
1279
1350
|
projectApiKey = _a.projectApiKey,
|
|
1280
1351
|
timeout = _a.timeout,
|
|
1281
|
-
host = _a.host
|
|
1352
|
+
host = _a.host,
|
|
1353
|
+
options = __rest(_a, ["pollingInterval", "personalApiKey", "projectApiKey", "timeout", "host"]);
|
|
1354
|
+
|
|
1282
1355
|
this.pollingInterval = pollingInterval;
|
|
1283
1356
|
this.personalApiKey = personalApiKey;
|
|
1284
1357
|
this.featureFlags = [];
|
|
@@ -1287,7 +1360,9 @@ function () {
|
|
|
1287
1360
|
this.timeout = timeout;
|
|
1288
1361
|
this.projectApiKey = projectApiKey;
|
|
1289
1362
|
this.host = host;
|
|
1290
|
-
this.poller = undefined;
|
|
1363
|
+
this.poller = undefined; // NOTE: as any is required here as the AbortSignal typing is slightly misaligned but works just fine
|
|
1364
|
+
|
|
1365
|
+
this.fetch = options.fetch || fetch;
|
|
1291
1366
|
void this.loadFeatureFlags();
|
|
1292
1367
|
}
|
|
1293
1368
|
|
|
@@ -1453,16 +1528,44 @@ function () {
|
|
|
1453
1528
|
};
|
|
1454
1529
|
|
|
1455
1530
|
FeatureFlagsPoller.prototype.matchFeatureFlagProperties = function (flag, distinctId, properties) {
|
|
1456
|
-
var
|
|
1531
|
+
var _a;
|
|
1457
1532
|
|
|
1458
1533
|
var flagFilters = flag.filters || {};
|
|
1459
1534
|
var flagConditions = flagFilters.groups || [];
|
|
1460
1535
|
var isInconclusive = false;
|
|
1461
|
-
var result = undefined;
|
|
1462
|
-
|
|
1536
|
+
var result = undefined; // # Stable sort conditions with variant overrides to the top. This ensures that if overrides are present, they are
|
|
1537
|
+
// # evaluated first, and the variant override is applied to the first matching condition.
|
|
1538
|
+
|
|
1539
|
+
var sortedFlagConditions = __spreadArray([], flagConditions, true).sort(function (conditionA, conditionB) {
|
|
1540
|
+
var AHasVariantOverride = !!conditionA.variant;
|
|
1541
|
+
var BHasVariantOverride = !!conditionB.variant;
|
|
1542
|
+
|
|
1543
|
+
if (AHasVariantOverride && BHasVariantOverride) {
|
|
1544
|
+
return 0;
|
|
1545
|
+
} else if (AHasVariantOverride) {
|
|
1546
|
+
return -1;
|
|
1547
|
+
} else if (BHasVariantOverride) {
|
|
1548
|
+
return 1;
|
|
1549
|
+
} else {
|
|
1550
|
+
return 0;
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1554
|
+
var _loop_1 = function (condition) {
|
|
1463
1555
|
try {
|
|
1464
|
-
if (
|
|
1465
|
-
|
|
1556
|
+
if (this_1.isConditionMatch(flag, distinctId, condition, properties)) {
|
|
1557
|
+
var variantOverride_1 = condition.variant;
|
|
1558
|
+
var flagVariants = ((_a = flagFilters.multivariate) === null || _a === void 0 ? void 0 : _a.variants) || [];
|
|
1559
|
+
|
|
1560
|
+
if (variantOverride_1 && flagVariants.some(function (variant) {
|
|
1561
|
+
return variant.key === variantOverride_1;
|
|
1562
|
+
})) {
|
|
1563
|
+
result = variantOverride_1;
|
|
1564
|
+
} else {
|
|
1565
|
+
result = this_1.getMatchingVariant(flag, distinctId) || true;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
return "break";
|
|
1466
1569
|
}
|
|
1467
1570
|
} catch (e) {
|
|
1468
1571
|
if (e instanceof InconclusiveMatchError) {
|
|
@@ -1471,7 +1574,17 @@ function () {
|
|
|
1471
1574
|
throw e;
|
|
1472
1575
|
}
|
|
1473
1576
|
}
|
|
1474
|
-
}
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
var this_1 = this;
|
|
1580
|
+
|
|
1581
|
+
for (var _i = 0, sortedFlagConditions_1 = sortedFlagConditions; _i < sortedFlagConditions_1.length; _i++) {
|
|
1582
|
+
var condition = sortedFlagConditions_1[_i];
|
|
1583
|
+
|
|
1584
|
+
var state_1 = _loop_1(condition);
|
|
1585
|
+
|
|
1586
|
+
if (state_1 === "break") break;
|
|
1587
|
+
}
|
|
1475
1588
|
|
|
1476
1589
|
if (result !== undefined) {
|
|
1477
1590
|
return result;
|
|
@@ -1599,13 +1712,13 @@ function () {
|
|
|
1599
1712
|
case 2:
|
|
1600
1713
|
res = _a.sent();
|
|
1601
1714
|
|
|
1602
|
-
if (res && res.
|
|
1715
|
+
if (res && res.status === 401) {
|
|
1603
1716
|
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");
|
|
1604
1717
|
}
|
|
1605
1718
|
|
|
1606
1719
|
return [4
|
|
1607
1720
|
/*yield*/
|
|
1608
|
-
, res.
|
|
1721
|
+
, res.json()];
|
|
1609
1722
|
|
|
1610
1723
|
case 3:
|
|
1611
1724
|
responseJson = _a.sent();
|
|
@@ -1644,48 +1757,57 @@ function () {
|
|
|
1644
1757
|
|
|
1645
1758
|
FeatureFlagsPoller.prototype._requestFeatureFlagDefinitions = function () {
|
|
1646
1759
|
return __awaiter(this, void 0, void 0, function () {
|
|
1647
|
-
var url,
|
|
1760
|
+
var url, options, abortTimeout, controller_1, err_2;
|
|
1648
1761
|
return __generator(this, function (_a) {
|
|
1649
1762
|
switch (_a.label) {
|
|
1650
1763
|
case 0:
|
|
1651
1764
|
url = "".concat(this.host, "/api/feature_flag/local_evaluation?token=").concat(this.projectApiKey);
|
|
1652
|
-
headers = {
|
|
1653
|
-
'Content-Type': 'application/json',
|
|
1654
|
-
Authorization: "Bearer ".concat(this.personalApiKey),
|
|
1655
|
-
'user-agent': "posthog-node/".concat(version)
|
|
1656
|
-
};
|
|
1657
1765
|
options = {
|
|
1658
1766
|
method: 'GET',
|
|
1659
|
-
headers:
|
|
1767
|
+
headers: {
|
|
1768
|
+
'Content-Type': 'application/json',
|
|
1769
|
+
Authorization: "Bearer ".concat(this.personalApiKey),
|
|
1770
|
+
'user-agent': "posthog-node/".concat(version)
|
|
1771
|
+
}
|
|
1660
1772
|
};
|
|
1773
|
+
abortTimeout = null;
|
|
1661
1774
|
|
|
1662
1775
|
if (this.timeout && typeof this.timeout === 'number') {
|
|
1663
|
-
|
|
1776
|
+
controller_1 = new AbortController();
|
|
1777
|
+
abortTimeout = safeSetTimeout(function () {
|
|
1778
|
+
controller_1.abort();
|
|
1779
|
+
}, this.timeout);
|
|
1780
|
+
options.signal = controller_1.signal;
|
|
1664
1781
|
}
|
|
1665
1782
|
|
|
1666
1783
|
_a.label = 1;
|
|
1667
1784
|
|
|
1668
1785
|
case 1:
|
|
1669
|
-
_a.trys.push([1, 3
|
|
1786
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
1670
1787
|
|
|
1671
1788
|
return [4
|
|
1672
1789
|
/*yield*/
|
|
1673
|
-
,
|
|
1790
|
+
, this.fetch(url, options)];
|
|
1674
1791
|
|
|
1675
1792
|
case 2:
|
|
1676
|
-
|
|
1677
|
-
return
|
|
1678
|
-
|
|
1679
|
-
, 4];
|
|
1793
|
+
return [2
|
|
1794
|
+
/*return*/
|
|
1795
|
+
, _a.sent()];
|
|
1680
1796
|
|
|
1681
1797
|
case 3:
|
|
1682
1798
|
err_2 = _a.sent();
|
|
1683
1799
|
throw new Error("Request failed with error: ".concat(err_2));
|
|
1684
1800
|
|
|
1685
1801
|
case 4:
|
|
1802
|
+
clearTimeout(abortTimeout);
|
|
1803
|
+
return [7
|
|
1804
|
+
/*endfinally*/
|
|
1805
|
+
];
|
|
1806
|
+
|
|
1807
|
+
case 5:
|
|
1686
1808
|
return [2
|
|
1687
1809
|
/*return*/
|
|
1688
|
-
|
|
1810
|
+
];
|
|
1689
1811
|
}
|
|
1690
1812
|
});
|
|
1691
1813
|
});
|
|
@@ -1759,6 +1881,17 @@ function matchProperty(property, propertyValues) {
|
|
|
1759
1881
|
case 'lte':
|
|
1760
1882
|
return typeof overrideValue == typeof value && overrideValue <= value;
|
|
1761
1883
|
|
|
1884
|
+
case 'is_date_after':
|
|
1885
|
+
case 'is_date_before':
|
|
1886
|
+
var parsedDate = convertToDateTime(value);
|
|
1887
|
+
var overrideDate = convertToDateTime(overrideValue);
|
|
1888
|
+
|
|
1889
|
+
if (operator === 'is_date_before') {
|
|
1890
|
+
return overrideDate < parsedDate;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
return overrideDate > parsedDate;
|
|
1894
|
+
|
|
1762
1895
|
default:
|
|
1763
1896
|
console.error("Unknown operator: ".concat(operator));
|
|
1764
1897
|
return false;
|
|
@@ -1774,6 +1907,22 @@ function isValidRegex(regex) {
|
|
|
1774
1907
|
}
|
|
1775
1908
|
}
|
|
1776
1909
|
|
|
1910
|
+
function convertToDateTime(value) {
|
|
1911
|
+
if (value instanceof Date) {
|
|
1912
|
+
return value;
|
|
1913
|
+
} else if (typeof value === 'string' || typeof value === 'number') {
|
|
1914
|
+
var date = new Date(value);
|
|
1915
|
+
|
|
1916
|
+
if (!isNaN(date.valueOf())) {
|
|
1917
|
+
return date;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
throw new InconclusiveMatchError("".concat(value, " is in an invalid date format"));
|
|
1921
|
+
} else {
|
|
1922
|
+
throw new InconclusiveMatchError("The date provided ".concat(value, " must be a string, number, or date object"));
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1777
1926
|
var THIRTY_SECONDS = 30 * 1000;
|
|
1778
1927
|
var MAX_CACHE_SIZE = 50 * 1000;
|
|
1779
1928
|
|
|
@@ -1795,6 +1944,7 @@ function (_super) {
|
|
|
1795
1944
|
options.sendFeatureFlagEvent = false; // Let `posthog-node` handle this on its own, since we're dealing with multiple distinctIDs
|
|
1796
1945
|
|
|
1797
1946
|
_this = _super.call(this, apiKey, options) || this;
|
|
1947
|
+
_this.options = options;
|
|
1798
1948
|
_this._memoryStorage = new PostHogMemoryStorage();
|
|
1799
1949
|
return _this;
|
|
1800
1950
|
}
|
|
@@ -1813,7 +1963,7 @@ function (_super) {
|
|
|
1813
1963
|
};
|
|
1814
1964
|
|
|
1815
1965
|
PostHogClient.prototype.fetch = function (url, options) {
|
|
1816
|
-
return
|
|
1966
|
+
return this.options.fetch ? this.options.fetch(url, options) : fetch(url, options);
|
|
1817
1967
|
};
|
|
1818
1968
|
|
|
1819
1969
|
PostHogClient.prototype.getLibraryId = function () {
|
|
@@ -1848,7 +1998,8 @@ function () {
|
|
|
1848
1998
|
personalApiKey: options.personalApiKey,
|
|
1849
1999
|
projectApiKey: apiKey,
|
|
1850
2000
|
timeout: options.requestTimeout,
|
|
1851
|
-
host: this._sharedClient.host
|
|
2001
|
+
host: this._sharedClient.host,
|
|
2002
|
+
fetch: options.fetch
|
|
1852
2003
|
});
|
|
1853
2004
|
}
|
|
1854
2005
|
|