123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- import * as TE from "fp-ts/TaskEither"
- import * as O from "fp-ts/Option"
- import { pipe } from "fp-ts/function"
- import { AxiosRequestConfig } from "axios"
- import cloneDeep from "lodash/cloneDeep"
- import { NetworkResponse, NetworkStrategy } from "../network"
- import { browserIsChrome, browserIsFirefox } from "../utils/userAgent"
- export const hasExtensionInstalled = () =>
- typeof window.__POSTWOMAN_EXTENSION_HOOK__ !== "undefined"
- export const hasChromeExtensionInstalled = () =>
- hasExtensionInstalled() && browserIsChrome()
- export const hasFirefoxExtensionInstalled = () =>
- hasExtensionInstalled() && browserIsFirefox()
- export const cancelRunningExtensionRequest = () => {
- window.__POSTWOMAN_EXTENSION_HOOK__?.cancelRunningRequest()
- }
- export const defineSubscribableObject = <T extends object>(obj: T) => {
- const proxyObject = {
- ...obj,
- _subscribers: {} as {
- // eslint-disable-next-line no-unused-vars
- [key in keyof T]?: ((...args: any[]) => any)[]
- },
- subscribe(prop: keyof T, func: (...args: any[]) => any): void {
- if (Array.isArray(this._subscribers[prop])) {
- this._subscribers[prop]?.push(func)
- } else {
- this._subscribers[prop] = [func]
- }
- },
- }
- type SubscribableProxyObject = typeof proxyObject
- return new Proxy(proxyObject, {
- set(obj, prop, newVal) {
- obj[prop as keyof SubscribableProxyObject] = newVal
- const currentSubscribers = obj._subscribers[prop as keyof T]
- if (Array.isArray(currentSubscribers)) {
- for (const subscriber of currentSubscribers) {
- subscriber(newVal)
- }
- }
- return true
- },
- })
- }
- const preProcessRequest = (req: AxiosRequestConfig): AxiosRequestConfig => {
- const reqClone = cloneDeep(req)
- // If the parameters are URLSearchParams, inject them to URL instead
- // This prevents marshalling issues with structured cloning of URLSearchParams
- if (reqClone.params instanceof URLSearchParams) {
- try {
- const url = new URL(reqClone.url ?? "")
- for (const [key, value] of reqClone.params.entries()) {
- url.searchParams.append(key, value)
- }
- reqClone.url = url.toString()
- } catch (e) {}
- reqClone.params = {}
- }
- return reqClone
- }
- const extensionStrategy: NetworkStrategy = (req) =>
- pipe(
- TE.Do,
- TE.bind("processedReq", () => TE.of(preProcessRequest(req))),
- // Storeing backup timing data in case the extension does not have that info
- TE.bind("backupTimeDataStart", () => TE.of(new Date().getTime())),
- // Run the request
- TE.bind("response", ({ processedReq }) =>
- pipe(
- window.__POSTWOMAN_EXTENSION_HOOK__,
- O.fromNullable,
- TE.fromOption(() => "NO_PW_EXT_HOOK" as const),
- TE.chain((extensionHook) =>
- TE.tryCatch(
- () =>
- extensionHook.sendRequest({
- ...processedReq,
- wantsBinary: true,
- }),
- (err) => err as any
- )
- )
- )
- ),
- // Inject backup time data if not present
- TE.map(({ backupTimeDataStart, response }) => ({
- ...response,
- config: {
- timeData: {
- startTime: backupTimeDataStart,
- endTime: new Date().getTime(),
- },
- ...response.config,
- },
- })),
- TE.orElse((e) =>
- e !== "cancellation" && e.response
- ? TE.right(e.response as NetworkResponse)
- : TE.left(e)
- )
- )
- export default extensionStrategy
|