Skip to content

Commit

Permalink
feat: Refactor request to make sure we only use available transports (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored May 8, 2024
1 parent edfb7d4 commit 3c0ecc9
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/extensions/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class Toolbar {
location.hash = state['desiredHash']
} else if (history) {
// second param is unused see https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState
history.replaceState(history.state, "", location.pathname + location.search) // completely remove hash
history.replaceState(history.state, '', location.pathname + location.search) // completely remove hash
} else {
location.hash = '' // clear hash (but leaves # unfortunately)
}
Expand Down
1 change: 0 additions & 1 deletion src/posthog-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ let ENQUEUE_REQUESTS = !SUPPORTS_REQUEST && userAgent?.indexOf('MSIE') === -1 &&

export const defaultConfig = (): PostHogConfig => ({
api_host: 'https://us.i.posthog.com',
api_transport: 'XHR',
ui_host: null,
token: '',
autocapture: true,
Expand Down
86 changes: 55 additions & 31 deletions src/request.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { _base64Encode, each } from './utils'
import { _base64Encode, each, find } from './utils'
import Config from './config'
import { Compression, RequestOptions, RequestResponse } from './types'
import { formDataToQuery } from './utils/request-utils'
Expand All @@ -19,36 +19,6 @@ type EncodedBody = {
body: string | BlobPart
}

// This is the entrypoint. It takes care of sanitizing the options and then calls the appropriate request method.
export const request = (_options: RequestOptions) => {
// Clone the options so we don't modify the original object
const options = { ..._options }
options.timeout = options.timeout || 60000

options.url = extendURLParams(options.url, {
_: new Date().getTime().toString(),
ver: Config.LIB_VERSION,
compression: options.compression,
})

if (options.transport === 'sendBeacon' && navigator?.sendBeacon) {
return _sendBeacon(options)
}

// NOTE: Until we are confident with it, we only use fetch if explicitly told so
// At some point we will make it the default over xhr
if (options.transport === 'fetch' && fetch) {
return _fetch(options)
}

if (XMLHttpRequest || !document) {
return xhr(options)
}

// Final fallback if everything else fails...
scriptRequest(options)
}

export const extendURLParams = (url: string, params: Record<string, any>): string => {
const [baseUrl, search] = url.split('?')
const newParams = { ...params }
Expand Down Expand Up @@ -225,3 +195,57 @@ const scriptRequest = (options: RequestOptions) => {
const s = document.getElementsByTagName('script')[0]
s.parentNode?.insertBefore(script, s)
}

const AVAILABLE_TRANSPORTS: { transport: RequestOptions['transport']; method: (options: RequestOptions) => void }[] = []

// We add the transports in order of preference

if (XMLHttpRequest) {
AVAILABLE_TRANSPORTS.push({
transport: 'XHR',
method: xhr,
})
}

if (fetch) {
AVAILABLE_TRANSPORTS.push({
transport: 'fetch',
method: _fetch,
})
}

if (navigator?.sendBeacon) {
AVAILABLE_TRANSPORTS.push({
transport: 'sendBeacon',
method: _sendBeacon,
})
}

AVAILABLE_TRANSPORTS.push({
transport: undefined,
method: scriptRequest,
})

// This is the entrypoint. It takes care of sanitizing the options and then calls the appropriate request method.
export const request = (_options: RequestOptions) => {
// Clone the options so we don't modify the original object
const options = { ..._options }
options.timeout = options.timeout || 60000

options.url = extendURLParams(options.url, {
_: new Date().getTime().toString(),
ver: Config.LIB_VERSION,
compression: options.compression,
})

const transport = options.transport ?? 'XHR'

const transportMethod =
find(AVAILABLE_TRANSPORTS, (t) => t.transport === transport)?.method ?? AVAILABLE_TRANSPORTS[0].method

if (!transportMethod) {
throw new Error('No available transport method')
}

transportMethod(options)
}
9 changes: 9 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,12 @@ export function isCrossDomainCookie(documentLocation: Location | undefined) {
export function isDistinctIdStringLike(value: string): boolean {
return ['distinct_id', 'distinctid'].includes(value.toLowerCase())
}

export function find<T>(value: T[], predicate: (value: T) => boolean): T | undefined {
for (let i = 0; i < value.length; i++) {
if (predicate(value[i])) {
return value[i]
}
}
return undefined
}

0 comments on commit 3c0ecc9

Please sign in to comment.