posthog-js-lite 3.0.0-beta.1 → 3.0.0-beta.2
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 +6 -0
- package/lib/index.cjs.js +87 -73
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +14 -5
- package/lib/index.esm.js +87 -73
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +12 -5
- package/lib/posthog-core/src/types.d.ts +2 -0
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ declare type PostHogCoreOptions = {
|
|
|
5
5
|
flushAt?: number;
|
|
6
6
|
/** The interval in milliseconds between periodic flushes */
|
|
7
7
|
flushInterval?: number;
|
|
8
|
+
/** The maximum number of queued messages to be flushed as part of a single batch (must be higher than `flushAt`) */
|
|
9
|
+
maxBatchSize?: number;
|
|
8
10
|
/** If set to true the SDK is essentially disabled (useful for local environments where you don't want to track anything) */
|
|
9
11
|
disabled?: boolean;
|
|
10
12
|
/** If set to false the SDK will not track until the `optIn` function is called. */
|
|
@@ -127,7 +129,9 @@ declare abstract class PostHogCoreStateless {
|
|
|
127
129
|
private apiKey;
|
|
128
130
|
host: string;
|
|
129
131
|
private flushAt;
|
|
132
|
+
private maxBatchSize;
|
|
130
133
|
private flushInterval;
|
|
134
|
+
private flushPromise;
|
|
131
135
|
private requestTimeout;
|
|
132
136
|
private featureFlagsRequestTimeoutMs;
|
|
133
137
|
private captureMode;
|
|
@@ -157,7 +161,7 @@ declare abstract class PostHogCoreStateless {
|
|
|
157
161
|
debug(enabled?: boolean): void;
|
|
158
162
|
get isDebug(): boolean;
|
|
159
163
|
private buildPayload;
|
|
160
|
-
protected addPendingPromise(promise: Promise<
|
|
164
|
+
protected addPendingPromise<T>(promise: Promise<T>): Promise<T>;
|
|
161
165
|
/***
|
|
162
166
|
*** TRACKING
|
|
163
167
|
***/
|
|
@@ -189,11 +193,16 @@ declare abstract class PostHogCoreStateless {
|
|
|
189
193
|
*** QUEUEING AND FLUSHING
|
|
190
194
|
***/
|
|
191
195
|
protected enqueue(type: string, _message: any, options?: PostHogCaptureOptions): void;
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
private clearFlushTimer;
|
|
197
|
+
/**
|
|
198
|
+
* Helper for flushing the queue in the background
|
|
199
|
+
* Avoids unnecessary promise errors
|
|
200
|
+
*/
|
|
201
|
+
private flushBackground;
|
|
202
|
+
flush(): Promise<any[]>;
|
|
203
|
+
private _flush;
|
|
194
204
|
private fetchWithRetry;
|
|
195
|
-
|
|
196
|
-
shutdown(shutdownTimeoutMs?: number): void;
|
|
205
|
+
shutdown(shutdownTimeoutMs?: number): Promise<void>;
|
|
197
206
|
}
|
|
198
207
|
declare abstract class PostHogCore extends PostHogCoreStateless {
|
|
199
208
|
private sendFeatureFlagEvent;
|
package/lib/index.esm.js
CHANGED
|
@@ -942,6 +942,7 @@ function isPostHogFetchError(err) {
|
|
|
942
942
|
}
|
|
943
943
|
class PostHogCoreStateless {
|
|
944
944
|
constructor(apiKey, options) {
|
|
945
|
+
this.flushPromise = null;
|
|
945
946
|
this.disableGeoip = true;
|
|
946
947
|
this.disabled = false;
|
|
947
948
|
this.defaultOptIn = true;
|
|
@@ -953,6 +954,7 @@ class PostHogCoreStateless {
|
|
|
953
954
|
this.apiKey = apiKey;
|
|
954
955
|
this.host = removeTrailingSlash(options?.host || 'https://app.posthog.com');
|
|
955
956
|
this.flushAt = options?.flushAt ? Math.max(options?.flushAt, 1) : 20;
|
|
957
|
+
this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
|
|
956
958
|
this.flushInterval = options?.flushInterval ?? 10000;
|
|
957
959
|
this.captureMode = options?.captureMode || 'form';
|
|
958
960
|
// If enable is explicitly set to false we override the optout
|
|
@@ -1031,9 +1033,12 @@ class PostHogCoreStateless {
|
|
|
1031
1033
|
addPendingPromise(promise) {
|
|
1032
1034
|
const promiseUUID = uuidv7();
|
|
1033
1035
|
this.pendingPromises[promiseUUID] = promise;
|
|
1034
|
-
promise
|
|
1036
|
+
promise
|
|
1037
|
+
.catch(() => { })
|
|
1038
|
+
.finally(() => {
|
|
1035
1039
|
delete this.pendingPromises[promiseUUID];
|
|
1036
1040
|
});
|
|
1041
|
+
return promise;
|
|
1037
1042
|
}
|
|
1038
1043
|
/***
|
|
1039
1044
|
*** TRACKING
|
|
@@ -1213,75 +1218,90 @@ class PostHogCoreStateless {
|
|
|
1213
1218
|
this._events.emit(type, message);
|
|
1214
1219
|
// Flush queued events if we meet the flushAt length
|
|
1215
1220
|
if (queue.length >= this.flushAt) {
|
|
1216
|
-
this.
|
|
1221
|
+
this.flushBackground();
|
|
1217
1222
|
}
|
|
1218
1223
|
if (this.flushInterval && !this._flushTimer) {
|
|
1219
|
-
this._flushTimer = safeSetTimeout(() => this.
|
|
1224
|
+
this._flushTimer = safeSetTimeout(() => this.flushBackground(), this.flushInterval);
|
|
1220
1225
|
}
|
|
1221
1226
|
});
|
|
1222
1227
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
this.
|
|
1227
|
-
|
|
1228
|
+
clearFlushTimer() {
|
|
1229
|
+
if (this._flushTimer) {
|
|
1230
|
+
clearTimeout(this._flushTimer);
|
|
1231
|
+
this._flushTimer = undefined;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Helper for flushing the queue in the background
|
|
1236
|
+
* Avoids unnecessary promise errors
|
|
1237
|
+
*/
|
|
1238
|
+
flushBackground() {
|
|
1239
|
+
void this.flush().catch(() => { });
|
|
1240
|
+
}
|
|
1241
|
+
async flush() {
|
|
1242
|
+
if (!this.flushPromise) {
|
|
1243
|
+
this.flushPromise = this._flush().finally(() => {
|
|
1244
|
+
this.flushPromise = null;
|
|
1228
1245
|
});
|
|
1229
|
-
|
|
1246
|
+
this.addPendingPromise(this.flushPromise);
|
|
1247
|
+
}
|
|
1248
|
+
return this.flushPromise;
|
|
1230
1249
|
}
|
|
1231
|
-
|
|
1232
|
-
this.
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1250
|
+
async _flush() {
|
|
1251
|
+
this.clearFlushTimer();
|
|
1252
|
+
await this._initPromise;
|
|
1253
|
+
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1254
|
+
if (!queue.length) {
|
|
1255
|
+
return [];
|
|
1256
|
+
}
|
|
1257
|
+
const items = queue.slice(0, this.maxBatchSize);
|
|
1258
|
+
const messages = items.map((item) => item.message);
|
|
1259
|
+
const persistQueueChange = () => {
|
|
1260
|
+
const refreshedQueue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1261
|
+
this.setPersistedProperty(PostHogPersistedProperty.Queue, refreshedQueue.slice(items.length));
|
|
1262
|
+
};
|
|
1263
|
+
const data = {
|
|
1264
|
+
api_key: this.apiKey,
|
|
1265
|
+
batch: messages,
|
|
1266
|
+
sent_at: currentISOTime(),
|
|
1267
|
+
};
|
|
1268
|
+
// Don't set the user agent if we're not on a browser. The latest spec allows
|
|
1269
|
+
// the User-Agent header (see https://fetch.spec.whatwg.org/#terminology-headers
|
|
1270
|
+
// and https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader),
|
|
1271
|
+
// but browsers such as Chrome and Safari have not caught up.
|
|
1272
|
+
this.getCustomUserAgent();
|
|
1273
|
+
const payload = JSON.stringify(data);
|
|
1274
|
+
const url = this.captureMode === 'form'
|
|
1275
|
+
? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
|
|
1276
|
+
: `${this.host}/batch/`;
|
|
1277
|
+
const fetchOptions = this.captureMode === 'form'
|
|
1278
|
+
? {
|
|
1279
|
+
method: 'POST',
|
|
1280
|
+
mode: 'no-cors',
|
|
1281
|
+
credentials: 'omit',
|
|
1282
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1283
|
+
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1240
1284
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
api_key: this.apiKey,
|
|
1246
|
-
batch: messages,
|
|
1247
|
-
sent_at: currentISOTime(),
|
|
1285
|
+
: {
|
|
1286
|
+
method: 'POST',
|
|
1287
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1288
|
+
body: payload,
|
|
1248
1289
|
};
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
: `${this.host}/batch/`;
|
|
1265
|
-
const fetchOptions = this.captureMode === 'form'
|
|
1266
|
-
? {
|
|
1267
|
-
method: 'POST',
|
|
1268
|
-
mode: 'no-cors',
|
|
1269
|
-
credentials: 'omit',
|
|
1270
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1271
|
-
body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
|
|
1272
|
-
}
|
|
1273
|
-
: {
|
|
1274
|
-
method: 'POST',
|
|
1275
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1276
|
-
body: payload,
|
|
1277
|
-
};
|
|
1278
|
-
const requestPromise = this.fetchWithRetry(url, fetchOptions);
|
|
1279
|
-
this.addPendingPromise(requestPromise
|
|
1280
|
-
.then(() => done())
|
|
1281
|
-
.catch((err) => {
|
|
1282
|
-
done(err);
|
|
1283
|
-
}));
|
|
1284
|
-
});
|
|
1290
|
+
try {
|
|
1291
|
+
await this.fetchWithRetry(url, fetchOptions);
|
|
1292
|
+
}
|
|
1293
|
+
catch (err) {
|
|
1294
|
+
// depending on the error type, eg a malformed JSON or broken queue, it'll always return an error
|
|
1295
|
+
// 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
|
|
1296
|
+
if (!(err instanceof PostHogFetchNetworkError)) {
|
|
1297
|
+
persistQueueChange();
|
|
1298
|
+
}
|
|
1299
|
+
this._events.emit('error', err);
|
|
1300
|
+
throw err;
|
|
1301
|
+
}
|
|
1302
|
+
persistQueueChange();
|
|
1303
|
+
this._events.emit('flush', messages);
|
|
1304
|
+
return messages;
|
|
1285
1305
|
}
|
|
1286
1306
|
async fetchWithRetry(url, options, retryOptions, requestTimeout) {
|
|
1287
1307
|
var _a;
|
|
@@ -1312,15 +1332,12 @@ class PostHogCoreStateless {
|
|
|
1312
1332
|
return res;
|
|
1313
1333
|
}, { ...this._retryOptions, ...retryOptions });
|
|
1314
1334
|
}
|
|
1315
|
-
async
|
|
1335
|
+
async shutdown(shutdownTimeoutMs = 30000) {
|
|
1316
1336
|
await this._initPromise;
|
|
1317
|
-
|
|
1337
|
+
this.clearFlushTimer();
|
|
1318
1338
|
try {
|
|
1319
|
-
await Promise.all(Object.values(this.pendingPromises)
|
|
1320
|
-
|
|
1321
|
-
})));
|
|
1322
|
-
const timeout = shutdownTimeoutMs ?? 30000;
|
|
1323
|
-
const startTimeWithDelay = Date.now() + timeout;
|
|
1339
|
+
await Promise.all(Object.values(this.pendingPromises));
|
|
1340
|
+
const startTimeWithDelay = Date.now() + shutdownTimeoutMs;
|
|
1324
1341
|
while (true) {
|
|
1325
1342
|
const queue = this.getPersistedProperty(PostHogPersistedProperty.Queue) || [];
|
|
1326
1343
|
if (queue.length === 0) {
|
|
@@ -1329,7 +1346,7 @@ class PostHogCoreStateless {
|
|
|
1329
1346
|
// flush again to make sure we send all events, some of which might've been added
|
|
1330
1347
|
// while we were waiting for the pending promises to resolve
|
|
1331
1348
|
// For example, see sendFeatureFlags in posthog-node/src/posthog-node.ts::capture
|
|
1332
|
-
await this.
|
|
1349
|
+
await this.flush();
|
|
1333
1350
|
// If we've been waiting for more than the shutdownTimeoutMs, stop it
|
|
1334
1351
|
const now = Date.now();
|
|
1335
1352
|
if (startTimeWithDelay < now) {
|
|
@@ -1344,9 +1361,6 @@ class PostHogCoreStateless {
|
|
|
1344
1361
|
console.error('Error while shutting down PostHog', e);
|
|
1345
1362
|
}
|
|
1346
1363
|
}
|
|
1347
|
-
shutdown(shutdownTimeoutMs) {
|
|
1348
|
-
void this.shutdownAsync(shutdownTimeoutMs);
|
|
1349
|
-
}
|
|
1350
1364
|
}
|
|
1351
1365
|
class PostHogCore extends PostHogCoreStateless {
|
|
1352
1366
|
constructor(apiKey, options) {
|
|
@@ -1812,7 +1826,7 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
1812
1826
|
}
|
|
1813
1827
|
}
|
|
1814
1828
|
|
|
1815
|
-
var version = "3.0.0-beta.
|
|
1829
|
+
var version = "3.0.0-beta.2";
|
|
1816
1830
|
|
|
1817
1831
|
function getContext(window) {
|
|
1818
1832
|
let context = {};
|