All files / src/api provider.ts

38.46% Statements 10/26
29.41% Branches 5/17
50% Functions 1/2
40% Lines 10/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92                                                                      1x     11x     11x 11x 11x 11x 11x   11x 11x 11x                                                                                    
import {APIMethods} from './client'
 
type Fetch = (input: any, init?: any) => Promise<any>
 
/** Response to an API call.  */
export interface APIResponse {
    json?: any
    text: string
    status: number
    headers: Record<string, string>
}
 
export interface APIProvider {
    /**
     * Call an API endpoint and return the response.
     * Provider is responsible for JSON encoding the params and decoding the response.
     * @argument path The endpoint path, e.g. `/v1/chain/get_info`
     * @argument params The request body if any.
     */
    call(args: {path: string; params?: unknown; method?: APIMethods}): Promise<APIResponse>
}
 
export interface FetchProviderOptions {
    /**
     * Fetch instance, must be provided in non-browser environments.
     * Node.js 18+ includes a built-in fetch implementation.
     */
    fetch?: Fetch
    /**
     * Headers that will be applied to every request
     * */
    headers?: Record<string, string>
}
 
/** Default provider that uses the Fetch API to call a single node. */
export class FetchProvider implements APIProvider {
    readonly url: string
    readonly fetch: Fetch
    readonly headers: Record<string, string> = {}
 
    constructor(url: string, options: FetchProviderOptions = {}) {
        url = url.trim()
        Iif (url.endsWith('/')) url = url.slice(0, -1)
        this.url = url
        if (options.headers) {
            this.headers = options.headers
        }
        if (!options.fetch) {
            if (typeof globalThis !== 'undefined' && globalThis.fetch) {
                this.fetch = globalThis.fetch.bind(globalThis)
            } else Eif (typeof window !== 'undefined' && window.fetch) {
                this.fetch = window.fetch.bind(window)
            } else {
                throw new Error('Missing fetch')
            }
        } else {
            Ethis.fetch = options.fetch
        }
    }
 
    async call(args: {
        path: string
        params?: unknown
        method?: APIMethods
        headers?: Record<string, string>
    }) {
        const url = this.url + args.path
        const reqBody = args.params !== undefined ? JSON.stringify(args.params) : undefined
        const reqHeaders = {
            ...this.headers,
            ...args.headers,
        }
        const response = await this.fetch(url, {
            method: args.method || 'POST',
            body: reqBody,
            headers: reqHeaders,
        })
        const text = await response.text()
        let json: any
        try {
            json = JSON.parse(text)
        } catch {
            // ignore json parse errors
        }
        const headers = {}
        for (const [key, value] of response.headers.entries()) {
            headers[key] = value
        }
        return {headers, status: response.status, json, text}
    }
}