* @param {URL} url * @param {boolean[]} invalid * @returns {Promise }
(url, invalid)
| 2741 | * @returns {Promise<import('types').ServerNodesResponse | import('types').ServerRedirectNode>} |
| 2742 | */ |
| 2743 | async function load_data(url, invalid) { |
| 2744 | const data_url = new URL(url); |
| 2745 | data_url.pathname = add_data_suffix(url.pathname); |
| 2746 | if (url.pathname.endsWith('/')) { |
| 2747 | data_url.searchParams.append(TRAILING_SLASH_PARAM, '1'); |
| 2748 | } |
| 2749 | if (DEV && url.searchParams.has(INVALIDATED_PARAM)) { |
| 2750 | throw new Error(`Cannot used reserved query parameter "${INVALIDATED_PARAM}"`); |
| 2751 | } |
| 2752 | data_url.searchParams.append(INVALIDATED_PARAM, invalid.map((i) => (i ? '1' : '0')).join('')); |
| 2753 | |
| 2754 | // use window.fetch directly to allow using a 3rd party-patched fetch implementation |
| 2755 | const fetcher = DEV ? dev_fetch : window.fetch; |
| 2756 | const res = await fetcher(data_url.href, {}); |
| 2757 | |
| 2758 | if (!res.ok) { |
| 2759 | // error message is a JSON-stringified string which devalue can't handle at the top level |
| 2760 | // turn it into a HttpError to not call handleError on the client again (was already handled on the server) |
| 2761 | // if `__data.json` doesn't exist or the server has an internal error, |
| 2762 | // avoid parsing the HTML error page as a JSON |
| 2763 | /** @type {string | undefined} */ |
| 2764 | let message; |
| 2765 | if (res.headers.get('content-type')?.includes('application/json')) { |
| 2766 | message = await res.json(); |
| 2767 | } else if (res.status === 404) { |
| 2768 | message = 'Not Found'; |
| 2769 | } else if (res.status === 500) { |
| 2770 | message = 'Internal Error'; |
| 2771 | } |
| 2772 | throw new HttpError(res.status, message); |
| 2773 | } |
| 2774 | |
| 2775 | // TODO: fix eslint error / figure out if it actually applies to our situation |
| 2776 | // eslint-disable-next-line |
| 2777 | return new Promise(async (resolve) => { |
| 2778 | /** |
| 2779 | * Map of deferred promises that will be resolved by a subsequent chunk of data |
| 2780 | * @type {Map<string, import('types').Deferred>} |
| 2781 | */ |
| 2782 | const deferreds = new Map(); |
| 2783 | const reader = /** @type {ReadableStream<Uint8Array>} */ (res.body).getReader(); |
| 2784 | const decoder = new TextDecoder(); |
| 2785 | |
| 2786 | /** |
| 2787 | * @param {any} data |
| 2788 | */ |
| 2789 | function deserialize(data) { |
| 2790 | return devalue.unflatten(data, { |
| 2791 | ...app.decoders, |
| 2792 | Promise: (id) => { |
| 2793 | return new Promise((fulfil, reject) => { |
| 2794 | deferreds.set(id, { fulfil, reject }); |
| 2795 | }); |
| 2796 | } |
| 2797 | }); |
| 2798 | } |
| 2799 | |
| 2800 | let text = ''; |
no test coverage detected