posthog-js-lite 3.0.0-beta.1 → 3.0.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 +37 -6
- package/lib/index.cjs.js +100 -74
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +22 -5
- package/lib/index.esm.js +100 -74
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +16 -5
- package/lib/posthog-core/src/types.d.ts +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
|
+
# 3.0.0 - 2024-03-18
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
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
|
|
6
|
+
2. `shutdown` 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.
|
|
7
|
+
3. Adds a new `featureFlagsRequestTimeoutMs` timeout parameter for feature flags which defaults to 10 seconds.
|
|
8
|
+
4. Flushes will now try to flush up to `maxBatchSize` (default 100) in one go
|
|
9
|
+
5. Queued events are limited up to `maxQueueSize` (default 1000) and the oldest events are dropped when the limit is reached
|
|
10
|
+
|
|
11
|
+
## Removed
|
|
12
|
+
|
|
13
|
+
1. Removes the `enable` option. You can now specify `defaultOptIn: false` to start the SDK opted out of tracking
|
|
14
|
+
2. `flushAsync` and `shutdownAsync` are removed with `flush` and `shutdown` now being the async methods.
|
|
15
|
+
|
|
16
|
+
## Changed
|
|
17
|
+
|
|
18
|
+
1. `flush` and `shutdown` now being async methods.
|
|
19
|
+
2. Many methods such as `capture` and `identify` no longer return the `this` object instead returning nothing
|
|
20
|
+
|
|
21
|
+
## Fixed
|
|
22
|
+
|
|
23
|
+
1. Fixed an issue where `shutdown` would potentially exit early if a flush was already in progress
|
|
24
|
+
2. Fixes some typos in types
|
|
25
|
+
|
|
26
|
+
# 3.0.0-beta.2 - 2024-03-12
|
|
27
|
+
|
|
28
|
+
1. `flushAsync` and `shutdownAsync` are removed with `flush` and `shutdown` now being the async methods.
|
|
29
|
+
2. Fixed an issue where `shutdownAsync` would potentially exit early if a flush was already in progress
|
|
30
|
+
3. Flushes will now try to flush up to `maxBatchSize` (default 100) in one go
|
|
31
|
+
|
|
1
32
|
# 3.0.0-beta.1 - 2024-03-04
|
|
2
33
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
34
|
+
1. Removes the `enable` option. You can now specify `defaultOptIn: false` to start the SDK opted out of tracking
|
|
35
|
+
2. 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
|
|
36
|
+
3. Many methods such as `capture` and `identify` no longer return the `this` object instead returning nothing
|
|
37
|
+
4. Fixes some typos in types
|
|
38
|
+
5. `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.
|
|
39
|
+
6. Adds a new `featureFlagsRequestTimeoutMs` timeout parameter for feature flags which defaults to 10 seconds.
|
|
9
40
|
|
|
10
41
|
# 2.6.2 - 2024-02-15
|
|
11
42
|
|
package/lib/index.cjs.js
CHANGED
|
@@ -946,6 +946,7 @@ function isPostHogFetchError(err) {
|
|
|
946
946
|
}
|
|
947
947
|
class PostHogCoreStateless {
|
|
948
948
|
constructor(apiKey, options) {
|
|
949
|
+
this.flushPromise = null;
|
|
949
950
|
this.disableGeoip = true;
|
|
950
951
|
this.disabled = false;
|
|
951
952
|
this.defaultOptIn = true;
|
|
@@ -957,6 +958,8 @@ class PostHogCoreStateless {
|
|
|
957
958
|
this.apiKey = apiKey;
|
|
958
959
|
this.host = removeTrailingSlash(options?.host || 'https://app.posthog.com');
|
|
959
960
|
this.flushAt = options?.flushAt ? Math.max(options?.flushAt, 1) : 20;
|
|
961
|
+
this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
|
|
962
|
+
this.maxQueueSize = Math.max(this.flushAt, options?.maxQueueSize ?? 1000);
|
|
960
963
|
this.flushInterval = options?.flushInterval ?? 10000;
|
|
961
964
|
this.captureMode = options?.captureMode || 'form';
|
|
962
965
|
// If enable is explicitly set to false we override the optout
|
|
@@ -1035,9 +1038,12 @@ class PostHogCoreStateless {
|
|
|
1035
1038
|
addPendingPromise(promise) {
|
|
1036
1039
|
const promiseUUID = uuidv7();
|
|
1037
1040
|
this.pendingPromises[promiseUUID] = promise;
|
|
1038
|
-
promise
|
|
1041
|
+
promise
|
|
1042
|
+
.catch(() => { })
|
|
1043
|
+
.finally(() => {
|
|
1039
1044
|
delete this.pendingPromises[promiseUUID];
|
|
1040
1045
|
});
|
|
1046
|
+
return promise;
|
|
1041
1047
|
}
|
|
1042
1048
|
/***
|
|
1043
1049
|
*** TRACKING
|
|
@@ -1102,7 +1108,7 @@ class PostHogCoreStateless {
|
|
|
1102
1108
|
const url = `${this.host}/decide/?v=3`;
|
|
1103
1109
|
const fetchOptions = {
|
|
1104
1110
|
method: 'POST',
|
|
1105
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1111
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1106
1112
|
body: JSON.stringify({
|
|
1107
1113
|
token: this.apiKey,
|
|
1108
1114
|
distinct_id: distinctId,
|
|
@@ -1212,80 +1218,106 @@ class PostHogCoreStateless {
|
|
|
1212
1218
|
delete message.distinctId;
|
|
1213
1219
|
}
|
|
1214
1220
|
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1221
|
+
if (queue.length >= this.maxQueueSize) {
|
|
1222
|
+
queue.shift();
|
|
1223
|
+
console.info('Queue is full, the oldest event is dropped.');
|
|
1224
|
+
}
|
|
1215
1225
|
queue.push({ message });
|
|
1216
1226
|
this.setPersistedProperty(PostHogPersistedProperty.Queue, queue);
|
|
1217
1227
|
this._events.emit(type, message);
|
|
1218
1228
|
// Flush queued events if we meet the flushAt length
|
|
1219
1229
|
if (queue.length >= this.flushAt) {
|
|
1220
|
-
this.
|
|
1230
|
+
this.flushBackground();
|
|
1221
1231
|
}
|
|
1222
1232
|
if (this.flushInterval && !this._flushTimer) {
|
|
1223
|
-
this._flushTimer = safeSetTimeout(() => this.
|
|
1233
|
+
this._flushTimer = safeSetTimeout(() => this.flushBackground(), this.flushInterval);
|
|
1224
1234
|
}
|
|
1225
1235
|
});
|
|
1226
1236
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
this.
|
|
1231
|
-
|
|
1237
|
+
clearFlushTimer() {
|
|
1238
|
+
if (this._flushTimer) {
|
|
1239
|
+
clearTimeout(this._flushTimer);
|
|
1240
|
+
this._flushTimer = undefined;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Helper for flushing the queue in the background
|
|
1245
|
+
* Avoids unnecessary promise errors
|
|
1246
|
+
*/
|
|
1247
|
+
flushBackground() {
|
|
1248
|
+
void this.flush().catch(() => { });
|
|
1249
|
+
}
|
|
1250
|
+
async flush() {
|
|
1251
|
+
if (!this.flushPromise) {
|
|
1252
|
+
this.flushPromise = this._flush().finally(() => {
|
|
1253
|
+
this.flushPromise = null;
|
|
1232
1254
|
});
|
|
1233
|
-
|
|
1255
|
+
this.addPendingPromise(this.flushPromise);
|
|
1256
|
+
}
|
|
1257
|
+
return this.flushPromise;
|
|
1234
1258
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1259
|
+
getCustomHeaders() {
|
|
1260
|
+
// Don't set the user agent if we're not on a browser. The latest spec allows
|
|
1261
|
+
// the User-Agent header (see https://fetch.spec.whatwg.org/#terminology-headers
|
|
1262
|
+
// and https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader),
|
|
1263
|
+
// but browsers such as Chrome and Safari have not caught up.
|
|
1264
|
+
const customUserAgent = this.getCustomUserAgent();
|
|
1265
|
+
const headers = {};
|
|
1266
|
+
if (customUserAgent && customUserAgent !== '') {
|
|
1267
|
+
headers['User-Agent'] = customUserAgent;
|
|
1268
|
+
}
|
|
1269
|
+
return headers;
|
|
1270
|
+
}
|
|
1271
|
+
async _flush() {
|
|
1272
|
+
this.clearFlushTimer();
|
|
1273
|
+
await this._initPromise;
|
|
1274
|
+
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1275
|
+
if (!queue.length) {
|
|
1276
|
+
return [];
|
|
1277
|
+
}
|
|
1278
|
+
const items = queue.slice(0, this.maxBatchSize);
|
|
1279
|
+
const messages = items.map((item) => item.message);
|
|
1280
|
+
const persistQueueChange = () => {
|
|
1281
|
+
const refreshedQueue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1282
|
+
this.setPersistedProperty(PostHogPersistedProperty.Queue, refreshedQueue.slice(items.length));
|
|
1283
|
+
};
|
|
1284
|
+
const data = {
|
|
1285
|
+
api_key: this.apiKey,
|
|
1286
|
+
batch: messages,
|
|
1287
|
+
sent_at: currentISOTime(),
|
|
1288
|
+
};
|
|
1289
|
+
const payload = JSON.stringify(data);
|
|
1290
|
+
const url = this.captureMode === 'form'
|
|
1291
|
+
? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
|
|
1292
|
+
: `${this.host}/batch/`;
|
|
1293
|
+
const fetchOptions = this.captureMode === 'form'
|
|
1294
|
+
? {
|
|
1295
|
+
method: 'POST',
|
|
1296
|
+
mode: 'no-cors',
|
|
1297
|
+
credentials: 'omit',
|
|
1298
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1299
|
+
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1244
1300
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
api_key: this.apiKey,
|
|
1250
|
-
batch: messages,
|
|
1251
|
-
sent_at: currentISOTime(),
|
|
1252
|
-
};
|
|
1253
|
-
const done = (err) => {
|
|
1254
|
-
if (err) {
|
|
1255
|
-
this._events.emit('error', err);
|
|
1256
|
-
}
|
|
1257
|
-
callback?.(err, messages);
|
|
1258
|
-
this._events.emit('flush', messages);
|
|
1301
|
+
: {
|
|
1302
|
+
method: 'POST',
|
|
1303
|
+
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1304
|
+
body: payload,
|
|
1259
1305
|
};
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1276
|
-
}
|
|
1277
|
-
: {
|
|
1278
|
-
method: 'POST',
|
|
1279
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1280
|
-
body: payload,
|
|
1281
|
-
};
|
|
1282
|
-
const requestPromise = this.fetchWithRetry(url, fetchOptions);
|
|
1283
|
-
this.addPendingPromise(requestPromise
|
|
1284
|
-
.then(() => done())
|
|
1285
|
-
.catch((err) => {
|
|
1286
|
-
done(err);
|
|
1287
|
-
}));
|
|
1288
|
-
});
|
|
1306
|
+
try {
|
|
1307
|
+
await this.fetchWithRetry(url, fetchOptions);
|
|
1308
|
+
}
|
|
1309
|
+
catch (err) {
|
|
1310
|
+
// depending on the error type, eg a malformed JSON or broken queue, it'll always return an error
|
|
1311
|
+
// 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
|
|
1312
|
+
if (!(err instanceof PostHogFetchNetworkError)) {
|
|
1313
|
+
persistQueueChange();
|
|
1314
|
+
}
|
|
1315
|
+
this._events.emit('error', err);
|
|
1316
|
+
throw err;
|
|
1317
|
+
}
|
|
1318
|
+
persistQueueChange();
|
|
1319
|
+
this._events.emit('flush', messages);
|
|
1320
|
+
return messages;
|
|
1289
1321
|
}
|
|
1290
1322
|
async fetchWithRetry(url, options, retryOptions, requestTimeout) {
|
|
1291
1323
|
var _a;
|
|
@@ -1316,15 +1348,12 @@ class PostHogCoreStateless {
|
|
|
1316
1348
|
return res;
|
|
1317
1349
|
}, { ...this._retryOptions, ...retryOptions });
|
|
1318
1350
|
}
|
|
1319
|
-
async
|
|
1351
|
+
async shutdown(shutdownTimeoutMs = 30000) {
|
|
1320
1352
|
await this._initPromise;
|
|
1321
|
-
|
|
1353
|
+
this.clearFlushTimer();
|
|
1322
1354
|
try {
|
|
1323
|
-
await Promise.all(Object.values(this.pendingPromises)
|
|
1324
|
-
|
|
1325
|
-
})));
|
|
1326
|
-
const timeout = shutdownTimeoutMs ?? 30000;
|
|
1327
|
-
const startTimeWithDelay = Date.now() + timeout;
|
|
1355
|
+
await Promise.all(Object.values(this.pendingPromises));
|
|
1356
|
+
const startTimeWithDelay = Date.now() + shutdownTimeoutMs;
|
|
1328
1357
|
while (true) {
|
|
1329
1358
|
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1330
1359
|
if (queue.length === 0) {
|
|
@@ -1333,7 +1362,7 @@ class PostHogCoreStateless {
|
|
|
1333
1362
|
// flush again to make sure we send all events, some of which might've been added
|
|
1334
1363
|
// while we were waiting for the pending promises to resolve
|
|
1335
1364
|
// For example, see sendFeatureFlags in posthog-node/src/posthog-node.ts::capture
|
|
1336
|
-
await this.
|
|
1365
|
+
await this.flush();
|
|
1337
1366
|
// If we've been waiting for more than the shutdownTimeoutMs, stop it
|
|
1338
1367
|
const now = Date.now();
|
|
1339
1368
|
if (startTimeWithDelay < now) {
|
|
@@ -1348,9 +1377,6 @@ class PostHogCoreStateless {
|
|
|
1348
1377
|
console.error('Error while shutting down PostHog', e);
|
|
1349
1378
|
}
|
|
1350
1379
|
}
|
|
1351
|
-
shutdown(shutdownTimeoutMs) {
|
|
1352
|
-
void this.shutdownAsync(shutdownTimeoutMs);
|
|
1353
|
-
}
|
|
1354
1380
|
}
|
|
1355
1381
|
class PostHogCore extends PostHogCoreStateless {
|
|
1356
1382
|
constructor(apiKey, options) {
|
|
@@ -1816,7 +1842,7 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
1816
1842
|
}
|
|
1817
1843
|
}
|
|
1818
1844
|
|
|
1819
|
-
var version = "3.0.0
|
|
1845
|
+
var version = "3.0.0";
|
|
1820
1846
|
|
|
1821
1847
|
function getContext(window) {
|
|
1822
1848
|
let context = {};
|