From 0b427350ecb6d605522e75fde340f984b98b4057 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Wed, 18 Dec 2024 08:42:51 +0000 Subject: [PATCH] chore: remove circular dependencies --- src/__tests__/utils.test.ts | 4 +- src/autocapture-utils.ts | 3 +- src/autocapture.ts | 3 +- src/consent.ts | 3 +- src/customizations/before-send.ts | 2 +- ...ilePropertiesAsPersonPropertiesForFlags.ts | 3 +- src/extensions/replay/sessionrecording.ts | 2 +- src/heatmaps.ts | 3 +- src/posthog-core.ts | 3 +- src/posthog-persistence.ts | 3 +- src/request.ts | 4 +- src/utils/encode-utils.ts | 95 +++++++++++++++ src/utils/event-utils.ts | 3 +- src/utils/index.ts | 112 ------------------ src/utils/string-utils.ts | 16 +++ src/utils/type-utils.ts | 2 +- src/utils/user-agent-utils.ts | 2 +- 17 files changed, 136 insertions(+), 127 deletions(-) create mode 100644 src/utils/encode-utils.ts create mode 100644 src/utils/string-utils.ts diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index a8ae87d6b..d58b94492 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -7,11 +7,13 @@ * currently not supported in the browser lib). */ -import { _copyAndTruncateStrings, isCrossDomainCookie, _base64Encode } from '../utils' +import { _copyAndTruncateStrings, isCrossDomainCookie } from '../utils' import { Info } from '../utils/event-utils' import { isLikelyBot, DEFAULT_BLOCKED_UA_STRS, isBlockedUA, NavigatorUAData } from '../utils/blocked-uas' import { expect } from '@jest/globals' +import { _base64Encode } from '../utils/encode-utils' + function userAgentFor(botString: string) { const randOne = (Math.random() + 1).toString(36).substring(7) const randTwo = (Math.random() + 1).toString(36).substring(7) diff --git a/src/autocapture-utils.ts b/src/autocapture-utils.ts index 49e6d9612..0c5474a21 100644 --- a/src/autocapture-utils.ts +++ b/src/autocapture-utils.ts @@ -1,10 +1,11 @@ import { AutocaptureConfig, Properties } from './types' -import { each, entries, includes, trim } from './utils' +import { each, entries } from './utils' import { isArray, isNullish, isString, isUndefined } from './utils/type-utils' import { logger } from './utils/logger' import { window } from './utils/globals' import { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils' +import { includes, trim } from './utils/string-utils' export function splitClassString(s: string): string[] { return s ? trim(s).split(/\s+/) : [] diff --git a/src/autocapture.ts b/src/autocapture.ts index 56cd4e1fd..917a4a788 100644 --- a/src/autocapture.ts +++ b/src/autocapture.ts @@ -1,4 +1,4 @@ -import { each, extend, includes, registerEvent } from './utils' +import { each, extend, registerEvent } from './utils' import { autocaptureCompatibleElements, getClassNames, @@ -24,6 +24,7 @@ import { createLogger } from './utils/logger' import { document, window } from './utils/globals' import { convertToURL } from './utils/request-utils' import { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils' +import { includes } from './utils/string-utils' const logger = createLogger('[AutoCapture]') diff --git a/src/consent.ts b/src/consent.ts index 0b6f4b830..a61dfdd5e 100644 --- a/src/consent.ts +++ b/src/consent.ts @@ -1,8 +1,9 @@ import { PostHog } from './posthog-core' -import { find, includes } from './utils' +import { find } from './utils' import { assignableWindow, navigator } from './utils/globals' import { cookieStore, localStore } from './storage' import { PersistentStore } from './types' +import { includes } from './utils/string-utils' const OPT_OUT_PREFIX = '__ph_opt_in_out_' diff --git a/src/customizations/before-send.ts b/src/customizations/before-send.ts index dc72de278..365273415 100644 --- a/src/customizations/before-send.ts +++ b/src/customizations/before-send.ts @@ -1,7 +1,7 @@ import { clampToRange } from '../utils/number-utils' import { BeforeSendFn, CaptureResult, KnownEventName } from '../types' -import { includes } from '../utils' import { isArray, isUndefined } from '../utils/type-utils' +import { includes } from '../utils/string-utils' function appendArray(currentValue: string[] | undefined, sampleType: string | string[]): string[] { return [...(currentValue ? currentValue : []), ...(isArray(sampleType) ? sampleType : [sampleType])] diff --git a/src/customizations/setAllPersonProfilePropertiesAsPersonPropertiesForFlags.ts b/src/customizations/setAllPersonProfilePropertiesAsPersonPropertiesForFlags.ts index 42b7f52f9..659eda5f9 100644 --- a/src/customizations/setAllPersonProfilePropertiesAsPersonPropertiesForFlags.ts +++ b/src/customizations/setAllPersonProfilePropertiesAsPersonPropertiesForFlags.ts @@ -1,6 +1,7 @@ import { PostHog } from '../posthog-core' import { CAMPAIGN_PARAMS, EVENT_TO_PERSON_PROPERTIES, Info } from '../utils/event-utils' -import { each, extend, includes } from '../utils' +import { each, extend } from '../utils' +import { includes } from '../utils/string-utils' export const setAllPersonProfilePropertiesAsPersonPropertiesForFlags = (posthog: PostHog): void => { const allProperties = extend({}, Info.properties(), Info.campaignParams(), Info.referrerInfo()) diff --git a/src/extensions/replay/sessionrecording.ts b/src/extensions/replay/sessionrecording.ts index 8f5d21597..a637bb523 100644 --- a/src/extensions/replay/sessionrecording.ts +++ b/src/extensions/replay/sessionrecording.ts @@ -45,8 +45,8 @@ import { isLocalhost } from '../../utils/request-utils' import { MutationRateLimiter } from './mutation-rate-limiter' import { gzipSync, strFromU8, strToU8 } from 'fflate' import { clampToRange } from '../../utils/number-utils' -import { includes } from '../../utils' import Config from '../../config' +import { includes } from '../../utils/string-utils' const LOGGER_PREFIX = '[SessionRecording]' const logger = createLogger(LOGGER_PREFIX) diff --git a/src/heatmaps.ts b/src/heatmaps.ts index 52b30c45d..e1e69a270 100644 --- a/src/heatmaps.ts +++ b/src/heatmaps.ts @@ -1,4 +1,4 @@ -import { includes, registerEvent } from './utils' +import { registerEvent } from './utils' import RageClick from './extensions/rageclick' import { DeadClickCandidate, Properties, RemoteConfig } from './types' import { PostHog } from './posthog-core' @@ -10,6 +10,7 @@ import { isEmptyObject, isObject, isUndefined } from './utils/type-utils' import { createLogger } from './utils/logger' import { isElementInToolbar, isElementNode, isTag } from './utils/element-utils' import { DeadClicksAutocapture, isDeadClicksEnabledForHeatmaps } from './extensions/dead-clicks-autocapture' +import { includes } from './utils/string-utils' const DEFAULT_FLUSH_INTERVAL = 5000 diff --git a/src/posthog-core.ts b/src/posthog-core.ts index af4f22cbf..c10a1895f 100644 --- a/src/posthog-core.ts +++ b/src/posthog-core.ts @@ -4,11 +4,9 @@ import { each, eachArray, extend, - includes, registerEvent, safewrapClass, isCrossDomainCookie, - isDistinctIdStringLike, } from './utils' import { assignableWindow, document, location, navigator, userAgent, window } from './utils/globals' import { PostHogFeatureFlags } from './posthog-featureflags' @@ -84,6 +82,7 @@ import { WebExperiments } from './web-experiments' import { PostHogExceptions } from './posthog-exceptions' import { SiteApps } from './site-apps' import { DeadClicksAutocapture, isDeadClicksEnabledForAutocapture } from './extensions/dead-clicks-autocapture' +import { includes, isDistinctIdStringLike } from './utils/string-utils' /* SIMPLE STYLE GUIDE: diff --git a/src/posthog-persistence.ts b/src/posthog-persistence.ts index 0a5546c00..f081ee708 100644 --- a/src/posthog-persistence.ts +++ b/src/posthog-persistence.ts @@ -1,6 +1,6 @@ /* eslint camelcase: "off" */ -import { each, extend, include, stripEmptyProperties, stripLeadingDollar } from './utils' +import { each, extend, include, stripEmptyProperties } from './utils' import { cookieStore, localPlusCookieStore, localStore, memoryStore, sessionStore } from './storage' import { PersistentStore, PostHogConfig, Properties } from './types' import { @@ -15,6 +15,7 @@ import { import { isEmptyObject, isObject, isUndefined } from './utils/type-utils' import { Info } from './utils/event-utils' import { logger } from './utils/logger' +import { stripLeadingDollar } from './utils/string-utils' const CASE_INSENSITIVE_PERSISTENCE_TYPES: readonly Lowercase[] = [ 'cookie', diff --git a/src/request.ts b/src/request.ts index f9fd9fdaf..8ca2a54fc 100644 --- a/src/request.ts +++ b/src/request.ts @@ -1,4 +1,4 @@ -import { _base64Encode, each, find } from './utils' +import { each, find } from './utils' import Config from './config' import { Compression, RequestOptions, RequestResponse } from './types' import { formDataToQuery } from './utils/request-utils' @@ -7,6 +7,8 @@ import { logger } from './utils/logger' import { AbortController, fetch, navigator, XMLHttpRequest } from './utils/globals' import { gzipSync, strToU8 } from 'fflate' +import { _base64Encode } from './utils/encode-utils' + // eslint-disable-next-line compat/compat export const SUPPORTS_REQUEST = !!XMLHttpRequest || !!fetch diff --git a/src/utils/encode-utils.ts b/src/utils/encode-utils.ts new file mode 100644 index 000000000..33a68dfec --- /dev/null +++ b/src/utils/encode-utils.ts @@ -0,0 +1,95 @@ +import { isNull } from './type-utils' + +export function _base64Encode(data: null): null +export function _base64Encode(data: undefined): undefined +export function _base64Encode(data: string): string +export function _base64Encode(data: string | null | undefined): string | null | undefined { + const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + let o1, + o2, + o3, + h1, + h2, + h3, + h4, + bits, + i = 0, + ac = 0, + enc = '' + const tmp_arr: string[] = [] + + if (!data) { + return data + } + + data = utf8Encode(data) + + do { + // pack three octets into four hexets + o1 = data.charCodeAt(i++) + o2 = data.charCodeAt(i++) + o3 = data.charCodeAt(i++) + + bits = (o1 << 16) | (o2 << 8) | o3 + + h1 = (bits >> 18) & 0x3f + h2 = (bits >> 12) & 0x3f + h3 = (bits >> 6) & 0x3f + h4 = bits & 0x3f + + // use hexets to index into b64, and append result to encoded string + tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4) + } while (i < data.length) + + enc = tmp_arr.join('') + + switch (data.length % 3) { + case 1: + enc = enc.slice(0, -2) + '==' + break + case 2: + enc = enc.slice(0, -1) + '=' + break + } + + return enc +} + +export const utf8Encode = function (string: string): string { + string = (string + '').replace(/\r\n/g, '\n').replace(/\r/g, '\n') + + let utftext = '', + start, + end + let stringl = 0, + n + + start = end = 0 + stringl = string.length + + for (n = 0; n < stringl; n++) { + const c1 = string.charCodeAt(n) + let enc = null + + if (c1 < 128) { + end++ + } else if (c1 > 127 && c1 < 2048) { + enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128) + } else { + enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128) + } + if (!isNull(enc)) { + if (end > start) { + utftext += string.substring(start, end) + } + utftext += enc + start = end = n + 1 + } + } + + if (end > start) { + utftext += string.substring(start, string.length) + } + + return utftext +} diff --git a/src/utils/event-utils.ts b/src/utils/event-utils.ts index c1699630c..fdb69a23c 100644 --- a/src/utils/event-utils.ts +++ b/src/utils/event-utils.ts @@ -2,9 +2,10 @@ import { getQueryParam, convertToURL } from './request-utils' import { isNull } from './type-utils' import { Properties } from '../types' import Config from '../config' -import { each, extend, stripEmptyProperties, stripLeadingDollar } from './index' +import { each, extend, stripEmptyProperties } from './index' import { document, location, userAgent, window } from './globals' import { detectBrowser, detectBrowserVersion, detectDevice, detectDeviceType, detectOS } from './user-agent-utils' +import { stripLeadingDollar } from './string-utils' const URL_REGEX_PREFIX = 'https?://(.*)' diff --git a/src/utils/index.ts b/src/utils/index.ts index 39d4bec8e..c699dc1bf 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,12 +5,6 @@ import { nativeForEach, nativeIndexOf, window } from './globals' const breaker: Breaker = {} -// UNDERSCORE -// Embed part of the Underscore Library -export const trim = function (str: string): string { - return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '') -} - export function eachArray( obj: E[] | null | undefined, iterator: (value: E, key: number) => void | Breaker, @@ -89,10 +83,6 @@ export const include = function ( return found } -export function includes(str: T[] | string, needle: T): boolean { - return (str as any).indexOf(needle) !== -1 -} - /** * Object.entries() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries @@ -157,10 +147,6 @@ export const stripEmptyProperties = function (p: Properties): Properties { return ret } -export const stripLeadingDollar = function (s: string): string { - return s.replace(/^\$/, '') -} - /** * Deep copies an object. * It handles cycles by replacing all references to them with `undefined` @@ -213,100 +199,6 @@ export function _copyAndTruncateStrings = Record> 18) & 0x3f - h2 = (bits >> 12) & 0x3f - h3 = (bits >> 6) & 0x3f - h4 = bits & 0x3f - - // use hexets to index into b64, and append result to encoded string - tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4) - } while (i < data.length) - - enc = tmp_arr.join('') - - switch (data.length % 3) { - case 1: - enc = enc.slice(0, -2) + '==' - break - case 2: - enc = enc.slice(0, -1) + '=' - break - } - - return enc -} - -export const utf8Encode = function (string: string): string { - string = (string + '').replace(/\r\n/g, '\n').replace(/\r/g, '\n') - - let utftext = '', - start, - end - let stringl = 0, - n - - start = end = 0 - stringl = string.length - - for (n = 0; n < stringl; n++) { - const c1 = string.charCodeAt(n) - let enc = null - - if (c1 < 128) { - end++ - } else if (c1 > 127 && c1 < 2048) { - enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128) - } else { - enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128) - } - if (!isNull(enc)) { - if (end > start) { - utftext += string.substring(start, end) - } - utftext += enc - start = end = n + 1 - } - } - - if (end > start) { - utftext += string.substring(start, string.length) - } - - return utftext -} - export const registerEvent = (function () { // written by Dean Edwards, 2005 // with input from Tino Zijdel - crisp@xs4all.nl @@ -405,10 +297,6 @@ export function isCrossDomainCookie(documentLocation: Location | undefined) { return hostname.split('.').slice(-2).join('.') !== 'herokuapp.com' } -export function isDistinctIdStringLike(value: string): boolean { - return ['distinct_id', 'distinctid'].includes(value.toLowerCase()) -} - export function find(value: T[], predicate: (value: T) => boolean): T | undefined { for (let i = 0; i < value.length; i++) { if (predicate(value[i])) { diff --git a/src/utils/string-utils.ts b/src/utils/string-utils.ts new file mode 100644 index 000000000..3cf337c1f --- /dev/null +++ b/src/utils/string-utils.ts @@ -0,0 +1,16 @@ +export function includes(str: T[] | string, needle: T): boolean { + return (str as any).indexOf(needle) !== -1 +} + +// UNDERSCORE +// Embed part of the Underscore Library +export const trim = function (str: string): string { + return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '') +} +export const stripLeadingDollar = function (s: string): string { + return s.replace(/^\$/, '') +} + +export function isDistinctIdStringLike(value: string): boolean { + return ['distinct_id', 'distinctid'].includes(value.toLowerCase()) +} diff --git a/src/utils/type-utils.ts b/src/utils/type-utils.ts index b6aeb9496..c3ea4ade5 100644 --- a/src/utils/type-utils.ts +++ b/src/utils/type-utils.ts @@ -1,6 +1,6 @@ -import { includes } from '.' import { window } from './globals' import { knownUnsafeEditableEvent, KnownUnsafeEditableEvent } from '../types' +import { includes } from './string-utils' // eslint-disable-next-line posthog-js/no-direct-array-check const nativeIsArray = Array.isArray diff --git a/src/utils/user-agent-utils.ts b/src/utils/user-agent-utils.ts index 7a27b0b90..69a1ac900 100644 --- a/src/utils/user-agent-utils.ts +++ b/src/utils/user-agent-utils.ts @@ -1,5 +1,5 @@ -import { includes } from './index' import { isFunction, isUndefined } from './type-utils' +import { includes } from './string-utils' /** * this device detection code is (at time of writing) about 3% of the size of the entire library