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/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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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 (
|
|
1457
|
-
|
|
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.
|
|
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.
|
|
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,
|
|
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:
|
|
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
|
-
|
|
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
|
|
1778
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
1662
1779
|
|
|
1663
1780
|
return [4
|
|
1664
1781
|
/*yield*/
|
|
1665
|
-
,
|
|
1782
|
+
, this.fetch(url, options)];
|
|
1666
1783
|
|
|
1667
1784
|
case 2:
|
|
1668
|
-
|
|
1669
|
-
return
|
|
1670
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|