@antoinette-agency/sofetch
Version: 
An opinionated Fetch wrapper for JSON APIs
4 lines • 66.1 kB
Source Map (JSON)
{
  "version": 3,
  "sources": ["src/cookieTypescriptUtils.ts", "src/soFetchConfig.ts", "src/soFetchPromise.ts", "src/sleep.ts", "src/getPayloadType.ts", "src/handleHttpErrors.ts", "src/transformRequest.ts", "src/handleBeforeFetchSend.ts", "src/soFetch.ts", "src/httpStatus.ts", "src/index.ts"],
  "sourcesContent": ["export function getCookie(name: string, documentCookie?:string) {\r\n    if (typeof(document) === \"undefined\" && !documentCookie) {\r\n        return;\r\n    }\r\n    const value = documentCookie || document.cookie;\r\n    const cookies = value.split(\"; \");\r\n    const cookieEntries = cookies.map(c => {\r\n        const parts = c.split(\"=\")\r\n        return {\r\n            key:parts[0],\r\n            value:parts[1]\r\n        }\r\n    })\r\n    const cookie = cookieEntries.find(x => x.key === name)\r\n    return cookie?.value\r\n}\r\n", "import {ErrorHandlerDict} from \"./errorHandlerDict.ts\";\r\nimport {SoFetchRequest} from \"./soFetch.ts\";\r\nimport {getCookie} from \"./cookieTypescriptUtils.ts\";\r\nimport {AuthTokenStorageType} from \"./authTokenStorageType.ts\";\r\nimport {AuthenticationType} from \"./authenticationType.ts\";\r\n\r\n/**\r\n * Configures all requests for a specific soFetch instance\r\n */\r\nexport class SoFetchConfig {\r\n    private errorHandlers: ErrorHandlerDict = {}\r\n    protected beforeSendHandlers: ((request: SoFetchRequest) => Promise<SoFetchRequest | void> | SoFetchRequest | void)[] = []\r\n    protected beforeFetchSendHandlers: ((request: RequestInit) => Promise<RequestInit | void> | RequestInit | void)[] = []\r\n    protected onRequestCompleteHandlers: ((response: Response, requestData: { duration: number, method: string }) => Promise<void> | void)[] = []\r\n\r\n    /**\r\n     * Specifies how (or if) soFetch should persist and authentication\r\n     */\r\n    public authTokenStorage:AuthTokenStorageType = null\r\n    private inMemoryAuthToken: string = \"\"\r\n\r\n    /**\r\n     * Specifies how soFetch should send authentication credentials to the server\r\n     */\r\n    public authenticationType:AuthenticationType = null\r\n    \r\n    protected getAuthToken = async () => {\r\n        switch (this.authTokenStorage) {\r\n            case null:\r\n                return \"\"\r\n            case \"memory\":\r\n                return this.inMemoryAuthToken\r\n            case \"localStorage\":\r\n                return localStorage?.getItem(this.authenticationKey) || \"\"\r\n            case \"sessionStorage\":\r\n                return sessionStorage?.getItem(this.authenticationKey) || \"\"\r\n            case \"cookie\":\r\n                return getCookie(this.authenticationKey) || \"\"\r\n            default:\r\n                return this.authTokenStorage();\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Use this method to set an auth token after it's been received from a server, typically as \r\n     * the response to a login request\r\n     * @param authToken\r\n     */\r\n    public setAuthToken = (authToken:string) => {\r\n        switch (this.authTokenStorage) {\r\n            case \"memory\":\r\n                this.inMemoryAuthToken = authToken\r\n                break\r\n            case \"localStorage\":\r\n                localStorage.setItem(this.authenticationKey, authToken)\r\n                break\r\n            case \"sessionStorage\":\r\n                sessionStorage.setItem(this.authenticationKey, authToken)\r\n                break\r\n            case \"cookie\":\r\n                document.cookie = `${this.authenticationKey}=${authToken};`\r\n                break\r\n            /*If we're here then authTokenStorage is either null or a custom function so\r\n                there's nothing to do*/\r\n            default:\r\n                break\r\n        }\r\n    }\r\n    \r\n    /**\r\n     * The base URL for all HTTP requests in the instance. If absent this is assumed to be the current base url.\r\n     * If running in Node relative requests without a baseUrl will throw an error.\r\n     */\r\n    baseUrl: string = \"\"\r\n\r\n    /**\r\n     * The key which is used if an authentication token is persisted via cookies, localStorage or sessionStorage\r\n     */\r\n    authenticationKey: string = \"SOFETCH_AUTHENTICATION\"\r\n\r\n    /**\r\n     * The key which is used if an authentication token is sent to the server via a custom header\r\n     */\r\n    authHeaderKey: string = \"\"\r\n\r\n    /**\r\n     * The key which is used if an authentication token is sent to the server via the query string\r\n     */\r\n    authQueryStringKey: string = \"\"\r\n\r\n    /**\r\n     * Adds a handler which will be executed on receipt from the server of the specified status code.\r\n     * Multiple handlers will be executed in the order in which they are added. If a request has it's\r\n     * own handler(s) for a given status code the corresponding handlers in the config will not be executed.\r\n     * @param status An HTTP status code\r\n     * @param handler A function which accepts a Fetch Response as an argument\r\n     * @example\r\n     *\r\n     *    soFetchConfig.catchHttp(404, (res:Response) => {\r\n     *         alert(\"This object can't be found\")\r\n     *     })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    catchHTTP(status: number, handler: (res: Response) => void) {\r\n        if (!this.errorHandlers[status]) {\r\n            this.errorHandlers[status] = []\r\n        }\r\n        this.errorHandlers[status].push(handler)\r\n    }\r\n    \r\n\r\n    /**\r\n     * Adds a handler which will be executed before every request. beforeSend handlers on the config\r\n     * will be executed before request-specific handlers\r\n     * @param handler\r\n     * @example\r\n     * \r\n     *    soFetch.config.beforeSend((req:SoFetchRequest) => {\r\n     *       console.info(`Sending ${req.method} request to URL ${req.url}`\r\n     *    })\r\n     *    \r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    beforeSend(handler: (request: SoFetchRequest) => Promise<SoFetchRequest | void> | SoFetchRequest | void) {\r\n        this.beforeSendHandlers.push(handler)\r\n    }\r\n\r\n    /**\r\n     * Adds a handler which will be executed before every request. beforeSend handlers on the config\r\n     * will be executed before request-specific handlers\r\n     * @param handler\r\n     * @example\r\n     *\r\n     *    soFetch.config.beforeSend((req:SoFetchRequest) => {\r\n     *       console.info(`Sending ${req.method} request to URL ${req.url}`\r\n     *    })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    beforeFetchSend(handler: (request: RequestInit) => Promise<RequestInit | void> | RequestInit | void) {\r\n        this.beforeFetchSendHandlers.push(handler)\r\n    }\r\n\r\n    /**\r\n     * Adds a handler which will be executed after every request. Handlers will fire regardless of whether\r\n     * the response status code indicated an error\r\n     * @param handler\r\n     * @example\r\n     *\r\n     *    soFetch.config.onRequestComplete((r: Response) => {\r\n     *       console.info(`Response received from ${r.url} with status ${r.status}`\r\n     *    })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    onRequestComplete(handler: (r: Response, metaData: { duration: number, method: string }) => void | Promise<void>) {\r\n        this.onRequestCompleteHandlers.push(handler)\r\n    }\r\n    \r\n    private setAuthTokenStorage = (authTokenStorage?:AuthTokenStorageType) => {\r\n        if (authTokenStorage) {\r\n            this.authTokenStorage = authTokenStorage\r\n            return\r\n        }\r\n        //If null is explicitly passed rather than the argument being undefined:\r\n        if (authTokenStorage === null) {\r\n            this.authTokenStorage = null\r\n        }\r\n        //Default is memory:\r\n        this.authTokenStorage = \"memory\"\r\n    }\r\n\r\n    /**\r\n     * Tells soFetch to use [bearer authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Authentication#bearer) to send an authentication token to the server\r\n     * @param authToken - optional. Use this if you have already obtained a token from a login process. Typically this would be left undefined for bearer authentication as the token is usually obtained from a login process.\r\n     * @param authenticationKey - optional. Specify an authentication key if you don't want to use the default: 'SOFETCH_AUTHENTICATION'\r\n     * @param authTokenStorage - optional, defaults to 'localStorage' on the browser and 'memory' in Node\r\n     */\r\n    useBearerAuthentication(props: {\r\n        authenticationKey?: string,\r\n        authTokenStorage?: AuthTokenStorageType,\r\n        authToken?: string,\r\n    } = {}) {\r\n        let { authenticationKey, authTokenStorage, authToken } = props\r\n        this.authenticationType = \"bearer\"\r\n        if (authenticationKey) {\r\n            this.authenticationKey = authenticationKey\r\n        }\r\n        if (authTokenStorage === undefined) {\r\n            authTokenStorage =  typeof(document) === \"undefined\" ? \"memory\" : \"localStorage\"\r\n        }\r\n        this.setAuthTokenStorage(authTokenStorage)\r\n        if (authToken) {\r\n            this.setAuthToken(authToken)\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Tells soFetch to authenticate using cookies.\r\n     * @param authToken - optional. Use this if you have already obtained a token. Typically this would be left undefined for bearer authentication as the token is usually obtained from a login process.\r\n     * @param authenticationKey - optional. Specify an authentication key if you don't want to use the default: 'SOFETCH_AUTHENTICATION'\r\n     */\r\n    useCookieAuthentication(props?:{\r\n        authenticationKey?:string,\r\n        authToken?:string,\r\n    }|undefined) {\r\n        this.authenticationType = \"cookies\"\r\n        let authenticationKey, authToken\r\n        if (props) {\r\n            authenticationKey = props.authenticationKey\r\n            authToken = props.authToken\r\n        }\r\n        if (authenticationKey) {\r\n            this.authenticationKey = authenticationKey\r\n        }\r\n        \r\n        //If we're in Node we'll simulate cookies in memory.\r\n        this.authTokenStorage =  typeof(document) === \"undefined\" ? \"memory\" : \"cookie\"\r\n        if (authToken) {\r\n            this.setAuthToken(authToken)\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Tells soFetch to send an authentication token to the server \r\n     * @param headerKey - required. The key of the header with which to send the authentication token\r\n     * @param authToken - optional. Use this if you have already obtained a token.\r\n     * @param authenticationKey - optional. Specify an authentication key if you don't want to use the default: 'SOFETCH_AUTHENTICATION'\r\n     * @param authTokenStorage - optional, defaults to 'localStorage' on the browser and 'memory' in Node\r\n     */\r\n    useHeaderAuthentication({headerKey, authToken, authenticationKey, authTokenStorage}:{\r\n        headerKey:string,\r\n        authenticationKey?:string,\r\n        authToken?:string,\r\n        authTokenStorage?:AuthTokenStorageType\r\n    }) {\r\n        this.authenticationType = \"header\"\r\n        this.authHeaderKey = headerKey\r\n        if (authenticationKey) {\r\n            this.authenticationKey = authenticationKey\r\n        }\r\n        this.setAuthTokenStorage(authTokenStorage)\r\n        if (authToken) {\r\n            this.setAuthToken(authToken)\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Tells soFetch to send append an authentication token to the request query string\r\n     * @param queryStringKey - required. The key of the query string item with which to send the authentication token\r\n     * @param authToken - optional. Use this if you have already obtained a token.\r\n     * @param authenticationKey - optional. Specify an authentication key if you don't want to use the default: 'SOFETCH_AUTHENTICATION'\r\n     * @param authTokenStorage - optional. Defaults to 'localStorage' on the browser and 'memory' in Node\r\n     */\r\n    useQueryStringAuthentication({queryStringKey, authToken, authenticationKey, authTokenStorage}:{\r\n        queryStringKey:string,\r\n        authenticationKey?:string,\r\n        authToken?:string,\r\n        authTokenStorage?:AuthTokenStorageType\r\n    }) {\r\n        this.authenticationType = \"queryString\"\r\n        this.authQueryStringKey = queryStringKey\r\n        if (authenticationKey) {\r\n            this.authenticationKey = authenticationKey\r\n        }\r\n        this.setAuthTokenStorage(authTokenStorage)\r\n        if (authToken) {\r\n            this.setAuthToken(authToken)\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Tells soFetch to use [basic authorization](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Authentication#basic) when communicating with the server\r\n     * @param username - optional but required is password is used. Use this if you've already obtained a username and password.\r\n     * @param password - optional but required is username is used. Use this if you've already obtained a username and password.\r\n     * @param authenticationKey - optional. Specify an authentication key if you don't want to use the default: 'SOFETCH_AUTHENTICATION'\r\n     * @param authTokenStorage - optional, defaults to 'localStorage' on the browser and 'memory' in Node\r\n     */\r\n    useBasicAuthentication({username, password, authenticationKey, authTokenStorage}:{\r\n        username?:string, \r\n        password?:string,\r\n        authenticationKey?:string,\r\n        authTokenStorage?:AuthTokenStorageType\r\n    }) {\r\n        this.authenticationType = \"basic\"\r\n        if ((username && !password) || (password && !username)) {\r\n            console.warn(\"Was expecting both username and password to be set for soFetch.config.useBasicAuthentication. Continuing but authentication may not behave as expected\")\r\n        }\r\n        if (authenticationKey) {\r\n            this.authenticationKey = authenticationKey\r\n        }\r\n        this.setAuthTokenStorage(authTokenStorage)\r\n        if (username && password) {\r\n            this.setBasicAuthCredentials({username, password})\r\n        }\r\n    }\r\n    \r\n    private setBasicAuthCredentials({username, password}: {password: string; username: string}) {\r\n        const token = btoa(`${username}:${password}`);\r\n        this.setAuthToken(token)\r\n    }\r\n}\r\n", "import {ErrorHandlerDict} from \"./errorHandlerDict.ts\";\r\nimport {SoFetchRequest} from \"./soFetch.ts\";\r\nimport {HttpStatus} from \"./httpStatus.ts\";\r\n\r\n/**\r\n * An awaitable promise-like class that additionally allows event and error handlers to be attached to the HTTP request\r\n * @example\r\n * \r\n *    const unicorn = await soFetch(\"https://unicorns.com/1234\")\r\n *      .beforeSend(req:SoFetchRequest) => {\r\n *          console.info(`Finding my unicorn at ${req.url}`)\r\n *       })\r\n *      .catchHttp(Status.NotFound404, (res:Response) => {\r\n *         console.error(\"This unicorn can't be found\")\r\n *       })\r\n */\r\nexport class SoFetchPromise<T> {\r\n    private readonly inner: Promise<T>;\r\n    errorHandlers:ErrorHandlerDict = {}\r\n    beforeSendHandlers:((request:SoFetchRequest) => Promise<SoFetchRequest | void> | SoFetchRequest | void)[] = []\r\n    beforeFetchSendHandlers:((init:RequestInit) => Promise<RequestInit | void> | RequestInit | void)[] = []\r\n    onRequestCompleteHandlers: ((response: Response, requestData: { duration: number, method: string }) => void | Promise<void>)[] = []\r\n    timeout: number = 30000\r\n    then: Promise<T>[\"then\"];\r\n    catch: Promise<T>[\"catch\"];\r\n    finally: Promise<T>[\"finally\"];\r\n    \r\n    constructor(executor: (\r\n        resolve: (value: T | PromiseLike<T>) => void,\r\n        reject: (reason?: any) => void\r\n    ) => void) {\r\n        this.inner = new Promise(executor);\r\n        // Bind promise methods once inner exists\r\n        this.then = this.inner.then.bind(this.inner);\r\n        this.catch = this.inner.catch.bind(this.inner);\r\n        this.finally = this.inner.finally.bind(this.inner);\r\n    }\r\n\r\n    /**\r\n     * Adds a handler which will be executed after this HTTP request is completed. Handlers will fire regardless of whether\r\n     * the response status code indicated an error\r\n     * @param handler\r\n     * @example\r\n     *\r\n     *    await soFetch(\"https://example.com/users\",{name:\"Sarah\", id:1234}).onRequestComplete((r: Response) => {\r\n     *       console.info(`Response received from ${r.url} with status ${r.status}`\r\n     *    })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    onRequestComplete(handler: (response: Response) => void | Promise<void>): SoFetchPromise<T> {\r\n        this.onRequestCompleteHandlers.push(handler)\r\n        return this\r\n    }\r\n\r\n    /**\r\n     * Adds a handler which will be executed before this HTTP request is sent. BeforeSend handlers added here will\r\n     * will be executed after those added on the config.\r\n     * @param handler\r\n     * @example\r\n     *\r\n     *    await soFetch(\"https://example.com/users\",{name:\"Sarah\", id:1234}).beforeSend((req:SoFetchRequest) => {\r\n     *       console.info(`Sending ${req.method} request to URL ${req.url}`\r\n     *    })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    beforeSend(handler: (request: SoFetchRequest) => Promise<SoFetchRequest | void> | SoFetchRequest | void): SoFetchPromise<T> {\r\n        this.beforeSendHandlers.push(handler)\r\n        return this\r\n    }\r\n\r\n    /**\r\n     * Adds a handler which allows developers to modify the low-level fetch RequestInit object before the HTTP\r\n     * request is made. These handlers execute after beforeSend handlers. This is useful for one-off\r\n     * occasions when you need to access some aspect of the low-level Fetch API. If you're using this a lot\r\n     * it might make more sense for you to use the Fetch API directly.\r\n     * @param handler\r\n     * @example\r\n     *\r\n     *    //An example of how you might send both files and data in a single request.\r\n     *    const postFilesAndDataResponse = await soFetch.put<PostFilesAndDataResponse>(\"https://example.com/files-and-data\").beforeFetchSend((init:RequestInit) => {\r\n     *       const formData = new FormData()\r\n     *       formData.append(\"company\", \"Antoinette\");\r\n     *       formData.append(\"file1\", myFile)\r\n     *       const headers = {...init.headers} as Record<string,string>\r\n     *       if (headers[\"content-type\"]) {\r\n     *           delete headers[\"content-type\"]\r\n     *       }\r\n     *       init.body = formData\r\n     *       init.headers = headers\r\n     *       return init\r\n     *    })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    beforeFetchSend(handler: (request: RequestInit) => Promise<RequestInit | void> | RequestInit | void): SoFetchPromise<T> {\r\n        this.beforeFetchSendHandlers.push(handler)\r\n        return this\r\n    }\r\n\r\n    /**\r\n     * Adds a handler which will be executed on receipt from the server of the specified status code.\r\n     * Multiple handlers will be executed in the order in which they are added. If you add an error handler\r\n     * for a specific status code here any corresponding handlers in the config will not be executed.\r\n     * @param status An HTTP status code\r\n     * @param handler A function which accepts a Fetch Response as an argument\r\n     * @example\r\n     *\r\n     *    const unicorn = await soFetch(\"https://unicorns.com/1234\")\r\n     *      .catchHttp(404, (res:Response) => {\r\n     *         console.error(\"This unicorn can't be found\")\r\n     *     })\r\n     *\r\n     * @see For more examples see https://sofetch.antoinette.agency\r\n     */\r\n    catchHTTP(status: HttpStatus, handler: (response: Response) => void): SoFetchPromise<T> {\r\n        if (!this.errorHandlers[status]) {\r\n            this.errorHandlers[status] = []\r\n        }\r\n        this.errorHandlers[status].push(handler)\r\n        return this\r\n    }\r\n\r\n    async setTimeout(ms: number) {\r\n        this.timeout = ms\r\n        return this\r\n    }\r\n}\r\n", "export function sleep(ms: number) {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n}", "import {UploadPayload} from \"./uploadPayload.ts\";\r\nimport {FileWithFieldName} from \"./fileWithFieldName.ts\";\r\n\r\nexport const normalisePayload = (payload:UploadPayload):{files?:FileWithFieldName[], jsonPayload?:object} => {\r\n    const {isDefined, isArray, isFiles} = getPayloadType(payload)\r\n    if (!isDefined) {\r\n        return {}\r\n    }\r\n    if (!isFiles) {\r\n        return {jsonPayload:payload}\r\n    }\r\n    const fileArray = (isArray ? payload : [payload]) as (File[] | FileWithFieldName[])\r\n    const files = (isFileWithFieldName(fileArray[0]) ? fileArray : fileArray.map(((x,i) => ({file:x, fieldName:`file${i}`})))) as FileWithFieldName[]\r\n    return {files}\r\n}\r\n\r\nexport const isFileWithFieldName = (v:object) => {\r\n    return 'file' in v && v.file instanceof File\r\n}\r\n\r\nexport const getPayloadType = (payload: UploadPayload): { isDefined: boolean, isArray: boolean, isFiles: boolean } => {\r\n    if (!payload) {\r\n        return {isDefined: false, isArray: false, isFiles: false}\r\n    }\r\n    if (Array.isArray(payload)) {\r\n        if (!payload.length) {\r\n            //For the purposes of the HTTP request a payload which is an empty array is undefined\r\n            //We won't be sending a body with the request.\r\n            return {isDefined: false, isArray: true, isFiles: false}\r\n        }\r\n        if (payload[0] instanceof File) {\r\n            return {isDefined: true, isArray: true, isFiles: true}\r\n        }\r\n        if (isFileWithFieldName(payload[0])) {\r\n            return {isDefined: true, isArray: true, isFiles: true}\r\n        }\r\n        return {isDefined: true, isArray: true, isFiles: false}\r\n    }\r\n    if (payload instanceof File) {\r\n        return {isDefined: true, isArray: false, isFiles: true}\r\n    }\r\n    if (isFileWithFieldName(payload)) {\r\n        return {isDefined: true, isArray: false, isFiles: true}\r\n    }\r\n    return {isDefined: true, isArray: false, isFiles: false}\r\n}", "import {ErrorHandlerDict} from \"./errorHandlerDict.ts\";\r\n\r\nexport const handleHttpErrors = (response: Response, errorHandlers: ErrorHandlerDict) => {\r\n    const status = response.status\r\n    const handled = !!(errorHandlers[status] && errorHandlers[status].length)\r\n    if (handled) {\r\n        errorHandlers[status].forEach(h => h(response))\r\n    }\r\n    return handled\r\n}", "import {SoFetchRequest} from \"./soFetch.ts\";\r\n\r\nexport const transformRequest = async (request: SoFetchRequest, beforeSendHandlers: ((request: SoFetchRequest) => Promise<SoFetchRequest | void> | SoFetchRequest | void)[]) => {\r\n    for(const h of beforeSendHandlers) {\r\n        request = (await h(request)) || request\r\n    }\r\n    return request\r\n}", "export const handleBeforeFetchSend = async (init: RequestInit, handlers: ((init: RequestInit) => Promise<RequestInit | void> | RequestInit | void)[]) => {\r\n    for(const h of handlers) {\r\n        init = (await h(init)) || init\r\n    }\r\n    return init\r\n}", "import {SoFetchConfig} from \"./soFetchConfig.ts\";\r\nimport {SoFetchPromise} from \"./soFetchPromise.ts\";\r\nimport {sleep} from \"./sleep.ts\";\r\nimport {UploadPayload} from \"./uploadPayload.ts\";\r\nimport {normalisePayload} from \"./getPayloadType.ts\";\r\nimport {FileWithFieldName} from \"./fileWithFieldName.ts\";\r\nimport {SoFetchLike} from \"./soFetchLike.ts\";\r\nimport {handleHttpErrors} from \"./handleHttpErrors.ts\";\r\nimport {transformRequest} from \"./transformRequest.ts\";\r\nimport {handleBeforeFetchSend} from \"./handleBeforeFetchSend.ts\";\r\n\r\nasync function addAuthentication(request: SoFetchRequest, config: SoFetchConfig) {\r\n    \r\n    const token = config.authenticationType === null ? \"\" : await config[\"getAuthToken\"]()\r\n    \r\n    if (!token) {\r\n        return request\r\n    }\r\n    \r\n    switch(config.authenticationType) {\r\n        case null:\r\n            return request;\r\n        case \"basic\":\r\n            request.headers[\"Authorization\"] = `Basic ${token}`\r\n            return request;\r\n        case \"bearer\":\r\n            request.headers[\"Authorization\"] = `Bearer ${token}`\r\n            return request;\r\n        case \"header\":\r\n            request.headers[config.authHeaderKey] = token\r\n            return request;\r\n        case \"queryString\":\r\n            const url = new URL(request.url)\r\n            url.searchParams.append(config.authQueryStringKey, token)\r\n            request.url = url.toString()\r\n            return request;\r\n        case \"cookies\":\r\n            if (typeof document === \"undefined\") {\r\n                request.headers['Cookie'] = `${config.authenticationKey}=${token}`\r\n            }\r\n            return request\r\n    }\r\n}\r\n\r\n/** @import { UploadPayload } from \"./uploadPayload.ts\" */\r\n\r\nconst convertArgsToFetchInit = async <T>({url, method, body, config, promise}: { url: string, method:string, body?:UploadPayload, config:SoFetchConfig, promise:SoFetchPromise<T> }) => {\r\n    const headers = {}\r\n    let request = {url, method, body, headers}\r\n    request.url = !config.baseUrl || request.url.startsWith(\"http\") ? request.url : `${config.baseUrl}${request.url}`\r\n    request = await addAuthentication(request, config)\r\n    request = await transformRequest(request, promise.beforeSendHandlers)\r\n    request = await transformRequest(request, config[\"beforeSendHandlers\"])\r\n    const {files} = normalisePayload(request.body)\r\n    const sendCookies = config.authenticationType === \"cookies\"\r\n    let init = files ? makeFilesRequest(request, files, sendCookies) : makeJsonRequest(request, sendCookies)\r\n    init = await handleBeforeFetchSend(init, promise.beforeFetchSendHandlers)\r\n    init = await handleBeforeFetchSend(init, config[\"beforeFetchSendHandlers\"])\r\n    return {init, finalUrl:request.url}\r\n}\r\n\r\nconst makeRequestWrapper = <TResponse>(config: SoFetchConfig, method:string, url:string, body?:UploadPayload) => {\r\n    const promise = new SoFetchPromise<TResponse>((resolve, reject) => {\r\n        (async () => {\r\n            await sleep(0) //Allows the promise to be initialised\r\n            const {finalUrl, init} = await convertArgsToFetchInit({url, method, body, config, promise})\r\n            \r\n            const startTime = new Date().getTime()\r\n            const response = await Promise.race([\r\n                fetch(finalUrl, init),\r\n                new Promise<Response>((_, reject) =>\r\n                    setTimeout(() => reject(new Error(\"SoFetch timed out\")), promise.timeout)\r\n                )\r\n            ]);\r\n            const duration = new Date().getTime() - startTime\r\n            \r\n            if (soFetch.verbose) {\r\n                console.info(`SoFetch: ${method} ${response.status} ${finalUrl}`)\r\n            }\r\n            for(const h of promise[\"onRequestCompleteHandlers\"]) {\r\n                await h(response, {duration, method:init.method || \"\"})\r\n            }\r\n            for(const h of config[\"onRequestCompleteHandlers\"]) {\r\n                await h(response, {duration, method:init.method || \"\"})\r\n            }\r\n            if (!response.ok) {\r\n                const requestHandled = handleHttpErrors(response, promise.errorHandlers)\r\n                let configHandled = false\r\n                if (!requestHandled) {\r\n                    configHandled = handleHttpErrors(response, config[\"errorHandlers\"])\r\n                }\r\n                if (!requestHandled && !configHandled) {\r\n                    // @ts-ignore\r\n                    throw new Error(`Received response ${response.status} from URL ${response.url}`, {cause: response})\r\n                }\r\n            }\r\n            const returnObject = await handleResponse(response)\r\n            resolve(returnObject)\r\n        })().catch(e => {\r\n            reject(e)\r\n        })\r\n    })\r\n    return promise\r\n}\r\n\r\nexport interface SoFetchRequest {\r\n    url:string,\r\n    method:string,\r\n    body:object | undefined\r\n    headers:Record<string,string>\r\n}\r\n\r\nconst makeJsonRequest = (request: SoFetchRequest, sendCookies: boolean):RequestInit => {\r\n    const { method, body} = request\r\n    if (body) {\r\n        request.headers['Content-Type'] = 'application/json'\r\n    }\r\n    const init:RequestInit = {\r\n        body: body ? JSON.stringify(body) : undefined,\r\n        headers: request.headers,\r\n        method,\r\n        credentials:sendCookies ? \"include\" : undefined\r\n    }\r\n    return init\r\n}\r\n\r\nconst makeFilesRequest = (request: SoFetchRequest, files: FileWithFieldName[], sendCookies: boolean):RequestInit => {\r\n    const {method, headers} = request\r\n    const formData = new FormData()\r\n    files.forEach(f => {\r\n        formData.append(f.fieldName, f.file, f.file.name)\r\n    })\r\n    const init:RequestInit = {\r\n        body: formData,\r\n        headers,\r\n        method,\r\n        credentials:sendCookies ? \"include\" : undefined\r\n    }\r\n    return init\r\n}\r\n\r\nconst handleResponse = async (response:Response) => {\r\n\r\n    if (response.status === 203) {\r\n        return undefined\r\n    }\r\n\r\n    const responseBody = await response.text();\r\n    if (!responseBody) {\r\n        return undefined\r\n    }\r\n    let responseObject: any = responseBody\r\n    try {\r\n        responseObject = JSON.parse(responseBody);\r\n    } catch {\r\n    }\r\n\r\n    return responseObject\r\n}\r\n\r\n/**\r\n * Makes an HTTP request to the specified URL.\r\n * @template TResponse The primitive or object type you're expecting from the server\r\n * @param {string} url An absolute or relative URL\r\n * @param {UploadPayload} [body] If absent soFetch will make a GET request. If present soFetch will make a POST request. To make PUT, PATCH, DELETE requests see soFetch.put, soFetch.patch, soFetch.delete\r\n * @returns An awaitable SoFetchPromise which resolves to type TResponse\r\n * @example\r\n * \r\n *    const products = await soFetch<Product[]>(\"/api/products\")\r\n *    \r\n * @see For more examples see https://sofetch.antoinette.agency\r\n */\r\nconst soFetch = (<TResponse>(url: string, body?: UploadPayload): SoFetchPromise<TResponse> => {\r\n    return makeRequestWrapper<TResponse>(soFetch.config || new SoFetchConfig(), body ? \"POST\" : \"GET\", url,  body)\r\n}) as SoFetchLike;\r\n\r\nsoFetch.verbose = false;\r\n\r\n\r\nsoFetch.config = new SoFetchConfig()\r\n\r\n/**\r\n * Makes a GET request to the specified URL\r\n * @template TResponse The primitive or object type you're expecting from the server\r\n * @param url An absolute or relative URL\r\n * @returns An awaitable SoFetchPromise which resolves to type TResponse\r\n * @example\r\n *\r\n *    const products = await soFetch.get<Product[]>(\"/api/products\")\r\n *\r\n * @see For more examples see https://sofetch.antoinette.agency\r\n */\r\nsoFetch.get = (url: string) => {\r\n    return makeRequestWrapper( soFetch.config,\"GET\", url)\r\n}\r\n\r\n/**\r\n * Makes a POST request to the specified URL\r\n * @template TResponse The primitive or object type you're expecting from the server\r\n * @param url An absolute or relative URL\r\n * @param {UploadPayload} [body] The body of the request\r\n * @returns An awaitable SoFetchPromise which resolves to type TResponse\r\n * @example\r\n *\r\n *    const newUser = {\r\n *        name:\"Regina George\",\r\n *        email:\"regina@massive-deal.com\"\r\n *    }\r\n *    const successResponse = await soFetch.post<Success>(\"/api/users\", newUser)\r\n *\r\n * @see For more examples see https://sofetch.antoinette.agency\r\n */\r\nsoFetch.post = (url: string, body?: object) => {\r\n    return makeRequestWrapper(soFetch.config,\"POST\", url, body)\r\n}\r\n\r\n/**\r\n * Makes a PUT request to the specified URL\r\n * @template TResponse The primitive or object type you're expecting from the server\r\n * @param url An absolute or relative URL\r\n * @param {UploadPayload} [body] The body of the request\r\n * @returns An awaitable SoFetchPromise which resolves to type TResponse\r\n * @example\r\n *\r\n *    const upsertUser = {\r\n *        name:\"Regina George\",\r\n *        email:\"regina@massive-deal.com\"\r\n *    }\r\n *    const successResponse = await soFetch.put<Success>(\"/api/users/1234\", upsertUser)\r\n *\r\n * @see For more examples see https://sofetch.antoinette.agency\r\n */\r\nsoFetch.put = (url: string, body?: object) => {\r\n    return makeRequestWrapper(soFetch.config,\"PUT\", url, body)\r\n}\r\n\r\n/**\r\n * Makes a PATCH request to the specified URL\r\n * @template TResponse The primitive or object type you're expecting from the server\r\n * @param url An absolute or relative URL\r\n * @param {UploadPayload} [body] The body of the request\r\n * @returns An awaitable SoFetchPromise which resolves to type TResponse\r\n * @example\r\n *\r\n *    const updateUserEmail = {\r\n *        email:\"regina@massive-deal.com\"\r\n *    }\r\n *    const successResponse = await soFetch.patch<Success>(\"/api/users/1234\", updateUserEmail)\r\n *\r\n * @see For more examples see https://sofetch.antoinette.agency\r\n */\r\nsoFetch.patch = (url: string, body?: object) => {\r\n    return makeRequestWrapper(soFetch.config,\"PATCH\", url, body)\r\n}\r\n\r\n/**\r\n * Makes a DELETE request to the specified URL\r\n * @template TResponse The primitive or object type you're expecting from the server\r\n * @param url An absolute or relative URL\r\n * @returns An awaitable SoFetchPromise which resolves to type TResponse\r\n * @example\r\n *\r\n *    await soFetch.delete(\"/api/users/1234\")\r\n *\r\n * @see For more examples see https://sofetch.antoinette.agency\r\n */\r\nsoFetch.delete = (url: string) => {\r\n    return makeRequestWrapper(soFetch.config,\"DELETE\", url)\r\n}\r\n\r\nfunction generateNewAuthenticationKey(authenticationKey: string) {\r\n    const regex = /^(.*?)([0-9]+)?$/;\r\n    const match = authenticationKey.match(regex);\r\n    const match1 = match ? match[1] : null\r\n    const match2 = match ? match[2] : null\r\n    if (!match1) {\r\n        return authenticationKey\r\n    }\r\n    let next = match2 ? (parseInt(match2) + 1) : 1\r\n    return `${match1}${next}`;\r\n}\r\n\r\n/**\r\n * Returns an independent instance of soFetch configured as per the original. The baseUrl and event handlers\r\n * will be copied over.\r\n * @see For examples see https://sofetch.antoinette.agency\r\n */\r\nsoFetch.instance = (configOrAuthKey?:SoFetchConfig | string) => {\r\n    \r\n    const configWasPassed = !!configOrAuthKey && typeof configOrAuthKey !== \"string\"\r\n    const oldConfig = configWasPassed ? (configOrAuthKey as SoFetchConfig) : soFetch.config\r\n    const newConfig = new SoFetchConfig()\r\n    const newAuthKey = typeof configOrAuthKey == \"string\" ? (configOrAuthKey as string) : undefined\r\n    if (!configWasPassed) {\r\n        newConfig.baseUrl = oldConfig.baseUrl\r\n        newConfig[\"beforeSendHandlers\"] = [...oldConfig[\"beforeSendHandlers\"]]\r\n        newConfig[\"beforeFetchSendHandlers\"] = [...oldConfig[\"beforeFetchSendHandlers\"]]\r\n        newConfig[\"onRequestCompleteHandlers\"] = [...oldConfig[\"onRequestCompleteHandlers\"]]\r\n        newConfig.authTokenStorage = oldConfig.authTokenStorage\r\n        newConfig[\"inMemoryAuthToken\"] = oldConfig[\"inMemoryAuthToken\"]\r\n    }\r\n    newConfig.authenticationKey = newAuthKey || generateNewAuthenticationKey(oldConfig.authenticationKey)\r\n    \r\n    const soFetchInstance = (<TResponse>(url: string, body?: UploadPayload): SoFetchPromise<TResponse> => {\r\n        return makeRequestWrapper<TResponse>(newConfig,body ? \"POST\" : \"GET\", url,  body)\r\n    }) as SoFetchLike;\r\n    soFetchInstance.get = (url: string, body?: UploadPayload) => {\r\n        return makeRequestWrapper(newConfig, \"GET\", url, body)\r\n    }\r\n    soFetchInstance.post = (url: string, body?: UploadPayload) => {\r\n        return makeRequestWrapper(newConfig,\"POST\", url, body)\r\n    }\r\n    soFetchInstance.put = (url: string, body?: UploadPayload) => {\r\n        return makeRequestWrapper(newConfig,\"PUT\", url, body)\r\n    }\r\n    soFetchInstance.patch = (url: string, body?: UploadPayload) => {\r\n        return makeRequestWrapper(newConfig,\"PATCH\", url, body)\r\n    }\r\n    soFetchInstance.delete = (url: string, body?: UploadPayload) => {\r\n        return makeRequestWrapper(newConfig,\"DELETE\", url, body)\r\n    }\r\n    soFetchInstance.verbose = soFetch.verbose\r\n    soFetchInstance.config = newConfig\r\n    soFetchInstance.instance = (c?:SoFetchConfig) => {\r\n        return soFetch.instance(c || newConfig)\r\n    }\r\n    return soFetchInstance\r\n}\r\n\r\nexport default soFetch;\r\n", "/**\r\n * Status codes issued by a server in response to a client's request. \r\n * All HTTP response status codes are separated into five classes or categories. The first digit of the status code defines the class of response, while the last two digits do not have any classifying or categorization role. There are five classes defined by the standard:\r\n *\r\n * - 1xx informational response \u2013 the request was received, continuing process\r\n * - 2xx successful \u2013 the request was successfully received, understood, and accepted\r\n * - 3xx redirection \u2013 further action needs to be taken in order to complete the request\r\n * - 4xx client error \u2013 the request contains bad syntax or cannot be fulfilled\r\n * - 5xx server error \u2013 the server failed to fulfil an apparently valid request\r\n *\r\n * @see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes\r\n */\r\nexport const enum HttpStatus {\r\n    /**\r\n     * Interim response indicates that everything so far is OK and that the client should continue with the request or ignore it if it is already finished.\r\n     * @see https://http.cat/status/100\r\n     */\r\n    Continue100 = 100,\r\n    \r\n    /**\r\n     * Sent in response to an Upgrade request header by the client, and indicates the protocol the server is switching too.\r\n     * @see https://http.cat/status/101\r\n     */\r\n    SwitchingProtocols101 = 101,\r\n\r\n    /**\r\n     * Indicates that the server has received and is processing the request, but no response is available yet.\r\n     * @see https://http.cat/status/102\r\n     */\r\n    Processing102 = 102,\r\n\r\n    /**\r\n     * The request has succeeded. The meaning of a success varies depending on the HTTP method:\r\n     * GET: The resource has been fetched and is transmitted in the message body.\r\n     * HEAD: The entity headers are in the message body.\r\n     * POST: The resource describing the result of the action is transmitted in the message body.\r\n     * TRACE: The message body contains the request message as received by the server\r\n     * @see https://http.cat/status/200\r\n     */\r\n    OK200 = 200,\r\n\r\n    /**\r\n     * Request has succeeded and a new resource has been created as a result of it. This is typically the response sent after a PUT request.\r\n     * @see https://http.cat/status/201\r\n     */\r\n    Created201 = 201,\r\n\r\n    /**\r\n     * Request has been received but not yet acted upon. It is non-committal, meaning that there is no way in HTTP to later send an asynchronous response indicating the outcome of processing the request. It is intended for cases where another process or server handles the request, or for batch processing.\r\n     * @see https://http.cat/status/202\r\n     */\r\n    Accepted202 = 202,\r\n\r\n    /**\r\n     * There is no content to send for this request, but the headers may be useful. The user-agent may update its cached headers for this resource with the new ones.\r\n     * @see https://http.cat/status/204\r\n     */\r\n    NoContent204 = 204,\r\n\r\n    /**\r\n     * Response code is sent after accomplishing request to tell user agent reset document view which sent this request.\r\n     * @see https://http.cat/status/205\r\n     */\r\n    ResetContent205 = 205,\r\n\r\n    /**\r\n     * Response code is used because of range header sent by the client to separate download into multiple streams.\r\n     * @see https://http.cat/status/206\r\n     */\r\n    PartialContent206 = 206,\r\n\r\n    /**\r\n     * Request has more than one possible responses. User-agent or user should choose one of them. There is no standardized way to choose one of the responses.\r\n     * @see https://http.cat/status/300\r\n     */\r\n    MultipleChoices300 = 300,\r\n\r\n    /**\r\n     * Means that URI of requested resource has been changed. Probably, new URI would be given in the response.\r\n     * @see https://http.cat/status/301\r\n     */\r\n    MovedPermanently301 = 301,\r\n\r\n    /**\r\n     * Means that URI of requested resource has been changed temporarily. New changes in the URI might be made in the future. Therefore, this same URI should be used by the client in future requests.\r\n     * @see https://en.wikipedia.org/wiki/HTTP_302\r\n     */\r\n    Found302 = 302,\r\n\r\n    /**\r\n     * Server sent this response to directing client to get requested resource to another URI with an GET request.\r\n     * @see https://http.cat/status/303\r\n     */\r\n    SeeOther303 = 303,\r\n\r\n    /**\r\n     * Used for caching purposes. It is telling to client that response has not been modified. So, client can continue to use same cached version of response.\r\n     * @see https://http.cat/status/304\r\n     */\r\n    NotModified304 = 304,\r\n\r\n    /**\r\n     * @deprecated\r\n     * Was defined in a previous version of the HTTP specification to indicate that a requested response must be accessed by a proxy. It has been deprecated due to security concerns regarding in-band configuration of a proxy.\r\n     * @see https://http.cat/status/305\r\n     */\r\n    UseProxy305 = 305,\r\n\r\n    /**\r\n     * Server sent this response to directing client to get requested resource to another URI with same method that used prior request. This has the same semantic than the 302 Found HTTP response code, with the exception that the user agent must not change the HTTP method used: if a POST was used in the first request, a POST must be used in the second request.\r\n     * @see https://http.cat/status/307\r\n     */\r\n    TemporaryRedirect307 = 307,\r\n\r\n    /**\r\n     * Means that the resource is now permanently located at another URI, specified by the Location: HTTP Response header. This has the same semantics as the 301 Moved Permanently HTTP response code, with the exception that the user agent must not change the HTTP method used: if a POST was used in the first request, a POST must be used in the second request.\r\n     * @see https://http.cat/status/308\r\n     */\r\n    PermanentRedirect308 = 308,\r\n\r\n    /**\r\n     * Server could not understand the request due to invalid syntax.\r\n     * @see https://http.cat/status/400\r\n     */\r\n    BadRequest400 = 400,\r\n\r\n    /**\r\n     * Although the HTTP standard specifies \"unauthorized\", semantically this response means \"unauthenticated\". That is, the client must authenticate itself to get the requested response.\r\n     * @see https://http.cat/status/401\r\n     */\r\n    Unauthorized401 = 401,\r\n\r\n    /**\r\n     * Reserved for future use. Initial aim for creating this code was using it for digital payment systems however this is not used currently.\r\n     * @see https://http.cat/status/402\r\n     */\r\n    PaymentRequired402 = 402,\r\n\r\n    /**\r\n     * Client does not have access rights to the content, i.e. they are unauthorized, so server is rejecting to give proper response. Unlike 401, the client's identity is known to the server.\r\n     * @see https://http.cat/status/403\r\n     */\r\n    Forbidden403 = 403,\r\n\r\n    /**\r\n     * Server can not find requested resource. In the browser, this means the URL is not recognized. In an API, this can also mean that the endpoint is valid but the resource itself does not exist. Servers may also send this response instead of 403 to hide the existence of a resource from an unauthorized client.\r\n     * @see https://http.cat/status/404\r\n     */\r\n    NotFound404 = 404,\r\n\r\n    /**\r\n     * Request method is known by the server but has been disabled and cannot be used. For example, an API may forbid DELETE-ing a resource. The two mandatory methods, GET and HEAD, must never be disabled and should not return this error code.\r\n     * @see https://http.cat/status/405\r\n     */\r\n    MethodNotAllowed405 = 405,\r\n\r\n    /**\r\n     * Response is sent when the web server, after performing server-driven content negotiation, doesn't find any content following the criteria given by the user agent.\r\n     * @see https://http.cat/status/406\r\n     */\r\n    NotAcceptable406 = 406,\r\n\r\n    /**\r\n     * Similar to 401 but authentication is needed to be done by a proxy.\r\n     * @see https://http.cat/status/407\r\n     */\r\n    ProxyAuthenticationRequired407 = 407,\r\n\r\n    /**\r\n     * Response is sent on an idle connection by some servers, even without any previous request by the client. It means that the server would like to shut down this unused connection.\r\n     * @see https://http.cat/status/408\r\n     */\r\n    RequestTimeout408 = 408,\r\n\r\n    /**\r\n     * Sent when a request conflicts with the current state of the server.\r\n     * @see https://http.cat/status/409\r\n     */\r\n    Conflict409 = 409,\r\n\r\n    /**\r\n     * Response would be sent when the requested content has been permenantly deleted from server, with no forwarding address.\r\n     * @see https://http.cat/status/410\r\n     */\r\n    Gone410 = 410,\r\n\r\n    /**\r\n     * Server rejected the request because the Content-Length header field is not defined and the server requires it.\r\n     * @see https://http.cat/status/411\r\n     */\r\n    LengthRequired411 = 411,\r\n\r\n    /**\r\n     * Client has indicated preconditions in its headers which the server does not meet.\r\n     * @see https://http.cat/status/412\r\n     */\r\n    PreconditionFailed412 = 412,\r\n\r\n    /**\r\n     * URI requested by the client is longer than the server is willing to interpret.\r\n     * @see https://http.cat/status/413\r\n     */\r\n    PayloadTooLarge413 = 413,\r\n\r\n    /**\r\n     * Media format of the requested data is not supported by the server, so the server is rejecting the request.\r\n     * @see https://http.cat/status/414\r\n     */\r\n    URITooLong414 = 414,\r\n\r\n    /**\r\n     * Media format of the requested data is not supported by the server.\r\n     * @see https://http.cat/status/415\r\n     */\r\n    UnsupportedMediaType415 = 415,\r\n\r\n    /**\r\n     * Range specified by the Range header field in the request can't be fulfilled\r\n     * @see https://http.cat/status/416\r\n     */\r\n    RangeNotSatisfiable416 = 416,\r\n\r\n    /**\r\n     * Response code means the expectation indicated by the Expect request header field can't be met by the server.\r\n     * @see https://http.cat/status/417\r\n     */\r\n    ExpectationFailed417 = 417,\r\n\r\n    /**\r\n     * Any attempt to brew coffee with a teapot should result in the error code \"418 I'm a teapot\". The resulting entity body MAY be short and stout.\r\n     * @see https://http.cat/status/418\r\n     */\r\n    ImATeapot418 = 418,\r\n\r\n    /**\r\n     * Defined in the specification of HTTP/2 to indicate that a server is not able to produce a response for the combination of scheme and authority that are included in the request URI.\r\n     * @see https://http.cat/status/421\r\n     */\r\n    MisdirectedRequest421 = 421,\r\n\r\n    /**\r\n     * Request was well-formed but was unable to be followed due to semantic errors.\r\n     * @see https://http.cat/status/422\r\n     */\r\n    UnprocessableEntity422 = 422,\r\n\r\n    /**\r\n     * The resource that is being accessed is locked.\r\n     * @see https://http.cat/status/423\r\n     */\r\n    Locked423 = 423,\r\n\r\n    /**\r\n     * Request failed due to failure of a previous request.\r\n     * @see https://http.cat/status/424\r\n     */\r\n    FailedDependency424 = 424,\r\n\r\n    /**\r\n     * \r\n     * @see https://http.cat/status/425\r\n     */\r\n    TooEarly425 = 425,\r\n\r\n    /**\r\n     * The server refuses to perform the request using the current protocol but might be willing to do so after the client upgrades to a different protocol.\r\n     * @see https://http.cat/status/426\r\n     */\r\n    UpgradeRequired426 = 426,\r\n\r\n    /**\r\n     * The origin server requires the request to be conditional. Intended to prevent the 'lost update' problem, where a client GETs a resource's state, modifies it, and PUTs it back to the server, when meanwhile a third party has modified the state on the server, leading to a conflict.\r\n     * @see https://http.cat/status/428\r\n     */\r\n    PreconditionRequired428 = 428,\r\n\r\n    /**\r\n     * User has sent too many requests in a given amount of time\r\n