posthog-node 4.15.0 → 4.16.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 +4 -0
- package/lib/index.cjs.js +105 -53
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +9 -2
- package/lib/index.esm.js +105 -53
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +7 -0
- package/lib/posthog-core/src/utils.d.ts +1 -1
- package/lib/posthog-node/src/posthog-node.d.ts +1 -1
- package/lib/posthog-node/test/test-utils.d.ts +1 -0
- package/package.json +1 -1
- package/src/posthog-node.ts +2 -2
- package/test/posthog-node.spec.ts +21 -23
- package/test/test-utils.ts +5 -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.16.0";
|
|
26
26
|
|
|
27
27
|
var PostHogPersistedProperty;
|
|
28
28
|
(function (PostHogPersistedProperty) {
|
|
@@ -1260,6 +1260,9 @@ async function logFlushError(err) {
|
|
|
1260
1260
|
function isPostHogFetchError(err) {
|
|
1261
1261
|
return typeof err === 'object' && (err instanceof PostHogFetchHttpError || err instanceof PostHogFetchNetworkError);
|
|
1262
1262
|
}
|
|
1263
|
+
function isPostHogFetchContentTooLargeError(err) {
|
|
1264
|
+
return typeof err === 'object' && err instanceof PostHogFetchHttpError && err.status === 413;
|
|
1265
|
+
}
|
|
1263
1266
|
var QuotaLimitedFeature;
|
|
1264
1267
|
(function (QuotaLimitedFeature) {
|
|
1265
1268
|
QuotaLimitedFeature["FeatureFlags"] = "feature_flags";
|
|
@@ -1268,6 +1271,7 @@ var QuotaLimitedFeature;
|
|
|
1268
1271
|
class PostHogCoreStateless {
|
|
1269
1272
|
constructor(apiKey, options) {
|
|
1270
1273
|
this.flushPromise = null;
|
|
1274
|
+
this.shutdownPromise = null;
|
|
1271
1275
|
this.pendingPromises = {};
|
|
1272
1276
|
// internal
|
|
1273
1277
|
this._events = new SimpleEventEmitter();
|
|
@@ -1780,13 +1784,21 @@ class PostHogCoreStateless {
|
|
|
1780
1784
|
});
|
|
1781
1785
|
}
|
|
1782
1786
|
async flush() {
|
|
1783
|
-
|
|
1784
|
-
|
|
1787
|
+
// Wait for the current flush operation to finish (regardless of success or failure), then try to flush again.
|
|
1788
|
+
// Use allSettled instead of finally to be defensive around flush throwing errors immediately rather than rejecting.
|
|
1789
|
+
const nextFlushPromise = Promise.allSettled([this.flushPromise]).then(() => {
|
|
1790
|
+
return this._flush();
|
|
1791
|
+
});
|
|
1792
|
+
this.flushPromise = nextFlushPromise;
|
|
1793
|
+
void this.addPendingPromise(nextFlushPromise);
|
|
1794
|
+
Promise.allSettled([nextFlushPromise]).then(() => {
|
|
1795
|
+
// If there are no others waiting to flush, clear the promise.
|
|
1796
|
+
// We don't strictly need to do this, but it could make debugging easier
|
|
1797
|
+
if (this.flushPromise === nextFlushPromise) {
|
|
1785
1798
|
this.flushPromise = null;
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
return this.flushPromise;
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
return nextFlushPromise;
|
|
1790
1802
|
}
|
|
1791
1803
|
getCustomHeaders() {
|
|
1792
1804
|
// Don't set the user agent if we're not on a browser. The latest spec allows
|
|
@@ -1803,56 +1815,80 @@ class PostHogCoreStateless {
|
|
|
1803
1815
|
async _flush() {
|
|
1804
1816
|
this.clearFlushTimer();
|
|
1805
1817
|
await this._initPromise;
|
|
1806
|
-
|
|
1818
|
+
let queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1807
1819
|
if (!queue.length) {
|
|
1808
1820
|
return [];
|
|
1809
1821
|
}
|
|
1810
|
-
const
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const fetchOptions = this.captureMode === 'form'
|
|
1829
|
-
? {
|
|
1830
|
-
method: 'POST',
|
|
1831
|
-
mode: 'no-cors',
|
|
1832
|
-
credentials: 'omit',
|
|
1833
|
-
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1834
|
-
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1822
|
+
const sentMessages = [];
|
|
1823
|
+
const originalQueueLength = queue.length;
|
|
1824
|
+
while (queue.length > 0 && sentMessages.length < originalQueueLength) {
|
|
1825
|
+
const batchItems = queue.slice(0, this.maxBatchSize);
|
|
1826
|
+
const batchMessages = batchItems.map((item) => item.message);
|
|
1827
|
+
const persistQueueChange = () => {
|
|
1828
|
+
const refreshedQueue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1829
|
+
const newQueue = refreshedQueue.slice(batchItems.length);
|
|
1830
|
+
this.setPersistedProperty(PostHogPersistedProperty.Queue, newQueue);
|
|
1831
|
+
queue = newQueue;
|
|
1832
|
+
};
|
|
1833
|
+
const data = {
|
|
1834
|
+
api_key: this.apiKey,
|
|
1835
|
+
batch: batchMessages,
|
|
1836
|
+
sent_at: currentISOTime(),
|
|
1837
|
+
};
|
|
1838
|
+
if (this.historicalMigration) {
|
|
1839
|
+
data.historical_migration = true;
|
|
1835
1840
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1841
|
+
const payload = JSON.stringify(data);
|
|
1842
|
+
const url = this.captureMode === 'form'
|
|
1843
|
+
? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
|
|
1844
|
+
: `${this.host}/batch/`;
|
|
1845
|
+
const fetchOptions = this.captureMode === 'form'
|
|
1846
|
+
? {
|
|
1847
|
+
method: 'POST',
|
|
1848
|
+
mode: 'no-cors',
|
|
1849
|
+
credentials: 'omit',
|
|
1850
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1851
|
+
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1852
|
+
}
|
|
1853
|
+
: {
|
|
1854
|
+
method: 'POST',
|
|
1855
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1856
|
+
body: payload,
|
|
1857
|
+
};
|
|
1858
|
+
const retryOptions = {
|
|
1859
|
+
retryCheck: (err) => {
|
|
1860
|
+
// don't automatically retry on 413 errors, we want to reduce the batch size first
|
|
1861
|
+
if (isPostHogFetchContentTooLargeError(err)) {
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
// otherwise, retry on network errors
|
|
1865
|
+
return isPostHogFetchError(err);
|
|
1866
|
+
},
|
|
1840
1867
|
};
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
}
|
|
1844
|
-
catch (err) {
|
|
1845
|
-
// depending on the error type, eg a malformed JSON or broken queue, it'll always return an error
|
|
1846
|
-
// and this will be an endless loop, in this case, if the error isn't a network issue, we always remove the items from the queue
|
|
1847
|
-
if (!(err instanceof PostHogFetchNetworkError)) {
|
|
1848
|
-
persistQueueChange();
|
|
1868
|
+
try {
|
|
1869
|
+
await this.fetchWithRetry(url, fetchOptions, retryOptions);
|
|
1849
1870
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1871
|
+
catch (err) {
|
|
1872
|
+
if (isPostHogFetchContentTooLargeError(err) && batchMessages.length > 1) {
|
|
1873
|
+
// if we get a 413 error, we want to reduce the batch size and try again
|
|
1874
|
+
this.maxBatchSize = Math.max(1, Math.floor(batchMessages.length / 2));
|
|
1875
|
+
this.logMsgIfDebug(() => console.warn(`Received 413 when sending batch of size ${batchMessages.length}, reducing batch size to ${this.maxBatchSize}`));
|
|
1876
|
+
// do not persist the queue change, we want to retry the same batch
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
// depending on the error type, eg a malformed JSON or broken queue, it'll always return an error
|
|
1880
|
+
// and this will be an endless loop, in this case, if the error isn't a network issue, we always remove the items from the queue
|
|
1881
|
+
if (!(err instanceof PostHogFetchNetworkError)) {
|
|
1882
|
+
persistQueueChange();
|
|
1883
|
+
}
|
|
1884
|
+
this._events.emit('error', err);
|
|
1885
|
+
throw err;
|
|
1886
|
+
}
|
|
1887
|
+
persistQueueChange();
|
|
1888
|
+
sentMessages.push(...batchMessages);
|
|
1852
1889
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
return messages;
|
|
1890
|
+
this._events.emit('flush', sentMessages);
|
|
1891
|
+
return sentMessages;
|
|
1856
1892
|
}
|
|
1857
1893
|
async fetchWithRetry(url, options, retryOptions, requestTimeout) {
|
|
1858
1894
|
var _a;
|
|
@@ -1885,7 +1921,7 @@ class PostHogCoreStateless {
|
|
|
1885
1921
|
return res;
|
|
1886
1922
|
}, { ...this._retryOptions, ...retryOptions });
|
|
1887
1923
|
}
|
|
1888
|
-
async
|
|
1924
|
+
async _shutdown(shutdownTimeoutMs = 30000) {
|
|
1889
1925
|
// A little tricky - we want to have a max shutdown time and enforce it, even if that means we have some
|
|
1890
1926
|
// dangling promises. We'll keep track of the timeout and resolve/reject based on that.
|
|
1891
1927
|
await this._initPromise;
|
|
@@ -1926,6 +1962,22 @@ class PostHogCoreStateless {
|
|
|
1926
1962
|
doShutdown(),
|
|
1927
1963
|
]);
|
|
1928
1964
|
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Call shutdown() once before the node process exits, so ensure that all events have been sent and all promises
|
|
1967
|
+
* have resolved. Do not use this function if you intend to keep using this PostHog instance after calling it.
|
|
1968
|
+
* @param shutdownTimeoutMs
|
|
1969
|
+
*/
|
|
1970
|
+
async shutdown(shutdownTimeoutMs = 30000) {
|
|
1971
|
+
if (this.shutdownPromise) {
|
|
1972
|
+
this.logMsgIfDebug(() => console.warn('shutdown() called while already shutting down. shutdown() is meant to be called once before process exit - use flush() for per-request cleanup'));
|
|
1973
|
+
}
|
|
1974
|
+
else {
|
|
1975
|
+
this.shutdownPromise = this._shutdown(shutdownTimeoutMs).finally(() => {
|
|
1976
|
+
this.shutdownPromise = null;
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
return this.shutdownPromise;
|
|
1980
|
+
}
|
|
1929
1981
|
}
|
|
1930
1982
|
|
|
1931
1983
|
class PostHogMemoryStorage {
|
|
@@ -4117,9 +4169,9 @@ class PostHog extends PostHogCoreStateless {
|
|
|
4117
4169
|
async reloadFeatureFlags() {
|
|
4118
4170
|
await this.featureFlagsPoller?.loadFeatureFlags(true);
|
|
4119
4171
|
}
|
|
4120
|
-
async
|
|
4172
|
+
async _shutdown(shutdownTimeoutMs) {
|
|
4121
4173
|
this.featureFlagsPoller?.stopPoller();
|
|
4122
|
-
return super.
|
|
4174
|
+
return super._shutdown(shutdownTimeoutMs);
|
|
4123
4175
|
}
|
|
4124
4176
|
addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties) {
|
|
4125
4177
|
const allPersonProperties = {
|