posthog-node 4.0.0-beta.1 → 4.0.0-beta.3
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 +14 -4
- package/lib/index.cjs.js +104 -85
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +18 -7
- package/lib/index.esm.js +104 -85
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +15 -5
- package/lib/posthog-core/src/types.d.ts +2 -0
- package/lib/posthog-node/src/feature-flags.d.ts +7 -1
- package/lib/posthog-node/src/posthog-node.d.ts +1 -2
- package/package.json +1 -1
- package/src/feature-flags.ts +5 -2
- package/src/posthog-node.ts +4 -7
- package/test/extensions/sentry-integration.spec.ts +4 -3
- package/test/feature-flags.spec.ts +2 -2
- package/test/posthog-node.spec.ts +84 -65
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
# 4.0.0-beta.3 - 2024-03-13
|
|
2
|
+
|
|
3
|
+
1. Sets `User-Agent` headers with SDK name and version for RN
|
|
4
|
+
|
|
5
|
+
# 4.0.0-beta.2 - 2024-03-12
|
|
6
|
+
|
|
7
|
+
1. `flushAsync` and `shutdownAsync` are removed with `flush` and `shutdown` now being the async methods.
|
|
8
|
+
2. Fixed an issue where `shutdown` would potentially exit early if a flush was already in progress
|
|
9
|
+
3. Flushes will now try to flush up to `maxBatchSize` (default 100) in one go
|
|
10
|
+
|
|
1
11
|
# 4.0.0-beta.1 - 2024-03-04
|
|
2
12
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
1. Adds a `disabled` option and the ability to change it later via `posthog.disabled = true`. Useful for disabling PostHog tracking for example in a testing environment without having complex conditional checking
|
|
14
|
+
2. Fixes some typos in types
|
|
15
|
+
3. `shutdown` and `shutdownAsync` takes a `shutdownTimeoutMs` param with a default of 30000 (30s). This is the time to wait for flushing events before shutting down the client. If the timeout is reached, the client will be shut down regardless of pending events.
|
|
16
|
+
4. Adds a new `featureFlagsRequestTimeoutMs` timeout parameter for feature flags which defaults to 3 seconds, updated from the default 10s for all other API calls.
|
|
7
17
|
|
|
8
18
|
# 3.6.3 - 2024-02-15
|
|
9
19
|
|
package/lib/index.cjs.js
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var rusha = require('rusha');
|
|
6
6
|
|
|
7
|
-
var version = "4.0.0-beta.
|
|
7
|
+
var version = "4.0.0-beta.3";
|
|
8
8
|
|
|
9
9
|
var PostHogPersistedProperty;
|
|
10
10
|
(function (PostHogPersistedProperty) {
|
|
@@ -950,6 +950,7 @@ function isPostHogFetchError(err) {
|
|
|
950
950
|
}
|
|
951
951
|
class PostHogCoreStateless {
|
|
952
952
|
constructor(apiKey, options) {
|
|
953
|
+
this.flushPromise = null;
|
|
953
954
|
this.disableGeoip = true;
|
|
954
955
|
this.disabled = false;
|
|
955
956
|
this.defaultOptIn = true;
|
|
@@ -961,6 +962,7 @@ class PostHogCoreStateless {
|
|
|
961
962
|
this.apiKey = apiKey;
|
|
962
963
|
this.host = removeTrailingSlash(options?.host || 'https://app.posthog.com');
|
|
963
964
|
this.flushAt = options?.flushAt ? Math.max(options?.flushAt, 1) : 20;
|
|
965
|
+
this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
|
|
964
966
|
this.flushInterval = options?.flushInterval ?? 10000;
|
|
965
967
|
this.captureMode = options?.captureMode || 'form';
|
|
966
968
|
// If enable is explicitly set to false we override the optout
|
|
@@ -1039,9 +1041,12 @@ class PostHogCoreStateless {
|
|
|
1039
1041
|
addPendingPromise(promise) {
|
|
1040
1042
|
const promiseUUID = uuidv7();
|
|
1041
1043
|
this.pendingPromises[promiseUUID] = promise;
|
|
1042
|
-
promise
|
|
1044
|
+
promise
|
|
1045
|
+
.catch(() => { })
|
|
1046
|
+
.finally(() => {
|
|
1043
1047
|
delete this.pendingPromises[promiseUUID];
|
|
1044
1048
|
});
|
|
1049
|
+
return promise;
|
|
1045
1050
|
}
|
|
1046
1051
|
/***
|
|
1047
1052
|
*** TRACKING
|
|
@@ -1106,7 +1111,7 @@ class PostHogCoreStateless {
|
|
|
1106
1111
|
const url = `${this.host}/decide/?v=3`;
|
|
1107
1112
|
const fetchOptions = {
|
|
1108
1113
|
method: 'POST',
|
|
1109
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1114
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1110
1115
|
body: JSON.stringify({
|
|
1111
1116
|
token: this.apiKey,
|
|
1112
1117
|
distinct_id: distinctId,
|
|
@@ -1221,75 +1226,97 @@ class PostHogCoreStateless {
|
|
|
1221
1226
|
this._events.emit(type, message);
|
|
1222
1227
|
// Flush queued events if we meet the flushAt length
|
|
1223
1228
|
if (queue.length >= this.flushAt) {
|
|
1224
|
-
this.
|
|
1229
|
+
this.flushBackground();
|
|
1225
1230
|
}
|
|
1226
1231
|
if (this.flushInterval && !this._flushTimer) {
|
|
1227
|
-
this._flushTimer = safeSetTimeout(() => this.
|
|
1232
|
+
this._flushTimer = safeSetTimeout(() => this.flushBackground(), this.flushInterval);
|
|
1228
1233
|
}
|
|
1229
1234
|
});
|
|
1230
1235
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
this.
|
|
1235
|
-
|
|
1236
|
+
clearFlushTimer() {
|
|
1237
|
+
if (this._flushTimer) {
|
|
1238
|
+
clearTimeout(this._flushTimer);
|
|
1239
|
+
this._flushTimer = undefined;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Helper for flushing the queue in the background
|
|
1244
|
+
* Avoids unnecessary promise errors
|
|
1245
|
+
*/
|
|
1246
|
+
flushBackground() {
|
|
1247
|
+
void this.flush().catch(() => { });
|
|
1248
|
+
}
|
|
1249
|
+
async flush() {
|
|
1250
|
+
if (!this.flushPromise) {
|
|
1251
|
+
this.flushPromise = this._flush().finally(() => {
|
|
1252
|
+
this.flushPromise = null;
|
|
1236
1253
|
});
|
|
1237
|
-
|
|
1254
|
+
this.addPendingPromise(this.flushPromise);
|
|
1255
|
+
}
|
|
1256
|
+
return this.flushPromise;
|
|
1238
1257
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1258
|
+
getCustomHeaders() {
|
|
1259
|
+
// Don't set the user agent if we're not on a browser. The latest spec allows
|
|
1260
|
+
// the User-Agent header (see https://fetch.spec.whatwg.org/#terminology-headers
|
|
1261
|
+
// and https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader),
|
|
1262
|
+
// but browsers such as Chrome and Safari have not caught up.
|
|
1263
|
+
const customUserAgent = this.getCustomUserAgent();
|
|
1264
|
+
const headers = {};
|
|
1265
|
+
if (customUserAgent && customUserAgent !== '') {
|
|
1266
|
+
headers['User-Agent'] = customUserAgent;
|
|
1267
|
+
}
|
|
1268
|
+
return headers;
|
|
1269
|
+
}
|
|
1270
|
+
async _flush() {
|
|
1271
|
+
this.clearFlushTimer();
|
|
1272
|
+
await this._initPromise;
|
|
1273
|
+
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1274
|
+
if (!queue.length) {
|
|
1275
|
+
return [];
|
|
1276
|
+
}
|
|
1277
|
+
const items = queue.slice(0, this.maxBatchSize);
|
|
1278
|
+
const messages = items.map((item) => item.message);
|
|
1279
|
+
const persistQueueChange = () => {
|
|
1280
|
+
const refreshedQueue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1281
|
+
this.setPersistedProperty(PostHogPersistedProperty.Queue, refreshedQueue.slice(items.length));
|
|
1282
|
+
};
|
|
1283
|
+
const data = {
|
|
1284
|
+
api_key: this.apiKey,
|
|
1285
|
+
batch: messages,
|
|
1286
|
+
sent_at: currentISOTime(),
|
|
1287
|
+
};
|
|
1288
|
+
const payload = JSON.stringify(data);
|
|
1289
|
+
const url = this.captureMode === 'form'
|
|
1290
|
+
? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
|
|
1291
|
+
: `${this.host}/batch/`;
|
|
1292
|
+
const fetchOptions = this.captureMode === 'form'
|
|
1293
|
+
? {
|
|
1294
|
+
method: 'POST',
|
|
1295
|
+
mode: 'no-cors',
|
|
1296
|
+
credentials: 'omit',
|
|
1297
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1298
|
+
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1248
1299
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
api_key: this.apiKey,
|
|
1254
|
-
batch: messages,
|
|
1255
|
-
sent_at: currentISOTime(),
|
|
1256
|
-
};
|
|
1257
|
-
const done = (err) => {
|
|
1258
|
-
if (err) {
|
|
1259
|
-
this._events.emit('error', err);
|
|
1260
|
-
}
|
|
1261
|
-
callback?.(err, messages);
|
|
1262
|
-
this._events.emit('flush', messages);
|
|
1300
|
+
: {
|
|
1301
|
+
method: 'POST',
|
|
1302
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1303
|
+
body: payload,
|
|
1263
1304
|
};
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1280
|
-
}
|
|
1281
|
-
: {
|
|
1282
|
-
method: 'POST',
|
|
1283
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1284
|
-
body: payload,
|
|
1285
|
-
};
|
|
1286
|
-
const requestPromise = this.fetchWithRetry(url, fetchOptions);
|
|
1287
|
-
this.addPendingPromise(requestPromise
|
|
1288
|
-
.then(() => done())
|
|
1289
|
-
.catch((err) => {
|
|
1290
|
-
done(err);
|
|
1291
|
-
}));
|
|
1292
|
-
});
|
|
1305
|
+
try {
|
|
1306
|
+
await this.fetchWithRetry(url, fetchOptions);
|
|
1307
|
+
}
|
|
1308
|
+
catch (err) {
|
|
1309
|
+
// depending on the error type, eg a malformed JSON or broken queue, it'll always return an error
|
|
1310
|
+
// 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
|
|
1311
|
+
if (!(err instanceof PostHogFetchNetworkError)) {
|
|
1312
|
+
persistQueueChange();
|
|
1313
|
+
}
|
|
1314
|
+
this._events.emit('error', err);
|
|
1315
|
+
throw err;
|
|
1316
|
+
}
|
|
1317
|
+
persistQueueChange();
|
|
1318
|
+
this._events.emit('flush', messages);
|
|
1319
|
+
return messages;
|
|
1293
1320
|
}
|
|
1294
1321
|
async fetchWithRetry(url, options, retryOptions, requestTimeout) {
|
|
1295
1322
|
var _a;
|
|
@@ -1320,15 +1347,12 @@ class PostHogCoreStateless {
|
|
|
1320
1347
|
return res;
|
|
1321
1348
|
}, { ...this._retryOptions, ...retryOptions });
|
|
1322
1349
|
}
|
|
1323
|
-
async
|
|
1350
|
+
async shutdown(shutdownTimeoutMs = 30000) {
|
|
1324
1351
|
await this._initPromise;
|
|
1325
|
-
|
|
1352
|
+
this.clearFlushTimer();
|
|
1326
1353
|
try {
|
|
1327
|
-
await Promise.all(Object.values(this.pendingPromises)
|
|
1328
|
-
|
|
1329
|
-
})));
|
|
1330
|
-
const timeout = shutdownTimeoutMs ?? 30000;
|
|
1331
|
-
const startTimeWithDelay = Date.now() + timeout;
|
|
1354
|
+
await Promise.all(Object.values(this.pendingPromises));
|
|
1355
|
+
const startTimeWithDelay = Date.now() + shutdownTimeoutMs;
|
|
1332
1356
|
while (true) {
|
|
1333
1357
|
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1334
1358
|
if (queue.length === 0) {
|
|
@@ -1337,7 +1361,7 @@ class PostHogCoreStateless {
|
|
|
1337
1361
|
// flush again to make sure we send all events, some of which might've been added
|
|
1338
1362
|
// while we were waiting for the pending promises to resolve
|
|
1339
1363
|
// For example, see sendFeatureFlags in posthog-node/src/posthog-node.ts::capture
|
|
1340
|
-
await this.
|
|
1364
|
+
await this.flush();
|
|
1341
1365
|
// If we've been waiting for more than the shutdownTimeoutMs, stop it
|
|
1342
1366
|
const now = Date.now();
|
|
1343
1367
|
if (startTimeWithDelay < now) {
|
|
@@ -1352,9 +1376,6 @@ class PostHogCoreStateless {
|
|
|
1352
1376
|
console.error('Error while shutting down PostHog', e);
|
|
1353
1377
|
}
|
|
1354
1378
|
}
|
|
1355
|
-
shutdown(shutdownTimeoutMs) {
|
|
1356
|
-
void this.shutdownAsync(shutdownTimeoutMs);
|
|
1357
|
-
}
|
|
1358
1379
|
}
|
|
1359
1380
|
|
|
1360
1381
|
class PostHogMemoryStorage {
|
|
@@ -1439,6 +1460,7 @@ class FeatureFlagsPoller {
|
|
|
1439
1460
|
projectApiKey,
|
|
1440
1461
|
timeout,
|
|
1441
1462
|
host,
|
|
1463
|
+
customHeaders,
|
|
1442
1464
|
...options
|
|
1443
1465
|
}) {
|
|
1444
1466
|
this.debugMode = false;
|
|
@@ -1456,6 +1478,7 @@ class FeatureFlagsPoller {
|
|
|
1456
1478
|
|
|
1457
1479
|
this.fetch = options.fetch || fetch$1;
|
|
1458
1480
|
this.onError = options.onError;
|
|
1481
|
+
this.customHeaders = customHeaders;
|
|
1459
1482
|
void this.loadFeatureFlags();
|
|
1460
1483
|
}
|
|
1461
1484
|
|
|
@@ -1758,10 +1781,9 @@ class FeatureFlagsPoller {
|
|
|
1758
1781
|
const url = `${this.host}/api/feature_flag/local_evaluation?token=${this.projectApiKey}&send_cohorts`;
|
|
1759
1782
|
const options = {
|
|
1760
1783
|
method: 'GET',
|
|
1761
|
-
headers: {
|
|
1784
|
+
headers: { ...this.customHeaders,
|
|
1762
1785
|
'Content-Type': 'application/json',
|
|
1763
|
-
Authorization: `Bearer ${this.personalApiKey}
|
|
1764
|
-
'user-agent': `posthog-node/${version}`
|
|
1786
|
+
Authorization: `Bearer ${this.personalApiKey}`
|
|
1765
1787
|
}
|
|
1766
1788
|
};
|
|
1767
1789
|
let abortTimeout = null;
|
|
@@ -2103,7 +2125,8 @@ class PostHog extends PostHogCoreStateless {
|
|
|
2103
2125
|
fetch: options.fetch,
|
|
2104
2126
|
onError: err => {
|
|
2105
2127
|
this._events.emit('error', err);
|
|
2106
|
-
}
|
|
2128
|
+
},
|
|
2129
|
+
customHeaders: this.getCustomHeaders()
|
|
2107
2130
|
});
|
|
2108
2131
|
}
|
|
2109
2132
|
|
|
@@ -2132,7 +2155,7 @@ class PostHog extends PostHogCoreStateless {
|
|
|
2132
2155
|
}
|
|
2133
2156
|
|
|
2134
2157
|
getCustomUserAgent() {
|
|
2135
|
-
return
|
|
2158
|
+
return `${this.getLibraryId()}/${this.getLibraryVersion()}`;
|
|
2136
2159
|
}
|
|
2137
2160
|
|
|
2138
2161
|
enable() {
|
|
@@ -2433,13 +2456,9 @@ class PostHog extends PostHogCoreStateless {
|
|
|
2433
2456
|
await this.featureFlagsPoller?.loadFeatureFlags(true);
|
|
2434
2457
|
}
|
|
2435
2458
|
|
|
2436
|
-
shutdown(shutdownTimeoutMs) {
|
|
2437
|
-
void this.shutdownAsync(shutdownTimeoutMs);
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
|
-
async shutdownAsync(shutdownTimeoutMs) {
|
|
2459
|
+
async shutdown(shutdownTimeoutMs) {
|
|
2441
2460
|
this.featureFlagsPoller?.stopPoller();
|
|
2442
|
-
return super.
|
|
2461
|
+
return super.shutdown(shutdownTimeoutMs);
|
|
2443
2462
|
}
|
|
2444
2463
|
|
|
2445
2464
|
addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties) {
|