All files / src http-request.js

7.4% Statements 6/81
100% Branches 2/2
50% Functions 1/2
7.4% Lines 6/81

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 821x 1x 1x                                                                                                                                                       1x 1x 1x  
const     attr = (el, attr)=> el.getAttribute(attr);
 
export class HttpRequestElement extends HTMLElement
{
    static observedAttributes =
    [   'value' // populated from localStorage, if defined initially, sets the value in storage
    ,   'slice'
    ,   'url'
    ,   'method'
    ,   'header-accept'
    ];

    get requestHeaders()
    {   const ret = {};
        [...this.attributes].filter(a=>a.name.startsWith('header-')).map( a => ret[a.name.substring(7)] = a.value );
        return ret
    }
    get requestProps()
    {   const ret = {};
        [...this.attributes].filter(a=>!a.name.startsWith('header-'))
                            .filter(a=>!a.name.startsWith('slice')).map( a => ret[a.name] = a.value );
        return ret
    }

    disconnectedCallback(){ this.#destroy?.(); }

    connectedCallback()
    {
        setTimeout(()=>this.fetch(),0)
    }
    #inProgressUrl = ''
    #destroy = ()=>{}

    async fetch()
    {
        if( !this.closest('custom-element') )
            return;
        const url = attr(this, 'url') || '';
        if( !url )
        {   this.#destroy?.();
            return this.value = {};
        }

        if( this.#inProgressUrl === url )
            return ;

        this.#inProgressUrl = url;
        const controller = new AbortController();
        this.#destroy = ()=> controller.abort(this.localName+' disconnected');

        const request = { ...this.requestProps, headers: this.requestHeaders }
        ,       slice = { request }
        ,      update = () => this.dispatchEvent( new Event('change') );
        this.value = slice;

        update();
        const response = await fetch(url,{ ...this.requestProps, signal: controller.signal, headers: this.requestHeaders })
        ,      r = {headers: {}};
        [...response.headers].map( ([k,v]) => r.headers[k] = v );
        'ok,status,statusText,type,url,redirected'.split(',').map( k=> r[k] = response[k] )

        slice.response = r;
        update();
        slice.data = await response.json();
        update();
    }

    attributeChangedCallback(name, oldValue, newValue)
    {   if( name === 'url' )
        {   if( newValue && oldValue !== newValue)
            {
                oldValue && this.#destroy?.();
                setTimeout(()=>this.fetch(),0)
            }
        }
    }

}
 
window.customElements.define( 'http-request', HttpRequestElement );
export default HttpRequestElement;