posthog-node
Version:
PostHog Node.js integration
1,261 lines (1,253 loc) • 148 kB
JavaScript
import { posix, dirname, sep } from 'path';
var version = "4.11.7";
var PostHogPersistedProperty;
(function (PostHogPersistedProperty) {
PostHogPersistedProperty["AnonymousId"] = "anonymous_id";
PostHogPersistedProperty["DistinctId"] = "distinct_id";
PostHogPersistedProperty["Props"] = "props";
PostHogPersistedProperty["FeatureFlagDetails"] = "feature_flag_details";
PostHogPersistedProperty["FeatureFlags"] = "feature_flags";
PostHogPersistedProperty["FeatureFlagPayloads"] = "feature_flag_payloads";
PostHogPersistedProperty["BootstrapFeatureFlagDetails"] = "bootstrap_feature_flag_details";
PostHogPersistedProperty["BootstrapFeatureFlags"] = "bootstrap_feature_flags";
PostHogPersistedProperty["BootstrapFeatureFlagPayloads"] = "bootstrap_feature_flag_payloads";
PostHogPersistedProperty["OverrideFeatureFlags"] = "override_feature_flags";
PostHogPersistedProperty["Queue"] = "queue";
PostHogPersistedProperty["OptedOut"] = "opted_out";
PostHogPersistedProperty["SessionId"] = "session_id";
PostHogPersistedProperty["SessionLastTimestamp"] = "session_timestamp";
PostHogPersistedProperty["PersonProperties"] = "person_properties";
PostHogPersistedProperty["GroupProperties"] = "group_properties";
PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
PostHogPersistedProperty["SessionReplay"] = "session_replay";
PostHogPersistedProperty["DecideEndpointWasHit"] = "decide_endpoint_was_hit";
PostHogPersistedProperty["SurveyLastSeenDate"] = "survey_last_seen_date";
PostHogPersistedProperty["SurveysSeen"] = "surveys_seen";
PostHogPersistedProperty["Surveys"] = "surveys";
PostHogPersistedProperty["RemoteConfig"] = "remote_config";
})(PostHogPersistedProperty || (PostHogPersistedProperty = {}));
var SurveyPosition;
(function (SurveyPosition) {
SurveyPosition["Left"] = "left";
SurveyPosition["Right"] = "right";
SurveyPosition["Center"] = "center";
})(SurveyPosition || (SurveyPosition = {}));
var SurveyWidgetType;
(function (SurveyWidgetType) {
SurveyWidgetType["Button"] = "button";
SurveyWidgetType["Tab"] = "tab";
SurveyWidgetType["Selector"] = "selector";
})(SurveyWidgetType || (SurveyWidgetType = {}));
var SurveyType;
(function (SurveyType) {
SurveyType["Popover"] = "popover";
SurveyType["API"] = "api";
SurveyType["Widget"] = "widget";
})(SurveyType || (SurveyType = {}));
var SurveyQuestionDescriptionContentType;
(function (SurveyQuestionDescriptionContentType) {
SurveyQuestionDescriptionContentType["Html"] = "html";
SurveyQuestionDescriptionContentType["Text"] = "text";
})(SurveyQuestionDescriptionContentType || (SurveyQuestionDescriptionContentType = {}));
var SurveyRatingDisplay;
(function (SurveyRatingDisplay) {
SurveyRatingDisplay["Number"] = "number";
SurveyRatingDisplay["Emoji"] = "emoji";
})(SurveyRatingDisplay || (SurveyRatingDisplay = {}));
var SurveyQuestionType;
(function (SurveyQuestionType) {
SurveyQuestionType["Open"] = "open";
SurveyQuestionType["MultipleChoice"] = "multiple_choice";
SurveyQuestionType["SingleChoice"] = "single_choice";
SurveyQuestionType["Rating"] = "rating";
SurveyQuestionType["Link"] = "link";
})(SurveyQuestionType || (SurveyQuestionType = {}));
var SurveyQuestionBranchingType;
(function (SurveyQuestionBranchingType) {
SurveyQuestionBranchingType["NextQuestion"] = "next_question";
SurveyQuestionBranchingType["End"] = "end";
SurveyQuestionBranchingType["ResponseBased"] = "response_based";
SurveyQuestionBranchingType["SpecificQuestion"] = "specific_question";
})(SurveyQuestionBranchingType || (SurveyQuestionBranchingType = {}));
var SurveyMatchType;
(function (SurveyMatchType) {
SurveyMatchType["Regex"] = "regex";
SurveyMatchType["NotRegex"] = "not_regex";
SurveyMatchType["Exact"] = "exact";
SurveyMatchType["IsNot"] = "is_not";
SurveyMatchType["Icontains"] = "icontains";
SurveyMatchType["NotIcontains"] = "not_icontains";
})(SurveyMatchType || (SurveyMatchType = {}));
/** Sync with plugin-server/src/types.ts */
var ActionStepStringMatching;
(function (ActionStepStringMatching) {
ActionStepStringMatching["Contains"] = "contains";
ActionStepStringMatching["Exact"] = "exact";
ActionStepStringMatching["Regex"] = "regex";
})(ActionStepStringMatching || (ActionStepStringMatching = {}));
const normalizeDecideResponse = (decideResponse) => {
if ('flags' in decideResponse) {
// Convert v4 format to v3 format
const featureFlags = getFlagValuesFromFlags(decideResponse.flags);
const featureFlagPayloads = getPayloadsFromFlags(decideResponse.flags);
return {
...decideResponse,
featureFlags,
featureFlagPayloads,
};
}
else {
// Convert v3 format to v4 format
const featureFlags = decideResponse.featureFlags ?? {};
const featureFlagPayloads = Object.fromEntries(Object.entries(decideResponse.featureFlagPayloads || {}).map(([k, v]) => [k, parsePayload(v)]));
const flags = Object.fromEntries(Object.entries(featureFlags).map(([key, value]) => [
key,
getFlagDetailFromFlagAndPayload(key, value, featureFlagPayloads[key]),
]));
return {
...decideResponse,
featureFlags,
featureFlagPayloads,
flags,
};
}
};
function getFlagDetailFromFlagAndPayload(key, value, payload) {
return {
key: key,
enabled: typeof value === 'string' ? true : value,
variant: typeof value === 'string' ? value : undefined,
reason: undefined,
metadata: {
id: undefined,
version: undefined,
payload: payload ? JSON.stringify(payload) : undefined,
description: undefined,
},
};
}
/**
* Get the flag values from the flags v4 response.
* @param flags - The flags
* @returns The flag values
*/
const getFlagValuesFromFlags = (flags) => {
return Object.fromEntries(Object.entries(flags ?? {})
.map(([key, detail]) => [key, getFeatureFlagValue(detail)])
.filter(([, value]) => value !== undefined));
};
/**
* Get the payloads from the flags v4 response.
* @param flags - The flags
* @returns The payloads
*/
const getPayloadsFromFlags = (flags) => {
const safeFlags = flags ?? {};
return Object.fromEntries(Object.keys(safeFlags)
.filter((flag) => {
const details = safeFlags[flag];
return details.enabled && details.metadata && details.metadata.payload !== undefined;
})
.map((flag) => {
const payload = safeFlags[flag].metadata?.payload;
return [flag, payload ? parsePayload(payload) : undefined];
}));
};
const getFeatureFlagValue = (detail) => {
return detail === undefined ? undefined : detail.variant ?? detail.enabled;
};
const parsePayload = (response) => {
if (typeof response !== 'string') {
return response;
}
try {
return JSON.parse(response);
}
catch {
return response;
}
};
function assert(truthyValue, message) {
if (!truthyValue || typeof truthyValue !== 'string' || isEmpty(truthyValue)) {
throw new Error(message);
}
}
function isEmpty(truthyValue) {
if (truthyValue.trim().length === 0) {
return true;
}
return false;
}
function removeTrailingSlash(url) {
return url?.replace(/\/+$/, '');
}
async function retriable(fn, props) {
let lastError = null;
for (let i = 0; i < props.retryCount + 1; i++) {
if (i > 0) {
// don't wait when it's the last try
await new Promise((r) => setTimeout(r, props.retryDelay));
}
try {
const res = await fn();
return res;
}
catch (e) {
lastError = e;
if (!props.retryCheck(e)) {
throw e;
}
}
}
throw lastError;
}
function currentTimestamp() {
return new Date().getTime();
}
function currentISOTime() {
return new Date().toISOString();
}
function safeSetTimeout(fn, timeout) {
// NOTE: we use this so rarely that it is totally fine to do `safeSetTimeout(fn, 0)``
// rather than setImmediate.
const t = setTimeout(fn, timeout);
// We unref if available to prevent Node.js hanging on exit
t?.unref && t?.unref();
return t;
}
function getFetch() {
return typeof fetch !== 'undefined' ? fetch : typeof global.fetch !== 'undefined' ? global.fetch : undefined;
}
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
// This work is free. You can redistribute it and/or modify it
// under the terms of the WTFPL, Version 2
// For more information see LICENSE.txt or http://www.wtfpl.net/
//
// For more information, the home page:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.4
// private property
const f = String.fromCharCode;
const keyStrBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const baseReverseDic = {};
function getBaseValue(alphabet, character) {
if (!baseReverseDic[alphabet]) {
baseReverseDic[alphabet] = {};
for (let i = 0; i < alphabet.length; i++) {
baseReverseDic[alphabet][alphabet.charAt(i)] = i;
}
}
return baseReverseDic[alphabet][character];
}
const LZString = {
compressToBase64: function (input) {
if (input == null) {
return '';
}
const res = LZString._compress(input, 6, function (a) {
return keyStrBase64.charAt(a);
});
switch (res.length % 4 // To produce valid Base64
) {
default: // When could this happen ?
case 0:
return res;
case 1:
return res + '===';
case 2:
return res + '==';
case 3:
return res + '=';
}
},
decompressFromBase64: function (input) {
if (input == null) {
return '';
}
if (input == '') {
return null;
}
return LZString._decompress(input.length, 32, function (index) {
return getBaseValue(keyStrBase64, input.charAt(index));
});
},
compress: function (uncompressed) {
return LZString._compress(uncompressed, 16, function (a) {
return f(a);
});
},
_compress: function (uncompressed, bitsPerChar, getCharFromInt) {
if (uncompressed == null) {
return '';
}
const context_dictionary = {}, context_dictionaryToCreate = {}, context_data = [];
let i, value, context_c = '', context_wc = '', context_w = '', context_enlargeIn = 2, // Compensate for the first entry which should not count
context_dictSize = 3, context_numBits = 2, context_data_val = 0, context_data_position = 0, ii;
for (ii = 0; ii < uncompressed.length; ii += 1) {
context_c = uncompressed.charAt(ii);
if (!Object.prototype.hasOwnProperty.call(context_dictionary, context_c)) {
context_dictionary[context_c] = context_dictSize++;
context_dictionaryToCreate[context_c] = true;
}
context_wc = context_w + context_c;
if (Object.prototype.hasOwnProperty.call(context_dictionary, context_wc)) {
context_w = context_wc;
}
else {
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
if (context_w.charCodeAt(0) < 256) {
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i = 0; i < 8; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
}
else {
value = 1;
for (i = 0; i < context_numBits; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i = 0; i < 16; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
delete context_dictionaryToCreate[context_w];
}
else {
value = context_dictionary[context_w];
for (i = 0; i < context_numBits; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
// Add wc to the dictionary.
context_dictionary[context_wc] = context_dictSize++;
context_w = String(context_c);
}
}
// Output the code for w.
if (context_w !== '') {
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
if (context_w.charCodeAt(0) < 256) {
for (i = 0; i < context_numBits; i++) {
context_data_val = context_data_val << 1;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i = 0; i < 8; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
}
else {
value = 1;
for (i = 0; i < context_numBits; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i = 0; i < 16; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
delete context_dictionaryToCreate[context_w];
}
else {
value = context_dictionary[context_w];
for (i = 0; i < context_numBits; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
}
// Mark the end of the stream
value = 2;
for (i = 0; i < context_numBits; i++) {
context_data_val = (context_data_val << 1) | (value & 1);
if (context_data_position == bitsPerChar - 1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
}
else {
context_data_position++;
}
value = value >> 1;
}
// Flush the last char
while (true) {
context_data_val = context_data_val << 1;
if (context_data_position == bitsPerChar - 1) {
context_data.push(getCharFromInt(context_data_val));
break;
}
else {
context_data_position++;
}
}
return context_data.join('');
},
decompress: function (compressed) {
if (compressed == null) {
return '';
}
if (compressed == '') {
return null;
}
return LZString._decompress(compressed.length, 32768, function (index) {
return compressed.charCodeAt(index);
});
},
_decompress: function (length, resetValue, getNextValue) {
const dictionary = [], result = [], data = { val: getNextValue(0), position: resetValue, index: 1 };
let enlargeIn = 4, dictSize = 4, numBits = 3, entry = '', i, w, bits, resb, maxpower, power, c;
for (i = 0; i < 3; i += 1) {
dictionary[i] = i;
}
bits = 0;
maxpower = Math.pow(2, 2);
power = 1;
while (power != maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb > 0 ? 1 : 0) * power;
power <<= 1;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
switch ((bits)) {
case 0:
bits = 0;
maxpower = Math.pow(2, 8);
power = 1;
while (power != maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb > 0 ? 1 : 0) * power;
power <<= 1;
}
c = f(bits);
break;
case 1:
bits = 0;
maxpower = Math.pow(2, 16);
power = 1;
while (power != maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb > 0 ? 1 : 0) * power;
power <<= 1;
}
c = f(bits);
break;
case 2:
return '';
}
dictionary[3] = c;
w = c;
result.push(c);
while (true) {
if (data.index > length) {
return '';
}
bits = 0;
maxpower = Math.pow(2, numBits);
power = 1;
while (power != maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb > 0 ? 1 : 0) * power;
power <<= 1;
}
switch ((c = bits)) {
case 0:
bits = 0;
maxpower = Math.pow(2, 8);
power = 1;
while (power != maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb > 0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = f(bits);
c = dictSize - 1;
enlargeIn--;
break;
case 1:
bits = 0;
maxpower = Math.pow(2, 16);
power = 1;
while (power != maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb > 0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = f(bits);
c = dictSize - 1;
enlargeIn--;
break;
case 2:
return result.join('');
}
if (enlargeIn == 0) {
enlargeIn = Math.pow(2, numBits);
numBits++;
}
if (dictionary[c]) {
entry = dictionary[c];
}
else {
if (c === dictSize) {
entry = w + w.charAt(0);
}
else {
return null;
}
}
result.push(entry);
// Add w+entry[0] to the dictionary.
dictionary[dictSize++] = w + entry.charAt(0);
enlargeIn--;
w = entry;
if (enlargeIn == 0) {
enlargeIn = Math.pow(2, numBits);
numBits++;
}
}
},
};
class SimpleEventEmitter {
constructor() {
this.events = {};
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return () => {
this.events[event] = this.events[event].filter((x) => x !== listener);
};
}
emit(event, payload) {
for (const listener of this.events[event] || []) {
listener(payload);
}
for (const listener of this.events['*'] || []) {
listener(event, payload);
}
}
}
// vendor from: https://github.com/LiosK/uuidv7/blob/f30b7a7faff73afbce0b27a46c638310f96912ba/src/index.ts
// https://github.com/LiosK/uuidv7#license
/**
* uuidv7: An experimental implementation of the proposed UUID Version 7
*
* @license Apache-2.0
* @copyright 2021-2023 LiosK
* @packageDocumentation
*/
const DIGITS = "0123456789abcdef";
/** Represents a UUID as a 16-byte byte array. */
class UUID {
/** @param bytes - The 16-byte byte array representation. */
constructor(bytes) {
this.bytes = bytes;
}
/**
* Creates an object from the internal representation, a 16-byte byte array
* containing the binary UUID representation in the big-endian byte order.
*
* This method does NOT shallow-copy the argument, and thus the created object
* holds the reference to the underlying buffer.
*
* @throws TypeError if the length of the argument is not 16.
*/
static ofInner(bytes) {
if (bytes.length !== 16) {
throw new TypeError("not 128-bit length");
}
else {
return new UUID(bytes);
}
}
/**
* Builds a byte array from UUIDv7 field values.
*
* @param unixTsMs - A 48-bit `unix_ts_ms` field value.
* @param randA - A 12-bit `rand_a` field value.
* @param randBHi - The higher 30 bits of 62-bit `rand_b` field value.
* @param randBLo - The lower 32 bits of 62-bit `rand_b` field value.
* @throws RangeError if any field value is out of the specified range.
*/
static fromFieldsV7(unixTsMs, randA, randBHi, randBLo) {
if (!Number.isInteger(unixTsMs) ||
!Number.isInteger(randA) ||
!Number.isInteger(randBHi) ||
!Number.isInteger(randBLo) ||
unixTsMs < 0 ||
randA < 0 ||
randBHi < 0 ||
randBLo < 0 ||
unixTsMs > 281474976710655 ||
randA > 0xfff ||
randBHi > 1073741823 ||
randBLo > 4294967295) {
throw new RangeError("invalid field value");
}
const bytes = new Uint8Array(16);
bytes[0] = unixTsMs / 2 ** 40;
bytes[1] = unixTsMs / 2 ** 32;
bytes[2] = unixTsMs / 2 ** 24;
bytes[3] = unixTsMs / 2 ** 16;
bytes[4] = unixTsMs / 2 ** 8;
bytes[5] = unixTsMs;
bytes[6] = 0x70 | (randA >>> 8);
bytes[7] = randA;
bytes[8] = 0x80 | (randBHi >>> 24);
bytes[9] = randBHi >>> 16;
bytes[10] = randBHi >>> 8;
bytes[11] = randBHi;
bytes[12] = randBLo >>> 24;
bytes[13] = randBLo >>> 16;
bytes[14] = randBLo >>> 8;
bytes[15] = randBLo;
return new UUID(bytes);
}
/**
* Builds a byte array from a string representation.
*
* This method accepts the following formats:
*
* - 32-digit hexadecimal format without hyphens: `0189dcd553117d408db09496a2eef37b`
* - 8-4-4-4-12 hyphenated format: `0189dcd5-5311-7d40-8db0-9496a2eef37b`
* - Hyphenated format with surrounding braces: `{0189dcd5-5311-7d40-8db0-9496a2eef37b}`
* - RFC 4122 URN format: `urn:uuid:0189dcd5-5311-7d40-8db0-9496a2eef37b`
*
* Leading and trailing whitespaces represents an error.
*
* @throws SyntaxError if the argument could not parse as a valid UUID string.
*/
static parse(uuid) {
let hex = undefined;
switch (uuid.length) {
case 32:
hex = /^[0-9a-f]{32}$/i.exec(uuid)?.[0];
break;
case 36:
hex =
/^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/i
.exec(uuid)
?.slice(1, 6)
.join("");
break;
case 38:
hex =
/^\{([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})\}$/i
.exec(uuid)
?.slice(1, 6)
.join("");
break;
case 45:
hex =
/^urn:uuid:([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/i
.exec(uuid)
?.slice(1, 6)
.join("");
break;
}
if (hex) {
const inner = new Uint8Array(16);
for (let i = 0; i < 16; i += 4) {
const n = parseInt(hex.substring(2 * i, 2 * i + 8), 16);
inner[i + 0] = n >>> 24;
inner[i + 1] = n >>> 16;
inner[i + 2] = n >>> 8;
inner[i + 3] = n;
}
return new UUID(inner);
}
else {
throw new SyntaxError("could not parse UUID string");
}
}
/**
* @returns The 8-4-4-4-12 canonical hexadecimal string representation
* (`0189dcd5-5311-7d40-8db0-9496a2eef37b`).
*/
toString() {
let text = "";
for (let i = 0; i < this.bytes.length; i++) {
text += DIGITS.charAt(this.bytes[i] >>> 4);
text += DIGITS.charAt(this.bytes[i] & 0xf);
if (i === 3 || i === 5 || i === 7 || i === 9) {
text += "-";
}
}
return text;
}
/**
* @returns The 32-digit hexadecimal representation without hyphens
* (`0189dcd553117d408db09496a2eef37b`).
*/
toHex() {
let text = "";
for (let i = 0; i < this.bytes.length; i++) {
text += DIGITS.charAt(this.bytes[i] >>> 4);
text += DIGITS.charAt(this.bytes[i] & 0xf);
}
return text;
}
/** @returns The 8-4-4-4-12 canonical hexadecimal string representation. */
toJSON() {
return this.toString();
}
/**
* Reports the variant field value of the UUID or, if appropriate, "NIL" or
* "MAX".
*
* For convenience, this method reports "NIL" or "MAX" if `this` represents
* the Nil or Max UUID, although the Nil and Max UUIDs are technically
* subsumed under the variants `0b0` and `0b111`, respectively.
*/
getVariant() {
const n = this.bytes[8] >>> 4;
if (n < 0) {
throw new Error("unreachable");
}
else if (n <= 0b0111) {
return this.bytes.every((e) => e === 0) ? "NIL" : "VAR_0";
}
else if (n <= 0b1011) {
return "VAR_10";
}
else if (n <= 0b1101) {
return "VAR_110";
}
else if (n <= 0b1111) {
return this.bytes.every((e) => e === 0xff) ? "MAX" : "VAR_RESERVED";
}
else {
throw new Error("unreachable");
}
}
/**
* Returns the version field value of the UUID or `undefined` if the UUID does
* not have the variant field value of `0b10`.
*/
getVersion() {
return this.getVariant() === "VAR_10" ? this.bytes[6] >>> 4 : undefined;
}
/** Creates an object from `this`. */
clone() {
return new UUID(this.bytes.slice(0));
}
/** Returns true if `this` is equivalent to `other`. */
equals(other) {
return this.compareTo(other) === 0;
}
/**
* Returns a negative integer, zero, or positive integer if `this` is less
* than, equal to, or greater than `other`, respectively.
*/
compareTo(other) {
for (let i = 0; i < 16; i++) {
const diff = this.bytes[i] - other.bytes[i];
if (diff !== 0) {
return Math.sign(diff);
}
}
return 0;
}
}
/**
* Encapsulates the monotonic counter state.
*
* This class provides APIs to utilize a separate counter state from that of the
* global generator used by {@link uuidv7} and {@link uuidv7obj}. In addition to
* the default {@link generate} method, this class has {@link generateOrAbort}
* that is useful to absolutely guarantee the monotonically increasing order of
* generated UUIDs. See their respective documentation for details.
*/
class V7Generator {
/**
* Creates a generator object with the default random number generator, or
* with the specified one if passed as an argument. The specified random
* number generator should be cryptographically strong and securely seeded.
*/
constructor(randomNumberGenerator) {
this.timestamp = 0;
this.counter = 0;
this.random = randomNumberGenerator ?? getDefaultRandom();
}
/**
* Generates a new UUIDv7 object from the current timestamp, or resets the
* generator upon significant timestamp rollback.
*
* This method returns a monotonically increasing UUID by reusing the previous
* timestamp even if the up-to-date timestamp is smaller than the immediately
* preceding UUID's. However, when such a clock rollback is considered
* significant (i.e., by more than ten seconds), this method resets the
* generator and returns a new UUID based on the given timestamp, breaking the
* increasing order of UUIDs.
*
* See {@link generateOrAbort} for the other mode of generation and
* {@link generateOrResetCore} for the low-level primitive.
*/
generate() {
return this.generateOrResetCore(Date.now(), 10000);
}
/**
* Generates a new UUIDv7 object from the current timestamp, or returns
* `undefined` upon significant timestamp rollback.
*
* This method returns a monotonically increasing UUID by reusing the previous
* timestamp even if the up-to-date timestamp is smaller than the immediately
* preceding UUID's. However, when such a clock rollback is considered
* significant (i.e., by more than ten seconds), this method aborts and
* returns `undefined` immediately.
*
* See {@link generate} for the other mode of generation and
* {@link generateOrAbortCore} for the low-level primitive.
*/
generateOrAbort() {
return this.generateOrAbortCore(Date.now(), 10000);
}
/**
* Generates a new UUIDv7 object from the `unixTsMs` passed, or resets the
* generator upon significant timestamp rollback.
*
* This method is equivalent to {@link generate} except that it takes a custom
* timestamp and clock rollback allowance.
*
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
* considered significant. A suggested value is `10_000` (milliseconds).
* @throws RangeError if `unixTsMs` is not a 48-bit positive integer.
*/
generateOrResetCore(unixTsMs, rollbackAllowance) {
let value = this.generateOrAbortCore(unixTsMs, rollbackAllowance);
if (value === undefined) {
// reset state and resume
this.timestamp = 0;
value = this.generateOrAbortCore(unixTsMs, rollbackAllowance);
}
return value;
}
/**
* Generates a new UUIDv7 object from the `unixTsMs` passed, or returns
* `undefined` upon significant timestamp rollback.
*
* This method is equivalent to {@link generateOrAbort} except that it takes a
* custom timestamp and clock rollback allowance.
*
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
* considered significant. A suggested value is `10_000` (milliseconds).
* @throws RangeError if `unixTsMs` is not a 48-bit positive integer.
*/
generateOrAbortCore(unixTsMs, rollbackAllowance) {
const MAX_COUNTER = 4398046511103;
if (!Number.isInteger(unixTsMs) ||
unixTsMs < 1 ||
unixTsMs > 281474976710655) {
throw new RangeError("`unixTsMs` must be a 48-bit positive integer");
}
else if (rollbackAllowance < 0 || rollbackAllowance > 281474976710655) {
throw new RangeError("`rollbackAllowance` out of reasonable range");
}
if (unixTsMs > this.timestamp) {
this.timestamp = unixTsMs;
this.resetCounter();
}
else if (unixTsMs + rollbackAllowance >= this.timestamp) {
// go on with previous timestamp if new one is not much smaller
this.counter++;
if (this.counter > MAX_COUNTER) {
// increment timestamp at counter overflow
this.timestamp++;
this.resetCounter();
}
}
else {
// abort if clock went backwards to unbearable extent
return undefined;
}
return UUID.fromFieldsV7(this.timestamp, Math.trunc(this.counter / 2 ** 30), this.counter & (2 ** 30 - 1), this.random.nextUint32());
}
/** Initializes the counter at a 42-bit random integer. */
resetCounter() {
this.counter =
this.random.nextUint32() * 0x400 + (this.random.nextUint32() & 0x3ff);
}
/**
* Generates a new UUIDv4 object utilizing the random number generator inside.
*
* @internal
*/
generateV4() {
const bytes = new Uint8Array(Uint32Array.of(this.random.nextUint32(), this.random.nextUint32(), this.random.nextUint32(), this.random.nextUint32()).buffer);
bytes[6] = 0x40 | (bytes[6] >>> 4);
bytes[8] = 0x80 | (bytes[8] >>> 2);
return UUID.ofInner(bytes);
}
}
/** A global flag to force use of cryptographically strong RNG. */
// declare const UUIDV7_DENY_WEAK_RNG: boolean;
/** Returns the default random number generator available in the environment. */
const getDefaultRandom = () => {
// fix: crypto isn't available in react-native, always use Math.random
// // detect Web Crypto API
// if (
// typeof crypto !== "undefined" &&
// typeof crypto.getRandomValues !== "undefined"
// ) {
// return new BufferedCryptoRandom();
// } else {
// // fall back on Math.random() unless the flag is set to true
// if (typeof UUIDV7_DENY_WEAK_RNG !== "undefined" && UUIDV7_DENY_WEAK_RNG) {
// throw new Error("no cryptographically strong RNG available");
// }
// return {
// nextUint32: (): number =>
// Math.trunc(Math.random() * 0x1_0000) * 0x1_0000 +
// Math.trunc(Math.random() * 0x1_0000),
// };
// }
return {
nextUint32: () => Math.trunc(Math.random() * 65536) * 65536 +
Math.trunc(Math.random() * 65536),
};
};
// /**
// * Wraps `crypto.getRandomValues()` to enable buffering; this uses a small
// * buffer by default to avoid both unbearable throughput decline in some
// * environments and the waste of time and space for unused values.
// */
// class BufferedCryptoRandom {
// private readonly buffer = new Uint32Array(8);
// private cursor = 0xffff;
// nextUint32(): number {
// if (this.cursor >= this.buffer.length) {
// crypto.getRandomValues(this.buffer);
// this.cursor = 0;
// }
// return this.buffer[this.cursor++];
// }
// }
let defaultGenerator;
/**
* Generates a UUIDv7 string.
*
* @returns The 8-4-4-4-12 canonical hexadecimal string representation
* ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
*/
const uuidv7 = () => uuidv7obj().toString();
/** Generates a UUIDv7 object. */
const uuidv7obj = () => (defaultGenerator || (defaultGenerator = new V7Generator())).generate();
class PostHogFetchHttpError extends Error {
constructor(response) {
super('HTTP error while fetching PostHog: ' + response.status);
this.response = response;
this.name = 'PostHogFetchHttpError';
}
}
class PostHogFetchNetworkError extends Error {
constructor(error) {
// TRICKY: "cause" is a newer property but is just ignored otherwise. Cast to any to ignore the type issue.
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore
super('Network error while fetching PostHog', error instanceof Error ? { cause: error } : {});
this.error = error;
this.name = 'PostHogFetchNetworkError';
}
}
function isPostHogFetchError(err) {
return typeof err === 'object' && (err instanceof PostHogFetchHttpError || err instanceof PostHogFetchNetworkError);
}
var QuotaLimitedFeature;
(function (QuotaLimitedFeature) {
QuotaLimitedFeature["FeatureFlags"] = "feature_flags";
QuotaLimitedFeature["Recordings"] = "recordings";
})(QuotaLimitedFeature || (QuotaLimitedFeature = {}));
class PostHogCoreStateless {
constructor(apiKey, options) {
this.flushPromise = null;
this.pendingPromises = {};
// internal
this._events = new SimpleEventEmitter();
this._isInitialized = false;
assert(apiKey, "You must pass your PostHog project's api key.");
this.apiKey = apiKey;
this.host = removeTrailingSlash(options?.host || 'https://us.i.posthog.com');
this.flushAt = options?.flushAt ? Math.max(options?.flushAt, 1) : 20;
this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
this.maxQueueSize = Math.max(this.flushAt, options?.maxQueueSize ?? 1000);
this.flushInterval = options?.flushInterval ?? 10000;
this.captureMode = options?.captureMode || 'json';
this.preloadFeatureFlags = options?.preloadFeatureFlags ?? true;
// If enable is explicitly set to false we override the optout
this.defaultOptIn = options?.defaultOptIn ?? true;
this.disableSurveys = options?.disableSurveys ?? false;
this._retryOptions = {
retryCount: options?.fetchRetryCount ?? 3,
retryDelay: options?.fetchRetryDelay ?? 3000,
retryCheck: isPostHogFetchError,
};
this.requestTimeout = options?.requestTimeout ?? 10000; // 10 seconds
this.featureFlagsRequestTimeoutMs = options?.featureFlagsRequestTimeoutMs ?? 3000; // 3 seconds
this.remoteConfigRequestTimeoutMs = options?.remoteConfigRequestTimeoutMs ?? 3000; // 3 seconds
this.disableGeoip = options?.disableGeoip ?? true;
this.disabled = options?.disabled ?? false;
this.historicalMigration = options?.historicalMigration ?? false;
// Init promise allows the derived class to block calls until it is ready
this._initPromise = Promise.resolve();
this._isInitialized = true;
}
logMsgIfDebug(fn) {
if (this.isDebug) {
fn();
}
}
wrap(fn) {
if (this.disabled) {
this.logMsgIfDebug(() => console.warn('[PostHog] The client is disabled'));
return;
}
if (this._isInitialized) {
// NOTE: We could also check for the "opt in" status here...
return fn();
}
this._initPromise.then(() => fn());
}
getCommonEventProperties() {
return {
$lib: this.getLibraryId(),
$lib_version: this.getLibraryVersion(),
};
}
get optedOut() {
return this.getPersistedProperty(PostHogPersistedProperty.OptedOut) ?? !this.defaultOptIn;
}
async optIn() {
this.wrap(() => {
this.setPersistedProperty(PostHogPersistedProperty.OptedOut, false);
});
}
async optOut() {
this.wrap(() => {
this.setPersistedProperty(PostHogPersistedProperty.OptedOut, true);
});
}
on(event, cb) {
return this._events.on(event, cb);
}
debug(enabled = true) {
this.removeDebugCallback?.();
if (enabled) {
const removeDebugCallback = this.on('*', (event, payload) => console.log('PostHog Debug', event, payload));
this.removeDebugCallback = () => {
removeDebugCallback();
this.removeDebugCallback = undefined;
};
}
}
get isDebug() {
return !!this.removeDebugCallback;
}
get isDisabled() {
return this.disabled;
}
buildPayload(payload) {
return {
distinct_id: payload.distinct_id,
event: payload.event,
properties: {
...(payload.properties || {}),
...this.getCommonEventProperties(), // Common PH props
},
};
}
addPendingPromise(promise) {
const promiseUUID = uuidv7();
this.pendingPromises[promiseUUID] = promise;
promise
.catch(() => { })
.finally(() => {
delete this.pendingPromises[promiseUUID];
});
return promise;
}
/***
*** TRACKING
***/
identifyStateless(distinctId, properties, options) {
this.wrap(() => {
// The properties passed to identifyStateless are event properties.
// To add person properties, pass in all person properties to the `$set` and `$set_once` keys.
const payload = {
...this.buildPayload({
distinct_id: distinctId,
event: '$identify',
properties,
}),
};
this.enqueue('identify', payload, options);
});
}
captureStateless(distinctId, event, properties, options) {
this.wrap(() => {
const payload = this.buildPayload({ distinct_id: distinctId, event, properties });
this.enqueue('capture', payload, options);
});
}
aliasStateless(alias, distinctId, properties, options) {
this.wrap(() => {
const payload = this.buildPayload({
event: '$create_alias',
distinct_id: distinctId,
properties: {
...(properties || {}),
distinct_id: distinctId,
alias,
},
});
this.enqueue('alias', payload, options);
});
}
/***
*** GROUPS
***/
groupIdentifyStateless(groupType, g