posthog-node 4.14.0 → 4.15.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 +5 -0
- package/lib/index.cjs.js +224 -25
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +42 -0
- package/lib/index.esm.js +224 -25
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +10 -0
- package/lib/posthog-core/src/utils.d.ts +1 -0
- package/lib/posthog-node/src/posthog-node.d.ts +7 -0
- package/lib/posthog-node/src/types.d.ts +26 -0
- package/package.json +1 -1
- package/src/posthog-node.ts +100 -0
- package/src/types.ts +26 -0
package/CHANGELOG.md
CHANGED
package/lib/index.cjs.js
CHANGED
|
@@ -22,7 +22,7 @@ function _interopNamespace(e) {
|
|
|
22
22
|
return Object.freeze(n);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
var version = "4.
|
|
25
|
+
var version = "4.15.0";
|
|
26
26
|
|
|
27
27
|
var PostHogPersistedProperty;
|
|
28
28
|
(function (PostHogPersistedProperty) {
|
|
@@ -278,6 +278,7 @@ const NEW_FLAGS_EXCLUDED_HASHES = new Set([
|
|
|
278
278
|
'fc80b8e2',
|
|
279
279
|
'75cc0998',
|
|
280
280
|
]);
|
|
281
|
+
const STRING_FORMAT = 'utf8';
|
|
281
282
|
function assert(truthyValue, message) {
|
|
282
283
|
if (!truthyValue || typeof truthyValue !== 'string' || isEmpty(truthyValue)) {
|
|
283
284
|
throw new Error(message);
|
|
@@ -1216,11 +1217,21 @@ const uuidv7 = () => uuidv7obj().toString();
|
|
|
1216
1217
|
const uuidv7obj = () => (defaultGenerator || (defaultGenerator = new V7Generator())).generate();
|
|
1217
1218
|
|
|
1218
1219
|
class PostHogFetchHttpError extends Error {
|
|
1219
|
-
constructor(response) {
|
|
1220
|
-
super('HTTP error while fetching PostHog: ' + response.status);
|
|
1220
|
+
constructor(response, reqByteLength) {
|
|
1221
|
+
super('HTTP error while fetching PostHog: status=' + response.status + ', reqByteLength=' + reqByteLength);
|
|
1221
1222
|
this.response = response;
|
|
1223
|
+
this.reqByteLength = reqByteLength;
|
|
1222
1224
|
this.name = 'PostHogFetchHttpError';
|
|
1223
1225
|
}
|
|
1226
|
+
get status() {
|
|
1227
|
+
return this.response.status;
|
|
1228
|
+
}
|
|
1229
|
+
get text() {
|
|
1230
|
+
return this.response.text();
|
|
1231
|
+
}
|
|
1232
|
+
get json() {
|
|
1233
|
+
return this.response.json();
|
|
1234
|
+
}
|
|
1224
1235
|
}
|
|
1225
1236
|
class PostHogFetchNetworkError extends Error {
|
|
1226
1237
|
constructor(error) {
|
|
@@ -1232,6 +1243,20 @@ class PostHogFetchNetworkError extends Error {
|
|
|
1232
1243
|
this.name = 'PostHogFetchNetworkError';
|
|
1233
1244
|
}
|
|
1234
1245
|
}
|
|
1246
|
+
async function logFlushError(err) {
|
|
1247
|
+
if (err instanceof PostHogFetchHttpError) {
|
|
1248
|
+
let text = '';
|
|
1249
|
+
try {
|
|
1250
|
+
text = await err.text;
|
|
1251
|
+
}
|
|
1252
|
+
catch { }
|
|
1253
|
+
console.error(`Error while flushing PostHog: message=${err.message}, response body=${text}`, err);
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
console.error('Error while flushing PostHog', err);
|
|
1257
|
+
}
|
|
1258
|
+
return Promise.resolve();
|
|
1259
|
+
}
|
|
1235
1260
|
function isPostHogFetchError(err) {
|
|
1236
1261
|
return typeof err === 'object' && (err instanceof PostHogFetchHttpError || err instanceof PostHogFetchNetworkError);
|
|
1237
1262
|
}
|
|
@@ -1365,12 +1390,26 @@ class PostHogCoreStateless {
|
|
|
1365
1390
|
this.enqueue('identify', payload, options);
|
|
1366
1391
|
});
|
|
1367
1392
|
}
|
|
1393
|
+
async identifyStatelessImmediate(distinctId, properties, options) {
|
|
1394
|
+
const payload = {
|
|
1395
|
+
...this.buildPayload({
|
|
1396
|
+
distinct_id: distinctId,
|
|
1397
|
+
event: '$identify',
|
|
1398
|
+
properties,
|
|
1399
|
+
}),
|
|
1400
|
+
};
|
|
1401
|
+
await this.sendImmediate('identify', payload, options);
|
|
1402
|
+
}
|
|
1368
1403
|
captureStateless(distinctId, event, properties, options) {
|
|
1369
1404
|
this.wrap(() => {
|
|
1370
1405
|
const payload = this.buildPayload({ distinct_id: distinctId, event, properties });
|
|
1371
1406
|
this.enqueue('capture', payload, options);
|
|
1372
1407
|
});
|
|
1373
1408
|
}
|
|
1409
|
+
async captureStatelessImmediate(distinctId, event, properties, options) {
|
|
1410
|
+
const payload = this.buildPayload({ distinct_id: distinctId, event, properties });
|
|
1411
|
+
await this.sendImmediate('capture', payload, options);
|
|
1412
|
+
}
|
|
1374
1413
|
aliasStateless(alias, distinctId, properties, options) {
|
|
1375
1414
|
this.wrap(() => {
|
|
1376
1415
|
const payload = this.buildPayload({
|
|
@@ -1385,6 +1424,18 @@ class PostHogCoreStateless {
|
|
|
1385
1424
|
this.enqueue('alias', payload, options);
|
|
1386
1425
|
});
|
|
1387
1426
|
}
|
|
1427
|
+
async aliasStatelessImmediate(alias, distinctId, properties, options) {
|
|
1428
|
+
const payload = this.buildPayload({
|
|
1429
|
+
event: '$create_alias',
|
|
1430
|
+
distinct_id: distinctId,
|
|
1431
|
+
properties: {
|
|
1432
|
+
...(properties || {}),
|
|
1433
|
+
distinct_id: distinctId,
|
|
1434
|
+
alias,
|
|
1435
|
+
},
|
|
1436
|
+
});
|
|
1437
|
+
await this.sendImmediate('alias', payload, options);
|
|
1438
|
+
}
|
|
1388
1439
|
/***
|
|
1389
1440
|
*** GROUPS
|
|
1390
1441
|
***/
|
|
@@ -1628,25 +1679,7 @@ class PostHogCoreStateless {
|
|
|
1628
1679
|
this._events.emit(type, `Library is disabled. Not sending event. To re-enable, call posthog.optIn()`);
|
|
1629
1680
|
return;
|
|
1630
1681
|
}
|
|
1631
|
-
const message =
|
|
1632
|
-
..._message,
|
|
1633
|
-
type: type,
|
|
1634
|
-
library: this.getLibraryId(),
|
|
1635
|
-
library_version: this.getLibraryVersion(),
|
|
1636
|
-
timestamp: options?.timestamp ? options?.timestamp : currentISOTime(),
|
|
1637
|
-
uuid: options?.uuid ? options.uuid : uuidv7(),
|
|
1638
|
-
};
|
|
1639
|
-
const addGeoipDisableProperty = options?.disableGeoip ?? this.disableGeoip;
|
|
1640
|
-
if (addGeoipDisableProperty) {
|
|
1641
|
-
if (!message.properties) {
|
|
1642
|
-
message.properties = {};
|
|
1643
|
-
}
|
|
1644
|
-
message['properties']['$geoip_disable'] = true;
|
|
1645
|
-
}
|
|
1646
|
-
if (message.distinctId) {
|
|
1647
|
-
message.distinct_id = message.distinctId;
|
|
1648
|
-
delete message.distinctId;
|
|
1649
|
-
}
|
|
1682
|
+
const message = this.prepareMessage(type, _message, options);
|
|
1650
1683
|
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1651
1684
|
if (queue.length >= this.maxQueueSize) {
|
|
1652
1685
|
queue.shift();
|
|
@@ -1664,6 +1697,73 @@ class PostHogCoreStateless {
|
|
|
1664
1697
|
}
|
|
1665
1698
|
});
|
|
1666
1699
|
}
|
|
1700
|
+
async sendImmediate(type, _message, options) {
|
|
1701
|
+
if (this.disabled) {
|
|
1702
|
+
this.logMsgIfDebug(() => console.warn('[PostHog] The client is disabled'));
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
if (!this._isInitialized) {
|
|
1706
|
+
await this._initPromise;
|
|
1707
|
+
}
|
|
1708
|
+
if (this.optedOut) {
|
|
1709
|
+
this._events.emit(type, `Library is disabled. Not sending event. To re-enable, call posthog.optIn()`);
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
const data = {
|
|
1713
|
+
api_key: this.apiKey,
|
|
1714
|
+
batch: [this.prepareMessage(type, _message, options)],
|
|
1715
|
+
sent_at: currentISOTime(),
|
|
1716
|
+
};
|
|
1717
|
+
if (this.historicalMigration) {
|
|
1718
|
+
data.historical_migration = true;
|
|
1719
|
+
}
|
|
1720
|
+
const payload = JSON.stringify(data);
|
|
1721
|
+
const url = this.captureMode === 'form'
|
|
1722
|
+
? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
|
|
1723
|
+
: `${this.host}/batch/`;
|
|
1724
|
+
const fetchOptions = this.captureMode === 'form'
|
|
1725
|
+
? {
|
|
1726
|
+
method: 'POST',
|
|
1727
|
+
mode: 'no-cors',
|
|
1728
|
+
credentials: 'omit',
|
|
1729
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1730
|
+
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1731
|
+
}
|
|
1732
|
+
: {
|
|
1733
|
+
method: 'POST',
|
|
1734
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1735
|
+
body: payload,
|
|
1736
|
+
};
|
|
1737
|
+
try {
|
|
1738
|
+
await this.fetchWithRetry(url, fetchOptions);
|
|
1739
|
+
}
|
|
1740
|
+
catch (err) {
|
|
1741
|
+
this._events.emit('error', err);
|
|
1742
|
+
throw err;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
prepareMessage(type, _message, options) {
|
|
1746
|
+
const message = {
|
|
1747
|
+
..._message,
|
|
1748
|
+
type: type,
|
|
1749
|
+
library: this.getLibraryId(),
|
|
1750
|
+
library_version: this.getLibraryVersion(),
|
|
1751
|
+
timestamp: options?.timestamp ? options?.timestamp : currentISOTime(),
|
|
1752
|
+
uuid: options?.uuid ? options.uuid : uuidv7(),
|
|
1753
|
+
};
|
|
1754
|
+
const addGeoipDisableProperty = options?.disableGeoip ?? this.disableGeoip;
|
|
1755
|
+
if (addGeoipDisableProperty) {
|
|
1756
|
+
if (!message.properties) {
|
|
1757
|
+
message.properties = {};
|
|
1758
|
+
}
|
|
1759
|
+
message['properties']['$geoip_disable'] = true;
|
|
1760
|
+
}
|
|
1761
|
+
if (message.distinctId) {
|
|
1762
|
+
message.distinct_id = message.distinctId;
|
|
1763
|
+
delete message.distinctId;
|
|
1764
|
+
}
|
|
1765
|
+
return message;
|
|
1766
|
+
}
|
|
1667
1767
|
clearFlushTimer() {
|
|
1668
1768
|
if (this._flushTimer) {
|
|
1669
1769
|
clearTimeout(this._flushTimer);
|
|
@@ -1675,7 +1775,9 @@ class PostHogCoreStateless {
|
|
|
1675
1775
|
* Avoids unnecessary promise errors
|
|
1676
1776
|
*/
|
|
1677
1777
|
flushBackground() {
|
|
1678
|
-
void this.flush().catch(() => {
|
|
1778
|
+
void this.flush().catch(async (err) => {
|
|
1779
|
+
await logFlushError(err);
|
|
1780
|
+
});
|
|
1679
1781
|
}
|
|
1680
1782
|
async flush() {
|
|
1681
1783
|
if (!this.flushPromise) {
|
|
@@ -1759,6 +1861,8 @@ class PostHogCoreStateless {
|
|
|
1759
1861
|
setTimeout(() => ctrl.abort(), ms);
|
|
1760
1862
|
return ctrl.signal;
|
|
1761
1863
|
});
|
|
1864
|
+
const body = options.body ? options.body : '';
|
|
1865
|
+
const reqByteLength = Buffer.byteLength(body, STRING_FORMAT);
|
|
1762
1866
|
return await retriable(async () => {
|
|
1763
1867
|
let res = null;
|
|
1764
1868
|
try {
|
|
@@ -1776,7 +1880,7 @@ class PostHogCoreStateless {
|
|
|
1776
1880
|
// https://developer.mozilla.org/en-US/docs/Web/API/Request/mode#no-cors
|
|
1777
1881
|
const isNoCors = options.mode === 'no-cors';
|
|
1778
1882
|
if (!isNoCors && (res.status < 200 || res.status >= 400)) {
|
|
1779
|
-
throw new PostHogFetchHttpError(res);
|
|
1883
|
+
throw new PostHogFetchHttpError(res, reqByteLength);
|
|
1780
1884
|
}
|
|
1781
1885
|
return res;
|
|
1782
1886
|
}, { ...this._retryOptions, ...retryOptions });
|
|
@@ -1808,7 +1912,7 @@ class PostHogCoreStateless {
|
|
|
1808
1912
|
if (!isPostHogFetchError(e)) {
|
|
1809
1913
|
throw e;
|
|
1810
1914
|
}
|
|
1811
|
-
|
|
1915
|
+
await logFlushError(e);
|
|
1812
1916
|
}
|
|
1813
1917
|
};
|
|
1814
1918
|
return Promise.race([
|
|
@@ -3689,6 +3793,79 @@ class PostHog extends PostHogCoreStateless {
|
|
|
3689
3793
|
});
|
|
3690
3794
|
this.addPendingPromise(capturePromise);
|
|
3691
3795
|
}
|
|
3796
|
+
async captureImmediate(props) {
|
|
3797
|
+
if (typeof props === 'string') {
|
|
3798
|
+
this.logMsgIfDebug(() => console.warn('Called capture() with a string as the first argument when an object was expected.'));
|
|
3799
|
+
}
|
|
3800
|
+
const {
|
|
3801
|
+
distinctId,
|
|
3802
|
+
event,
|
|
3803
|
+
properties,
|
|
3804
|
+
groups,
|
|
3805
|
+
sendFeatureFlags,
|
|
3806
|
+
timestamp,
|
|
3807
|
+
disableGeoip,
|
|
3808
|
+
uuid
|
|
3809
|
+
} = props;
|
|
3810
|
+
const _capture = props => {
|
|
3811
|
+
return super.captureStatelessImmediate(distinctId, event, props, {
|
|
3812
|
+
timestamp,
|
|
3813
|
+
disableGeoip,
|
|
3814
|
+
uuid
|
|
3815
|
+
});
|
|
3816
|
+
};
|
|
3817
|
+
const _getFlags = async (distinctId, groups, disableGeoip) => {
|
|
3818
|
+
return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags;
|
|
3819
|
+
};
|
|
3820
|
+
const capturePromise = Promise.resolve().then(async () => {
|
|
3821
|
+
if (sendFeatureFlags) {
|
|
3822
|
+
// If we are sending feature flags, we need to make sure we have the latest flags
|
|
3823
|
+
// return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
|
|
3824
|
+
return await _getFlags(distinctId, groups, disableGeoip);
|
|
3825
|
+
}
|
|
3826
|
+
if (event === '$feature_flag_called') {
|
|
3827
|
+
// If we're capturing a $feature_flag_called event, we don't want to enrich the event with cached flags that may be out of date.
|
|
3828
|
+
return {};
|
|
3829
|
+
}
|
|
3830
|
+
if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
|
|
3831
|
+
// Otherwise we may as well check for the flags locally and include them if they are already loaded
|
|
3832
|
+
const groupsWithStringValues = {};
|
|
3833
|
+
for (const [key, value] of Object.entries(groups || {})) {
|
|
3834
|
+
groupsWithStringValues[key] = String(value);
|
|
3835
|
+
}
|
|
3836
|
+
return await this.getAllFlags(distinctId, {
|
|
3837
|
+
groups: groupsWithStringValues,
|
|
3838
|
+
disableGeoip,
|
|
3839
|
+
onlyEvaluateLocally: true
|
|
3840
|
+
});
|
|
3841
|
+
}
|
|
3842
|
+
return {};
|
|
3843
|
+
}).then(flags => {
|
|
3844
|
+
// Derive the relevant flag properties to add
|
|
3845
|
+
const additionalProperties = {};
|
|
3846
|
+
if (flags) {
|
|
3847
|
+
for (const [feature, variant] of Object.entries(flags)) {
|
|
3848
|
+
additionalProperties[`$feature/${feature}`] = variant;
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
const activeFlags = Object.keys(flags || {}).filter(flag => flags?.[flag] !== false).sort();
|
|
3852
|
+
if (activeFlags.length > 0) {
|
|
3853
|
+
additionalProperties['$active_feature_flags'] = activeFlags;
|
|
3854
|
+
}
|
|
3855
|
+
return additionalProperties;
|
|
3856
|
+
}).catch(() => {
|
|
3857
|
+
// Something went wrong getting the flag info - we should capture the event anyways
|
|
3858
|
+
return {};
|
|
3859
|
+
}).then(additionalProperties => {
|
|
3860
|
+
// No matter what - capture the event
|
|
3861
|
+
_capture({
|
|
3862
|
+
...additionalProperties,
|
|
3863
|
+
...properties,
|
|
3864
|
+
$groups: groups
|
|
3865
|
+
});
|
|
3866
|
+
});
|
|
3867
|
+
await capturePromise;
|
|
3868
|
+
}
|
|
3692
3869
|
identify({
|
|
3693
3870
|
distinctId,
|
|
3694
3871
|
properties,
|
|
@@ -3707,11 +3884,33 @@ class PostHog extends PostHogCoreStateless {
|
|
|
3707
3884
|
disableGeoip
|
|
3708
3885
|
});
|
|
3709
3886
|
}
|
|
3887
|
+
async identifyImmediate({
|
|
3888
|
+
distinctId,
|
|
3889
|
+
properties,
|
|
3890
|
+
disableGeoip
|
|
3891
|
+
}) {
|
|
3892
|
+
// promote $set and $set_once to top level
|
|
3893
|
+
const userPropsOnce = properties?.$set_once;
|
|
3894
|
+
delete properties?.$set_once;
|
|
3895
|
+
// if no $set is provided we assume all properties are $set
|
|
3896
|
+
const userProps = properties?.$set || properties;
|
|
3897
|
+
await super.identifyStatelessImmediate(distinctId, {
|
|
3898
|
+
$set: userProps,
|
|
3899
|
+
$set_once: userPropsOnce
|
|
3900
|
+
}, {
|
|
3901
|
+
disableGeoip
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3710
3904
|
alias(data) {
|
|
3711
3905
|
super.aliasStateless(data.alias, data.distinctId, undefined, {
|
|
3712
3906
|
disableGeoip: data.disableGeoip
|
|
3713
3907
|
});
|
|
3714
3908
|
}
|
|
3909
|
+
async aliasImmediate(data) {
|
|
3910
|
+
await super.aliasStatelessImmediate(data.alias, data.distinctId, undefined, {
|
|
3911
|
+
disableGeoip: data.disableGeoip
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3715
3914
|
isLocalEvaluationReady() {
|
|
3716
3915
|
return this.featureFlagsPoller?.isLocalEvaluationReady() ?? false;
|
|
3717
3916
|
}
|