@leanbase.com/js 0.1.3 → 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +37 -0
  2. package/dist/autocapture-utils.d.ts +17 -0
  3. package/dist/autocapture.d.ts +35 -0
  4. package/dist/config.d.ts +5 -0
  5. package/dist/constants.d.ts +54 -0
  6. package/dist/entrypoints/main.cjs.d.ts +4 -0
  7. package/dist/entrypoints/module.es.d.ts +4 -0
  8. package/dist/extensions/rageclick.d.ts +9 -0
  9. package/dist/iife.d.ts +19 -0
  10. package/dist/index.d.ts +2 -779
  11. package/dist/leanbase-logger.d.ts +6 -0
  12. package/dist/leanbase-persistence.d.ts +64 -0
  13. package/dist/leanbase.d.ts +49 -0
  14. package/dist/leanbase.iife.js +1 -4747
  15. package/dist/leanbase.iife.js.map +1 -1
  16. package/dist/main.js +2 -0
  17. package/dist/main.js.map +1 -0
  18. package/dist/module.d.ts +780 -0
  19. package/dist/module.js +2 -0
  20. package/dist/module.js.map +1 -0
  21. package/dist/page-view.d.ts +29 -0
  22. package/dist/scroll-manager.d.ts +21 -0
  23. package/dist/session-props.d.ts +32 -0
  24. package/dist/sessionid.d.ts +50 -0
  25. package/dist/storage.d.ts +24 -0
  26. package/{src/types.ts → dist/types.d.ts} +145 -235
  27. package/dist/utils/blocked-uas.d.ts +17 -0
  28. package/dist/utils/element-utils.d.ts +5 -0
  29. package/dist/utils/event-utils.d.ts +22 -0
  30. package/dist/utils/index.d.ts +44 -0
  31. package/dist/utils/request-utils.d.ts +12 -0
  32. package/dist/utils/simple-event-emitter.d.ts +6 -0
  33. package/dist/utils/user-agent-utils.d.ts +18 -0
  34. package/dist/uuidv7.d.ts +43 -0
  35. package/dist/version.d.ts +1 -0
  36. package/lib/autocapture-utils.d.ts +17 -0
  37. package/{src/autocapture-utils.ts → lib/autocapture-utils.js} +196 -280
  38. package/lib/autocapture-utils.js.map +1 -0
  39. package/lib/autocapture.d.ts +35 -0
  40. package/lib/autocapture.js +311 -0
  41. package/lib/autocapture.js.map +1 -0
  42. package/lib/config.d.ts +5 -0
  43. package/lib/config.js +7 -0
  44. package/lib/config.js.map +1 -0
  45. package/lib/constants.d.ts +54 -0
  46. package/{src/constants.ts → lib/constants.js} +50 -57
  47. package/lib/constants.js.map +1 -0
  48. package/lib/entrypoints/main.cjs.d.ts +4 -0
  49. package/lib/entrypoints/main.cjs.js +3 -0
  50. package/lib/entrypoints/main.cjs.js.map +1 -0
  51. package/lib/entrypoints/module.es.d.ts +4 -0
  52. package/lib/entrypoints/module.es.js +3 -0
  53. package/lib/entrypoints/module.es.js.map +1 -0
  54. package/lib/extensions/rageclick.d.ts +9 -0
  55. package/lib/extensions/rageclick.js +27 -0
  56. package/lib/extensions/rageclick.js.map +1 -0
  57. package/lib/iife.d.ts +19 -0
  58. package/lib/iife.js +67 -0
  59. package/lib/iife.js.map +1 -0
  60. package/{src/index.ts → lib/index.d.ts} +2 -2
  61. package/lib/index.js +2 -0
  62. package/lib/index.js.map +1 -0
  63. package/lib/leanbase-logger.d.ts +6 -0
  64. package/lib/leanbase-logger.js +25 -0
  65. package/lib/leanbase-logger.js.map +1 -0
  66. package/lib/leanbase-persistence.d.ts +64 -0
  67. package/lib/leanbase-persistence.js +287 -0
  68. package/lib/leanbase-persistence.js.map +1 -0
  69. package/lib/leanbase.d.ts +49 -0
  70. package/lib/leanbase.js +294 -0
  71. package/lib/leanbase.js.map +1 -0
  72. package/lib/page-view.d.ts +29 -0
  73. package/lib/page-view.js +81 -0
  74. package/lib/page-view.js.map +1 -0
  75. package/lib/scroll-manager.d.ts +21 -0
  76. package/lib/scroll-manager.js +79 -0
  77. package/lib/scroll-manager.js.map +1 -0
  78. package/lib/session-props.d.ts +32 -0
  79. package/lib/session-props.js +73 -0
  80. package/lib/session-props.js.map +1 -0
  81. package/lib/sessionid.d.ts +50 -0
  82. package/{src/sessionid.ts → lib/sessionid.js} +128 -204
  83. package/lib/sessionid.js.map +1 -0
  84. package/lib/storage.d.ts +24 -0
  85. package/{src/storage.ts → lib/storage.js} +182 -225
  86. package/lib/storage.js.map +1 -0
  87. package/lib/types.d.ts +544 -0
  88. package/lib/types.js +7 -0
  89. package/lib/types.js.map +1 -0
  90. package/lib/utils/blocked-uas.d.ts +17 -0
  91. package/{src/utils/blocked-uas.ts → lib/utils/blocked-uas.js} +19 -48
  92. package/lib/utils/blocked-uas.js.map +1 -0
  93. package/lib/utils/element-utils.d.ts +5 -0
  94. package/{src/utils/element-utils.ts → lib/utils/element-utils.js} +13 -17
  95. package/lib/utils/element-utils.js.map +1 -0
  96. package/lib/utils/event-utils.d.ts +22 -0
  97. package/lib/utils/event-utils.js +258 -0
  98. package/lib/utils/event-utils.js.map +1 -0
  99. package/lib/utils/index.d.ts +44 -0
  100. package/lib/utils/index.js +183 -0
  101. package/lib/utils/index.js.map +1 -0
  102. package/lib/utils/request-utils.d.ts +12 -0
  103. package/lib/utils/request-utils.js +107 -0
  104. package/lib/utils/request-utils.js.map +1 -0
  105. package/lib/utils/simple-event-emitter.d.ts +6 -0
  106. package/lib/utils/simple-event-emitter.js +24 -0
  107. package/lib/utils/simple-event-emitter.js.map +1 -0
  108. package/lib/utils/user-agent-utils.d.ts +18 -0
  109. package/lib/utils/user-agent-utils.js +369 -0
  110. package/lib/utils/user-agent-utils.js.map +1 -0
  111. package/lib/uuidv7.d.ts +43 -0
  112. package/{src/uuidv7.ts → lib/uuidv7.js} +103 -131
  113. package/lib/uuidv7.js.map +1 -0
  114. package/lib/version.d.ts +1 -0
  115. package/lib/version.js +2 -0
  116. package/lib/version.js.map +1 -0
  117. package/package.json +56 -45
  118. package/dist/index.cjs +0 -3034
  119. package/dist/index.cjs.map +0 -1
  120. package/dist/index.mjs +0 -3032
  121. package/dist/index.mjs.map +0 -1
  122. package/src/autocapture.ts +0 -415
  123. package/src/config.ts +0 -8
  124. package/src/extensions/rageclick.ts +0 -34
  125. package/src/iife.ts +0 -87
  126. package/src/leanbase-logger.ts +0 -26
  127. package/src/leanbase-persistence.ts +0 -374
  128. package/src/leanbase.ts +0 -424
  129. package/src/page-view.ts +0 -124
  130. package/src/scroll-manager.ts +0 -103
  131. package/src/session-props.ts +0 -114
  132. package/src/utils/event-utils.ts +0 -304
  133. package/src/utils/index.ts +0 -222
  134. package/src/utils/request-utils.ts +0 -128
  135. package/src/utils/simple-event-emitter.ts +0 -27
  136. package/src/utils/user-agent-utils.ts +0 -357
  137. package/src/version.ts +0 -1
