posthog-js-lite 4.1.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/context.ts DELETED
@@ -1,170 +0,0 @@
1
- import { utils } from 'posthog-core'
2
- import { version } from '../package.json'
3
-
4
- export function getContext(window: Window | undefined): any {
5
- let context = {}
6
- if (window?.navigator) {
7
- const userAgent = window.navigator.userAgent
8
- const osValue = os(window)
9
- context = {
10
- ...context,
11
- ...(osValue !== undefined && { $os: osValue }),
12
- $browser: browser(userAgent, window.navigator.vendor, !!(window as any).opera),
13
- $referrer: window.document.referrer,
14
- $referring_domain: referringDomain(window.document.referrer),
15
- $device: device(userAgent),
16
- $current_url: window.location.href,
17
- $host: window.location.host,
18
- $pathname: window.location.pathname,
19
- $browser_version: browserVersion(userAgent, window.navigator.vendor, !!(window as any).opera),
20
- $screen_height: window.screen.height,
21
- $screen_width: window.screen.width,
22
- $screen_dpr: window.devicePixelRatio,
23
- }
24
- }
25
-
26
- context = {
27
- ...context,
28
- $lib: 'js',
29
- $lib_version: version,
30
- $insert_id: Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10),
31
- $time: utils.currentTimestamp() / 1000, // epoch time in seconds
32
- }
33
- return context // TODO: strip empty props?
34
- }
35
-
36
- function includes(haystack: string, needle: string): boolean {
37
- return haystack.indexOf(needle) >= 0
38
- }
39
-
40
- function browser(userAgent: string, vendor: string, opera: boolean): string {
41
- vendor = vendor || '' // vendor is undefined for at least IE9
42
- if (opera || includes(userAgent, ' OPR/')) {
43
- if (includes(userAgent, 'Mini')) {
44
- return 'Opera Mini'
45
- }
46
- return 'Opera'
47
- } else if (/(BlackBerry|PlayBook|BB10)/i.test(userAgent)) {
48
- return 'BlackBerry'
49
- } else if (includes(userAgent, 'IEMobile') || includes(userAgent, 'WPDesktop')) {
50
- return 'Internet Explorer Mobile'
51
- } else if (includes(userAgent, 'SamsungBrowser/')) {
52
- // https://developer.samsung.com/internet/user-agent-string-format
53
- return 'Samsung Internet'
54
- } else if (includes(userAgent, 'Edge') || includes(userAgent, 'Edg/')) {
55
- return 'Microsoft Edge'
56
- } else if (includes(userAgent, 'FBIOS')) {
57
- return 'Facebook Mobile'
58
- } else if (includes(userAgent, 'Chrome')) {
59
- return 'Chrome'
60
- } else if (includes(userAgent, 'CriOS')) {
61
- return 'Chrome iOS'
62
- } else if (includes(userAgent, 'UCWEB') || includes(userAgent, 'UCBrowser')) {
63
- return 'UC Browser'
64
- } else if (includes(userAgent, 'FxiOS')) {
65
- return 'Firefox iOS'
66
- } else if (includes(vendor, 'Apple')) {
67
- if (includes(userAgent, 'Mobile')) {
68
- return 'Mobile Safari'
69
- }
70
- return 'Safari'
71
- } else if (includes(userAgent, 'Android')) {
72
- return 'Android Mobile'
73
- } else if (includes(userAgent, 'Konqueror')) {
74
- return 'Konqueror'
75
- } else if (includes(userAgent, 'Firefox')) {
76
- return 'Firefox'
77
- } else if (includes(userAgent, 'MSIE') || includes(userAgent, 'Trident/')) {
78
- return 'Internet Explorer'
79
- } else if (includes(userAgent, 'Gecko')) {
80
- return 'Mozilla'
81
- } else {
82
- return ''
83
- }
84
- }
85
-
86
- function browserVersion(userAgent: string, vendor: string, opera: boolean): number | null {
87
- const regexList = {
88
- 'Internet Explorer Mobile': /rv:(\d+(\.\d+)?)/,
89
- 'Microsoft Edge': /Edge?\/(\d+(\.\d+)?)/,
90
- Chrome: /Chrome\/(\d+(\.\d+)?)/,
91
- 'Chrome iOS': /CriOS\/(\d+(\.\d+)?)/,
92
- 'UC Browser': /(UCBrowser|UCWEB)\/(\d+(\.\d+)?)/,
93
- Safari: /Version\/(\d+(\.\d+)?)/,
94
- 'Mobile Safari': /Version\/(\d+(\.\d+)?)/,
95
- Opera: /(Opera|OPR)\/(\d+(\.\d+)?)/,
96
- Firefox: /Firefox\/(\d+(\.\d+)?)/,
97
- 'Firefox iOS': /FxiOS\/(\d+(\.\d+)?)/,
98
- Konqueror: /Konqueror:(\d+(\.\d+)?)/,
99
- BlackBerry: /BlackBerry (\d+(\.\d+)?)/,
100
- 'Android Mobile': /android\s(\d+(\.\d+)?)/,
101
- 'Samsung Internet': /SamsungBrowser\/(\d+(\.\d+)?)/,
102
- 'Internet Explorer': /(rv:|MSIE )(\d+(\.\d+)?)/,
103
- Mozilla: /rv:(\d+(\.\d+)?)/,
104
- }
105
-
106
- const browserString = browser(userAgent, vendor, opera) as keyof typeof regexList
107
- const regex: RegExp = regexList[browserString] || undefined
108
-
109
- if (regex === undefined) {
110
- return null
111
- }
112
- const matches = userAgent.match(regex)
113
- if (!matches) {
114
- return null
115
- }
116
- return parseFloat(matches[matches.length - 2])
117
- }
118
-
119
- function os(window: Window | undefined): string | undefined {
120
- if (!window?.navigator) {
121
- return undefined
122
- }
123
- const a = window.navigator.userAgent
124
- if (/Windows/i.test(a)) {
125
- if (/Phone/.test(a) || /WPDesktop/.test(a)) {
126
- return 'Windows Phone'
127
- }
128
- return 'Windows'
129
- } else if (/(iPhone|iPad|iPod)/.test(a)) {
130
- return 'iOS'
131
- } else if (/Android/.test(a)) {
132
- return 'Android'
133
- } else if (/(BlackBerry|PlayBook|BB10)/i.test(a)) {
134
- return 'BlackBerry'
135
- } else if (/Mac/i.test(a)) {
136
- return 'Mac OS X'
137
- } else if (/Linux/.test(a)) {
138
- return 'Linux'
139
- } else if (/CrOS/.test(a)) {
140
- return 'Chrome OS'
141
- } else {
142
- return undefined
143
- }
144
- }
145
-
146
- function device(userAgent: string): string {
147
- if (/Windows Phone/i.test(userAgent) || /WPDesktop/.test(userAgent)) {
148
- return 'Windows Phone'
149
- } else if (/iPad/.test(userAgent)) {
150
- return 'iPad'
151
- } else if (/iPod/.test(userAgent)) {
152
- return 'iPod Touch'
153
- } else if (/iPhone/.test(userAgent)) {
154
- return 'iPhone'
155
- } else if (/(BlackBerry|PlayBook|BB10)/i.test(userAgent)) {
156
- return 'BlackBerry'
157
- } else if (/Android/.test(userAgent)) {
158
- return 'Android'
159
- } else {
160
- return ''
161
- }
162
- }
163
-
164
- function referringDomain(referrer: string): string {
165
- const split = referrer.split('/')
166
- if (split.length >= 3) {
167
- return split[2]
168
- }
169
- return ''
170
- }
package/src/patch.ts DELETED
@@ -1,50 +0,0 @@
1
- // import { patch } from 'rrweb/typings/utils'
2
- // copied from: https://github.com/PostHog/posthog-js/blob/main/src/extensions/replay/rrweb-plugins/patch.ts
3
- // which was copied from https://github.com/rrweb-io/rrweb/blob/8aea5b00a4dfe5a6f59bd2ae72bb624f45e51e81/packages/rrweb/src/utils.ts#L129
4
- // which was copied from https://github.com/getsentry/sentry-javascript/blob/b2109071975af8bf0316d3b5b38f519bdaf5dc15/packages/utils/src/object.ts
5
-
6
- // copied from: https://github.com/PostHog/posthog-js/blob/main/react/src/utils/type-utils.ts#L4
7
- export const isFunction = function (f: any): f is (...args: any[]) => any {
8
- return typeof f === 'function'
9
- }
10
-
11
- export function patch(
12
- source: { [key: string]: any },
13
- name: string,
14
- replacement: (...args: unknown[]) => unknown
15
- ): () => void {
16
- try {
17
- if (!(name in source)) {
18
- return () => {
19
- //
20
- }
21
- }
22
-
23
- const original = source[name] as () => unknown
24
- const wrapped = replacement(original)
25
-
26
- // Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
27
- // otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
28
- if (isFunction(wrapped)) {
29
- wrapped.prototype = wrapped.prototype || {}
30
- Object.defineProperties(wrapped, {
31
- __posthog_wrapped__: {
32
- enumerable: false,
33
- value: true,
34
- },
35
- })
36
- }
37
-
38
- source[name] = wrapped
39
-
40
- return () => {
41
- source[name] = original
42
- }
43
- } catch {
44
- return () => {
45
- //
46
- }
47
- // This can throw if multiple fill happens on a global object like XMLHttpRequest
48
- // Fixes https://github.com/getsentry/sentry-javascript/issues/2043
49
- }
50
- }
@@ -1,141 +0,0 @@
1
- import { version } from '../package.json'
2
-
3
- import { PostHogCore, getFetch } from 'posthog-core'
4
- import type {
5
- PostHogEventProperties,
6
- PostHogFetchOptions,
7
- PostHogFetchResponse,
8
- PostHogPersistedProperty,
9
- } from 'posthog-core'
10
-
11
- import { getContext } from './context'
12
- import { PostHogStorage, getStorage } from './storage'
13
- import { PostHogOptions } from './types'
14
- import { patch } from './patch'
15
-
16
- export class PostHog extends PostHogCore {
17
- private _storage: PostHogStorage
18
- private _storageCache: any
19
- private _storageKey: string
20
- private _lastPathname: string = ''
21
-
22
- constructor(apiKey: string, options?: PostHogOptions) {
23
- super(apiKey, options)
24
-
25
- // posthog-js stores options in one object on
26
- this._storageKey = options?.persistence_name ? `ph_${options.persistence_name}` : `ph_${apiKey}_posthog`
27
-
28
- this._storage = getStorage(options?.persistence || 'localStorage', this.getWindow())
29
- this.setupBootstrap(options)
30
-
31
- if (options?.preloadFeatureFlags !== false) {
32
- this.reloadFeatureFlags()
33
- }
34
-
35
- if (options?.captureHistoryEvents && typeof window !== 'undefined') {
36
- this._lastPathname = window?.location?.pathname || ''
37
- this.setupHistoryEventTracking()
38
- }
39
- }
40
-
41
- private getWindow(): Window | undefined {
42
- return typeof window !== 'undefined' ? window : undefined
43
- }
44
-
45
- getPersistedProperty<T>(key: PostHogPersistedProperty): T | undefined {
46
- if (!this._storageCache) {
47
- this._storageCache = JSON.parse(this._storage.getItem(this._storageKey) || '{}') || {}
48
- }
49
-
50
- return this._storageCache[key]
51
- }
52
-
53
- setPersistedProperty<T>(key: PostHogPersistedProperty, value: T | null): void {
54
- if (!this._storageCache) {
55
- this._storageCache = JSON.parse(this._storage.getItem(this._storageKey) || '{}') || {}
56
- }
57
-
58
- if (value === null) {
59
- delete this._storageCache[key]
60
- } else {
61
- this._storageCache[key] = value
62
- }
63
-
64
- this._storage.setItem(this._storageKey, JSON.stringify(this._storageCache))
65
- }
66
-
67
- fetch(url: string, options: PostHogFetchOptions): Promise<PostHogFetchResponse> {
68
- const fetchFn = getFetch()
69
-
70
- if (!fetchFn) {
71
- // error will be handled by the caller (fetchWithRetry)
72
- return Promise.reject(new Error('Fetch API is not available in this environment.'))
73
- }
74
-
75
- return fetchFn(url, options)
76
- }
77
-
78
- getLibraryId(): string {
79
- return 'posthog-js-lite'
80
- }
81
-
82
- getLibraryVersion(): string {
83
- return version
84
- }
85
-
86
- getCustomUserAgent(): void {
87
- return
88
- }
89
-
90
- getCommonEventProperties(): PostHogEventProperties {
91
- return {
92
- ...super.getCommonEventProperties(),
93
- ...getContext(this.getWindow()),
94
- }
95
- }
96
-
97
- private setupHistoryEventTracking(): void {
98
- const window = this.getWindow()
99
- if (!window) {
100
- return
101
- }
102
-
103
- // Old fashioned, we could also use arrow functions but I think the closure for a patch is more reliable
104
- // eslint-disable-next-line @typescript-eslint/no-this-alias
105
- const self = this
106
-
107
- patch(window.history, 'pushState', (originalPushState) => {
108
- return function patchedPushState(this: History, state: any, title: string, url?: string | URL | null): void {
109
- ;(originalPushState as History['pushState']).call(this, state, title, url)
110
- self.captureNavigationEvent('pushState')
111
- }
112
- })
113
-
114
- patch(window.history, 'replaceState', (originalReplaceState) => {
115
- return function patchedReplaceState(this: History, state: any, title: string, url?: string | URL | null): void {
116
- ;(originalReplaceState as History['replaceState']).call(this, state, title, url)
117
- self.captureNavigationEvent('replaceState')
118
- }
119
- })
120
-
121
- // For popstate we need to listen to the event instead of overriding a method
122
- window.addEventListener('popstate', () => {
123
- this.captureNavigationEvent('popstate')
124
- })
125
- }
126
-
127
- private captureNavigationEvent(navigationType: 'pushState' | 'replaceState' | 'popstate'): void {
128
- const window = this.getWindow()
129
- if (!window) {
130
- return
131
- }
132
-
133
- const currentPathname = window.location.pathname
134
-
135
- // Only capture pageview if the pathname has changed
136
- if (currentPathname !== this._lastPathname) {
137
- this.capture('$pageview', { navigation_type: navigationType })
138
- this._lastPathname = currentPathname
139
- }
140
- }
141
- }
package/src/storage.ts DELETED
@@ -1,168 +0,0 @@
1
- import { PostHogOptions } from './types'
2
-
3
- export type PostHogStorage = {
4
- getItem: (key: string) => string | null | undefined
5
- setItem: (key: string, value: string) => void
6
- removeItem: (key: string) => void
7
- clear: () => void
8
- getAllKeys: () => readonly string[]
9
- }
10
-
11
- // Methods partially borrowed from quirksmode.org/js/cookies.html
12
- export const cookieStore: PostHogStorage = {
13
- getItem(key) {
14
- try {
15
- const nameEQ = key + '='
16
- const ca = document.cookie.split(';')
17
- for (let i = 0; i < ca.length; i++) {
18
- let c = ca[i]
19
- while (c.charAt(0) == ' ') {
20
- c = c.substring(1, c.length)
21
- }
22
- if (c.indexOf(nameEQ) === 0) {
23
- return decodeURIComponent(c.substring(nameEQ.length, c.length))
24
- }
25
- }
26
- } catch (err) {}
27
- return null
28
- },
29
-
30
- setItem(key: string, value: string) {
31
- try {
32
- const cdomain = '',
33
- expires = '',
34
- secure = ''
35
-
36
- const new_cookie_val = key + '=' + encodeURIComponent(value) + expires + '; path=/' + cdomain + secure
37
- document.cookie = new_cookie_val
38
- } catch (err) {
39
- return
40
- }
41
- },
42
-
43
- removeItem(name) {
44
- try {
45
- cookieStore.setItem(name, '')
46
- } catch (err) {
47
- return
48
- }
49
- },
50
- clear() {
51
- document.cookie = ''
52
- },
53
- getAllKeys() {
54
- const ca = document.cookie.split(';')
55
- const keys = []
56
-
57
- for (let i = 0; i < ca.length; i++) {
58
- let c = ca[i]
59
- while (c.charAt(0) == ' ') {
60
- c = c.substring(1, c.length)
61
- }
62
- keys.push(c.split('=')[0])
63
- }
64
-
65
- return keys
66
- },
67
- }
68
-
69
- const createStorageLike = (store: any): PostHogStorage => {
70
- return {
71
- getItem(key) {
72
- return store.getItem(key)
73
- },
74
-
75
- setItem(key, value) {
76
- store.setItem(key, value)
77
- },
78
-
79
- removeItem(key) {
80
- store.removeItem(key)
81
- },
82
- clear() {
83
- store.clear()
84
- },
85
- getAllKeys() {
86
- const keys = []
87
- for (const key in localStorage) {
88
- keys.push(key)
89
- }
90
- return keys
91
- },
92
- }
93
- }
94
-
95
- const checkStoreIsSupported = (storage: PostHogStorage, key = '__mplssupport__'): boolean => {
96
- try {
97
- const val = 'xyz'
98
- storage.setItem(key, val)
99
- if (storage.getItem(key) !== val) {
100
- return false
101
- }
102
- storage.removeItem(key)
103
- return true
104
- } catch (err) {
105
- return false
106
- }
107
- }
108
-
109
- let localStore: PostHogStorage | undefined = undefined
110
- let sessionStore: PostHogStorage | undefined = undefined
111
-
112
- const createMemoryStorage = (): PostHogStorage => {
113
- const _cache: { [key: string]: any | undefined } = {}
114
-
115
- const store: PostHogStorage = {
116
- getItem(key) {
117
- return _cache[key]
118
- },
119
-
120
- setItem(key, value) {
121
- _cache[key] = value !== null ? value : undefined
122
- },
123
-
124
- removeItem(key) {
125
- delete _cache[key]
126
- },
127
- clear() {
128
- for (const key in _cache) {
129
- delete _cache[key]
130
- }
131
- },
132
- getAllKeys() {
133
- const keys = []
134
- for (const key in _cache) {
135
- keys.push(key)
136
- }
137
- return keys
138
- },
139
- }
140
- return store
141
- }
142
-
143
- export const getStorage = (type: PostHogOptions['persistence'], window: Window | undefined): PostHogStorage => {
144
- if (window) {
145
- if (!localStore) {
146
- const _localStore = createStorageLike(window.localStorage)
147
- localStore = checkStoreIsSupported(_localStore) ? _localStore : undefined
148
- }
149
-
150
- if (!sessionStore) {
151
- const _sessionStore = createStorageLike(window.sessionStorage)
152
- sessionStore = checkStoreIsSupported(_sessionStore) ? _sessionStore : undefined
153
- }
154
- }
155
-
156
- switch (type) {
157
- case 'cookie':
158
- return cookieStore || localStore || sessionStore || createMemoryStorage()
159
- case 'localStorage':
160
- return localStore || sessionStore || createMemoryStorage()
161
- case 'sessionStorage':
162
- return sessionStore || createMemoryStorage()
163
- case 'memory':
164
- return createMemoryStorage()
165
- default:
166
- return createMemoryStorage()
167
- }
168
- }
package/src/types.ts DELETED
@@ -1,8 +0,0 @@
1
- import type { PostHogCoreOptions } from 'posthog-core'
2
-
3
- export type PostHogOptions = {
4
- autocapture?: boolean
5
- persistence?: 'localStorage' | 'sessionStorage' | 'cookie' | 'memory'
6
- persistence_name?: string
7
- captureHistoryEvents?: boolean
8
- } & PostHogCoreOptions