UNPKG

posthog-node

Version:
1,266 lines (1,255 loc) 149 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var path = require('path'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } 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, option