@@ -1,114 +0,0 @@
1
- /* Store some session-level attribution-related properties in the persistence layer
2
- *
3
- * These have the same lifespan as a session_id, meaning that if the session_id changes, these properties will be reset.
4
- *
5
- * We only store the entry URL and referrer, and derive many props (such as utm tags) from those.
6
- *
7
- * Given that the cookie is limited to 4K bytes, we don't want to store too much data, so we chose not to store device
8
- * properties (such as browser, OS, etc) here, as usually getting the current value of those from event properties is
9
- * sufficient.
10
- */
11
- import { getPersonInfo, getPersonPropsFromInfo } from './utils/event-utils'
12
- import type { SessionIdManager } from './sessionid'
13
- import type { LeanbasePersistence } from './leanbase-persistence'
14
- import { CLIENT_SESSION_PROPS } from './constants'
15
- import type { Leanbase } from './leanbase'
16
- import { each, stripEmptyProperties } from './utils'
17
- import { stripLeadingDollar } from '@posthog/core'
18
-
19
- interface LegacySessionSourceProps {
20
- initialPathName: string
21
- referringDomain: string // Is actually referring host, but named referring domain for internal consistency. Should contain a port if there is one.
22
- utm_medium?: string
23
- utm_source?: string
24
- utm_campaign?: string
25
- utm_content?: string
26
- utm_term?: string
27
- }
28
-
29
- interface CurrentSessionSourceProps {
30
- r: string // Referring host
31
- u: string | undefined // full URL
32
- }
33
-
34
- interface StoredSessionSourceProps {
35
- sessionId: string
36
- props: LegacySessionSourceProps | CurrentSessionSourceProps
37
- }
38
-
39
- const generateSessionSourceParams = (posthog?: Leanbase): LegacySessionSourceProps | CurrentSessionSourceProps => {
40
- return getPersonInfo(posthog?.config.mask_personal_data_properties, posthog?.config.custom_personal_data_properties)
41
- }
42
-
43
- export class SessionPropsManager {
44
- private readonly _instance: Leanbase
45
- private readonly _sessionIdManager: SessionIdManager
46
- private readonly _persistence: LeanbasePersistence
47
- private readonly _sessionSourceParamGenerator: (
48
- instance?: Leanbase
49
- ) => LegacySessionSourceProps | CurrentSessionSourceProps
50
-
51
- constructor(
52
- instance: Leanbase,
53
- sessionIdManager: SessionIdManager,
54
- persistence: LeanbasePersistence,
55
- sessionSourceParamGenerator?: (instance?: Leanbase) => LegacySessionSourceProps | CurrentSessionSourceProps
56
- ) {
57
- this._instance = instance
58
- this._sessionIdManager = sessionIdManager
59
- this._persistence = persistence
60
- this._sessionSourceParamGenerator = sessionSourceParamGenerator || generateSessionSourceParams
61
-
62
- this._sessionIdManager.onSessionId(this._onSessionIdCallback)
63
- }
64
-
65
- _getStored(): StoredSessionSourceProps | undefined {
66
- return this._persistence.props[CLIENT_SESSION_PROPS]
67
- }
68
-
69
- _onSessionIdCallback = (sessionId: string) => {
70
- const stored = this._getStored()
71
- if (stored && stored.sessionId === sessionId) {
72
- return
73
- }
74
-
75
- const newProps: StoredSessionSourceProps = {
76
- sessionId,
77
- props: this._sessionSourceParamGenerator(this._instance),
78
- }
79
- this._persistence.register({ [CLIENT_SESSION_PROPS]: newProps })
80
- }
81
-
82
- getSetOnceProps() {
83
- const p = this._getStored()?.props
84
- if (!p) {
85
- return {}
86
- }
87
- if ('r' in p) {
88
- return getPersonPropsFromInfo(p)
89
- } else {
90
- return {
91
- $referring_domain: p.referringDomain,
92
- $pathname: p.initialPathName,
93
- utm_source: p.utm_source,
94
- utm_campaign: p.utm_campaign,
95
- utm_medium: p.utm_medium,
96
- utm_content: p.utm_content,
97
- utm_term: p.utm_term,
98
- }
99
- }
100
- }
101
-
102
- getSessionProps() {
103
- // it's the same props, but don't include null for unset properties, and add a prefix
104
- const p: Record<string, any> = {}
105
- each(stripEmptyProperties(this.getSetOnceProps()), (v, k) => {
106
- if (k === '$current_url') {
107
- // $session_entry_current_url would be a weird name, call it $session_entry_url instead
108
- k = 'url'
109
- }
110
- p[`$session_entry_${stripLeadingDollar(k)}`] = v
111
- })
112
- return p
113
- }
114
- }
@@ -1,304 +0,0 @@
1
- import { convertToURL, getQueryParam, maskQueryParams } from './request-utils'
2
- import { isNull, stripLeadingDollar } from '@posthog/core'
3
- import { each, extend, extendArray, stripEmptyProperties } from './index'
4
- import { document, location, userAgent, window } from './'
5
- import { detectBrowser, detectBrowserVersion, detectDevice, detectDeviceType, detectOS } from './user-agent-utils'
6
- import { cookieStore } from '../storage'
7
- import Config from '../config'
8
- import { Properties } from '../types'
9
-
10
- const URL_REGEX_PREFIX = 'https?://(.*)'
11
-
12
- // CAMPAIGN_PARAMS and EVENT_TO_PERSON_PROPERTIES should be kept in sync with
13
- // https://github.com/PostHog/posthog/blob/master/plugin-server/src/utils/db/utils.ts#L60
14
-
15
- // The list of campaign parameters that could be considered personal data under e.g. GDPR.
16
- // These can be masked in URLs and properties before being sent to posthog.
17
- export const PERSONAL_DATA_CAMPAIGN_PARAMS = [
18
- 'gclid', // google ads
19
- 'gclsrc', // google ads 360
20
- 'dclid', // google display ads
21
- 'gbraid', // google ads, web to app
22
- 'wbraid', // google ads, app to web
23
- 'fbclid', // facebook
24
- 'msclkid', // microsoft
25
- 'twclid', // twitter
26
- 'li_fat_id', // linkedin
27
- 'igshid', // instagram
28
- 'ttclid', // tiktok
29
- 'rdt_cid', // reddit
30
- 'epik', // pinterest
31
- 'qclid', // quora
32
- 'sccid', // snapchat
33
- 'irclid', // impact
34
- '_kx', // klaviyo
35
- ]
36
-
37
- export const CAMPAIGN_PARAMS = extendArray(
38
- [
39
- 'utm_source',
40
- 'utm_medium',
41
- 'utm_campaign',
42
- 'utm_content',
43
- 'utm_term',
44
- 'gad_source', // google ads source
45
- 'mc_cid', // mailchimp campaign id
46
- ],
47
- PERSONAL_DATA_CAMPAIGN_PARAMS
48
- )
49
-
50
- export const EVENT_TO_PERSON_PROPERTIES = [
51
- // mobile params
52
- '$app_build',
53
- '$app_name',
54
- '$app_namespace',
55
- '$app_version',
56
- // web params
57
- '$browser',
58
- '$browser_version',
59
- '$device_type',
60
- '$current_url',
61
- '$pathname',
62
- '$os',
63
- '$os_name', // $os_name is a special case, it's treated as an alias of $os!
64
- '$os_version',
65
- '$referring_domain',
66
- '$referrer',
67
- '$screen_height',
68
- '$screen_width',
69
- '$viewport_height',
70
- '$viewport_width',
71
- '$raw_user_agent',
72
- ]
73
-
74
- export const MASKED = '<masked>'
75
-
76
- // Campaign params that can be read from the cookie store
77
- export const COOKIE_CAMPAIGN_PARAMS = [
78
- 'li_fat_id', // linkedin
79
- ]
80
-
81
- export function getCampaignParams(
82
- customTrackedParams?: string[],
83
- maskPersonalDataProperties?: boolean,
84
- customPersonalDataProperties?: string[] | undefined
85
- ): Record<string, string> {
86
- if (!document) {
87
- return {}
88
- }
89
-
90
- const paramsToMask = maskPersonalDataProperties
91
- ? extendArray([], PERSONAL_DATA_CAMPAIGN_PARAMS, customPersonalDataProperties || [])
92
- : []
93
-
94
- // Initially get campaign params from the URL
95
- const urlCampaignParams = _getCampaignParamsFromUrl(
96
- maskQueryParams(document.URL, paramsToMask, MASKED),
97
- customTrackedParams
98
- )
99
-
100
- // But we can also get some of them from the cookie store
101
- // For example: https://learn.microsoft.com/en-us/linkedin/marketing/conversions/enabling-first-party-cookies?view=li-lms-2025-05#reading-li_fat_id-from-cookies
102
- const cookieCampaignParams = _getCampaignParamsFromCookie()
103
-
104
- // Prefer the values found in the urlCampaignParams if possible
105
- // `extend` will override the values if found in the second argument
106
- return extend(cookieCampaignParams, urlCampaignParams)
107
- }
108
-
109
- function _getCampaignParamsFromUrl(url: string, customParams?: string[]): Record<string, string> {
110
- const campaign_keywords = CAMPAIGN_PARAMS.concat(customParams || [])
111
-
112
- const params: Record<string, any> = {}
113
- each(campaign_keywords, function (kwkey) {
114
- const kw = getQueryParam(url, kwkey)
115
- params[kwkey] = kw ? kw : null
116
- })
117
-
118
- return params
119
- }
120
-
121
- function _getCampaignParamsFromCookie(): Record<string, string> {
122
- const params: Record<string, any> = {}
123
- each(COOKIE_CAMPAIGN_PARAMS, function (kwkey) {
124
- const kw = cookieStore._get(kwkey)
125
- params[kwkey] = kw ? kw : null
126
- })
127
-
128
- return params
129
- }
130
-
131
- function _getSearchEngine(referrer: string): string | null {
132
- if (!referrer) {
133
- return null
134
- } else {
135
- if (referrer.search(URL_REGEX_PREFIX + 'google.([^/?]*)') === 0) {
136
- return 'google'
137
- } else if (referrer.search(URL_REGEX_PREFIX + 'bing.com') === 0) {
138
- return 'bing'
139
- } else if (referrer.search(URL_REGEX_PREFIX + 'yahoo.com') === 0) {
140
- return 'yahoo'
141
- } else if (referrer.search(URL_REGEX_PREFIX + 'duckduckgo.com') === 0) {
142
- return 'duckduckgo'
143
- } else {
144
- return null
145
- }
146
- }
147
- }
148
-
149
- function _getSearchInfoFromReferrer(referrer: string): Record<string, any> {
150
- const search = _getSearchEngine(referrer)
151
- const param = search != 'yahoo' ? 'q' : 'p'
152
- const ret: Record<string, any> = {}
153
-
154
- if (!isNull(search)) {
155
- ret['$search_engine'] = search
156
-
157
- const keyword = document ? getQueryParam(document.referrer, param) : ''
158
- if (keyword.length) {
159
- ret['ph_keyword'] = keyword
160
- }
161
- }
162
-
163
- return ret
164
- }
165
-
166
- export function getSearchInfo(): Record<string, any> {
167
- const referrer = document?.referrer
168
- if (!referrer) {
169
- return {}
170
- }
171
- return _getSearchInfoFromReferrer(referrer)
172
- }
173
-
174
- export function getBrowserLanguage(): string | undefined {
175
- return (
176
- navigator.language || // Any modern browser
177
- (navigator as Record<string, any>).userLanguage // IE11
178
- )
179
- }
180
-
181
- export function getBrowserLanguagePrefix(): string | undefined {
182
- const lang = getBrowserLanguage()
183
- return typeof lang === 'string' ? lang.split('-')[0] : undefined
184
- }
185
-
186
- export function getReferrer(): string {
187
- return document?.referrer || '$direct'
188
- }
189
-
190
- export function getReferringDomain(): string {
191
- if (!document?.referrer) {
192
- return '$direct'
193
- }
194
- return convertToURL(document.referrer)?.host || '$direct'
195
- }
196
-
197
- export function getReferrerInfo(): Record<string, any> {
198
- return {
199
- $referrer: getReferrer(),
200
- $referring_domain: getReferringDomain(),
201
- }
202
- }
203
-
204
- export function getPersonInfo(maskPersonalDataProperties?: boolean, customPersonalDataProperties?: string[]) {
205
- const paramsToMask = maskPersonalDataProperties
206
- ? extendArray([], PERSONAL_DATA_CAMPAIGN_PARAMS, customPersonalDataProperties || [])
207
- : []
208
- const url = location?.href.substring(0, 1000)
209
- // we're being a bit more economical with bytes here because this is stored in the cookie
210
- return {
211
- r: getReferrer().substring(0, 1000),
212
- u: url ? maskQueryParams(url, paramsToMask, MASKED) : undefined,
213
- }
214
- }
215
-
216
- export function getPersonPropsFromInfo(info: Record<string, any>): Record<string, any> {
217
- const { r: referrer, u: url } = info
218
- const referring_domain =
219
- referrer == null ? undefined : referrer == '$direct' ? '$direct' : convertToURL(referrer)?.host
220
-
221
- const props: Record<string, string | undefined> = {
222
- $referrer: referrer,
223
- $referring_domain: referring_domain,
224
- }
225
- if (url) {
226
- props['$current_url'] = url
227
- const location = convertToURL(url)
228
- props['$host'] = location?.host
229
- props['$pathname'] = location?.pathname
230
- const campaignParams = _getCampaignParamsFromUrl(url)
231
- extend(props, campaignParams)
232
- }
233
- if (referrer) {
234
- const searchInfo = _getSearchInfoFromReferrer(referrer)
235
- extend(props, searchInfo)
236
- }
237
- return props
238
- }
239
-
240
- export function getInitialPersonPropsFromInfo(info: Record<string, any>): Record<string, any> {
241
- const personProps = getPersonPropsFromInfo(info)
242
- const props: Record<string, any> = {}
243
- each(personProps, function (val: any, key: string) {
244
- props[`$initial_${stripLeadingDollar(key)}`] = val
245
- })
246
- return props
247
- }
248
-
249
- export function getTimezone(): string | undefined {
250
- try {
251
- return Intl.DateTimeFormat().resolvedOptions().timeZone
252
- } catch {
253
- return undefined
254
- }
255
- }
256
-
257
- export function getTimezoneOffset(): number | undefined {
258
- try {
259
- return new Date().getTimezoneOffset()
260
- } catch {
261
- return undefined
262
- }
263
- }
264
-
265
- export function getEventProperties(
266
- maskPersonalDataProperties?: boolean,
267
- customPersonalDataProperties?: string[]
268
- ): Properties {
269
- if (!userAgent) {
270
- return {}
271
- }
272
- const paramsToMask = maskPersonalDataProperties
273
- ? extendArray([], PERSONAL_DATA_CAMPAIGN_PARAMS, customPersonalDataProperties || [])
274
- : []
275
- const [os_name, os_version] = detectOS(userAgent)
276
- return extend(
277
- stripEmptyProperties({
278
- $os: os_name,
279
- $os_version: os_version,
280
- $browser: detectBrowser(userAgent, navigator.vendor),
281
- $device: detectDevice(userAgent),
282
- $device_type: detectDeviceType(userAgent),
283
- $timezone: getTimezone(),
284
- $timezone_offset: getTimezoneOffset(),
285
- }),
286
- {
287
- $current_url: maskQueryParams(location?.href, paramsToMask, MASKED),
288
- $host: location?.host,
289
- $pathname: location?.pathname,
290
- $raw_user_agent: userAgent.length > 1000 ? userAgent.substring(0, 997) + '...' : userAgent,
291
- $browser_version: detectBrowserVersion(userAgent, navigator.vendor),
292
- $browser_language: getBrowserLanguage(),
293
- $browser_language_prefix: getBrowserLanguagePrefix(),
294
- $screen_height: window?.screen.height,
295
- $screen_width: window?.screen.width,
296
- $viewport_height: window?.innerHeight,
297
- $viewport_width: window?.innerWidth,
298
- $lib: 'web',
299
- $lib_version: Config.LIB_VERSION,
300
- $insert_id: Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10),
301
- $time: Date.now() / 1000, // epoch time in seconds
302
- }
303
- )
304
- }
@@ -1,222 +0,0 @@
1
- import { isFormData, isNull, isNullish, hasOwnProperty, isArray, isString, isNumber } from '@posthog/core'
2
- import { Properties } from '../types'
3
-
4
- const breaker: Breaker = {}
5
-
6
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
7
- export type Breaker = {}
8
-
9
- export const ArrayProto = Array.prototype
10
- export const nativeForEach = ArrayProto.forEach
11
- export const nativeIndexOf = ArrayProto.indexOf
12
-
13
- const win: (Window & typeof globalThis) | undefined = typeof window !== 'undefined' ? window : undefined
14
- const global: typeof globalThis | undefined = typeof globalThis !== 'undefined' ? globalThis : win
15
- export const navigator = global?.navigator
16
- export const document = global?.document
17
- export const location = global?.location
18
- export const fetch = global?.fetch
19
- export const XMLHttpRequest =
20
- global?.XMLHttpRequest && 'withCredentials' in new global.XMLHttpRequest() ? global.XMLHttpRequest : undefined
21
- export const AbortController = global?.AbortController
22
- export const userAgent = navigator?.userAgent
23
- export { win as window }
24
-
25
- export function eachArray<E = any>(
26
- obj: E[] | null | undefined,
27
- iterator: (value: E, key: number) => void | Breaker,
28
- thisArg?: any
29
- ): void {
30
- if (isArray(obj)) {
31
- if (nativeForEach && obj.forEach === nativeForEach) {
32
- obj.forEach(iterator, thisArg)
33
- } else if ('length' in obj && obj.length === +obj.length) {
34
- for (let i = 0, l = obj.length; i < l; i++) {
35
- if (i in obj && iterator.call(thisArg, obj[i], i) === breaker) {
36
- return
37
- }
38
- }
39
- }
40
- }
41
- }
42
-
43
- /**
44
- * @param {*=} obj
45
- * @param {function(...*)=} iterator
46
- * @param {Object=} thisArg
47
- */
48
- export function each(obj: any, iterator: (value: any, key: any) => void | Breaker, thisArg?: any): void {
49
- if (isNullish(obj)) {
50
- return
51
- }
52
- if (isArray(obj)) {
53
- return eachArray(obj, iterator, thisArg)
54
- }
55
- if (isFormData(obj)) {
56
- for (const pair of obj.entries()) {
57
- if (iterator.call(thisArg, pair[1], pair[0]) === breaker) {
58
- return
59
- }
60
- }
61
- return
62
- }
63
- for (const key in obj) {
64
- if (hasOwnProperty.call(obj, key)) {
65
- if (iterator.call(thisArg, obj[key], key) === breaker) {
66
- return
67
- }
68
- }
69
- }
70
- }
71
-
72
- export const extend = function (obj: Record<string, any>, ...args: Record<string, any>[]): Record<string, any> {
73
- eachArray(args, function (source) {
74
- for (const prop in source) {
75
- if (source[prop] !== void 0) {
76
- obj[prop] = source[prop]
77
- }
78
- }
79
- })
80
- return obj
81
- }
82
-
83
- export const extendArray = function <T>(obj: T[], ...args: T[][]): T[] {
84
- eachArray(args, function (source) {
85
- eachArray(source, function (item) {
86
- obj.push(item)
87
- })
88
- })
89
- return obj
90
- }
91
-
92
- export const include = function (
93
- obj: null | string | Array<any> | Record<string, any>,
94
- target: any
95
- ): boolean | Breaker {
96
- let found = false
97
- if (isNull(obj)) {
98
- return found
99
- }
100
- if (nativeIndexOf && obj.indexOf === nativeIndexOf) {
101
- return obj.indexOf(target) != -1
102
- }
103
- each(obj, function (value) {
104
- if (found || (found = value === target)) {
105
- return breaker
106
- }
107
- return
108
- })
109
- return found
110
- }
111
-
112
- /**
113
- * Object.entries() polyfill
114
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
115
- */
116
- export function entries<T = any>(obj: Record<string, T>): [string, T][] {
117
- const ownProps = Object.keys(obj)
118
- let i = ownProps.length
119
- const resArray = new Array(i) // preallocate the Array
120
-
121
- while (i--) {
122
- resArray[i] = [ownProps[i], obj[ownProps[i]]]
123
- }
124
- return resArray
125
- }
126
-
127
- export function addEventListener(
128
- element: Window | Document | Element | undefined,
129
- event: string,
130
- callback: EventListener,
131
- options?: AddEventListenerOptions
132
- ): void {
133
- const { capture = false, passive = true } = options ?? {}
134
-
135
- // This is the only place where we are allowed to call this function
136
- // because the whole idea is that we should be calling this instead of the built-in one
137
- // eslint-disable-next-line posthog-js/no-add-event-listener
138
- element?.addEventListener(event, callback, { capture, passive })
139
- }
140
-
141
- export const stripEmptyProperties = function (p: Properties): Properties {
142
- const ret: Properties = {}
143
- each(p, function (v, k) {
144
- if ((isString(v) && v.length > 0) || isNumber(v)) {
145
- ret[k] = v
146
- }
147
- })
148
- return ret
149
- }
150
-
151
- const EXCLUDED_FROM_CROSS_SUBDOMAIN_COOKIE = ['herokuapp.com', 'vercel.app', 'netlify.app']
152
- export function isCrossDomainCookie(documentLocation: Location | undefined) {
153
- const hostname = documentLocation?.hostname
154
-
155
- if (!isString(hostname)) {
156
- return false
157
- }
158
- // split and slice isn't a great way to match arbitrary domains,
159
- // but it's good enough for ensuring we only match herokuapp.com when it is the TLD
160
- // for the hostname
161
- const lastTwoParts = hostname.split('.').slice(-2).join('.')
162
-
163
- for (const excluded of EXCLUDED_FROM_CROSS_SUBDOMAIN_COOKIE) {
164
- if (lastTwoParts === excluded) {
165
- return false
166
- }
167
- }
168
-
169
- return true
170
- }
171
-
172
- /**
173
- * Deep copies an object.
174
- * It handles cycles by replacing all references to them with `undefined`
175
- * Also supports customizing native values
176
- *
177
- * @param value
178
- * @param customizer
179
- * @returns {{}|undefined|*}
180
- */
181
- function deepCircularCopy<T extends Record<string, any> = Record<string, any>>(
182
- value: T,
183
- customizer?: <K extends keyof T = keyof T>(value: T[K], key?: K) => T[K]
184
- ): T | undefined {
185
- const COPY_IN_PROGRESS_SET = new Set()
186
-
187
- function internalDeepCircularCopy(value: T, key?: string): T | undefined {
188
- if (value !== Object(value)) return customizer ? customizer(value as any, key) : value // primitive value
189
-
190
- if (COPY_IN_PROGRESS_SET.has(value)) return undefined
191
- COPY_IN_PROGRESS_SET.add(value)
192
- let result: T
193
-
194
- if (isArray(value)) {
195
- result = [] as any as T
196
- eachArray(value, (it) => {
197
- result.push(internalDeepCircularCopy(it))
198
- })
199
- } else {
200
- result = {} as T
201
- each(value, (val, key) => {
202
- if (!COPY_IN_PROGRESS_SET.has(val)) {
203
- ;(result as any)[key] = internalDeepCircularCopy(val, key)
204
- }
205
- })
206
- }
207
- return result
208
- }
209
- return internalDeepCircularCopy(value)
210
- }
211
-
212
- export function copyAndTruncateStrings<T extends Record<string, any> = Record<string, any>>(
213
- object: T,
214
- maxStringLength: number | null
215
- ): T {
216
- return deepCircularCopy(object, (value: any) => {
217
- if (isString(value) && !isNull(maxStringLength)) {
218
- return (value as string).slice(0, maxStringLength)
219
- }
220
- return value
221
- }) as T
222
- }