UNPKG

@jspm/generator

Version:

Package Import Map Generation Tool

130 lines (128 loc) 4.7 kB
// @ts-ignore import { fetch as fetchImpl, clearCache } from '#fetch'; import { Pool } from './pool.js'; let retryCount = 5; export function setRetryCount(count) { retryCount = count; } export function setFetchPoolSize(size) { fetchPool.setSize(size); } /** * Allows customizing the fetch implementation used by the generator. */ export function setFetch(fetch) { _fetch = wrappedFetch(fetch); } const virtualSources = {}; /** * Allows virtual sources to be defined into the wrapped fetch implementation. */ export function setVirtualSourceData(urlBase, sourceData) { virtualSources[urlBase.endsWith('/') ? urlBase : urlBase + '/'] = sourceData; } const emptyHeaders = new Headers(); const jsonHeaders = new Headers([ [ 'content-type', 'application/json' ] ]); function sourceResponse(url, buffer) { return { url, ok: true, headers: url.endsWith('.json') ? jsonHeaders : emptyHeaders, status: 200, async text () { return buffer.toString(); }, async json () { return JSON.parse(buffer.toString()); }, arrayBuffer () { if (typeof buffer === 'string') return new TextEncoder().encode(buffer.toString()).buffer; return new Uint8Array(buffer); } }; } /** * Wraps a fetch request with pooling, retry logic on exceptions (emfile / network errors), * and source virtualization. */ function wrappedFetch(fetch) { const wrappedFetch = async function(url, ...args) { url = url.toString(); let matchedVirtual = false; for (const virtualBase of Object.keys(virtualSources)){ if (url.startsWith(virtualBase)) { const virtualFileData = virtualSources[virtualBase]; let subdir = url.slice(virtualBase.length); const source = virtualFileData[subdir]; if (source) return sourceResponse(url, source); // check if we have files within this virtual source path as a folder // and if so return the file listing as a 204 listing (internal non-public convention) let dirFiles = null; if (!subdir.endsWith('/') && subdir.length) subdir += '/'; for (const file of Object.keys(virtualFileData)){ if (file.startsWith(subdir)) { dirFiles = dirFiles || []; let filename = file.slice(subdir.length); if (filename.indexOf('/') !== -1) { filename = filename.slice(0, filename.indexOf('/')); if (dirFiles.includes(filename)) continue; } dirFiles.push(filename); } } if (dirFiles) { return { // we use a 204 status for directory responses on the filesystem // which support listing. This is only a local internal convention, // not intended for external URLs. ok: true, status: 204, headers: emptyHeaders, async text () { return ''; }, async json () { return dirFiles; }, arrayBuffer () { return new ArrayBuffer(0); } }; } // we allow fallthrough to other virtual source bases // that is, virtual source bases are allowed to nest eachother // this may not be useful in which case we can remove matchedVirtual = true; } } if (matchedVirtual) { return { url, ok: false, headers: emptyHeaders, status: 404, statusText: 'Virtual source not found' }; } let retries = 0; try { await fetchPool.queue(); while(true){ try { return await fetch(url, ...args); } catch (e) { if (retries++ >= retryCount) throw e; } } } finally{ fetchPool.pop(); } }; return wrappedFetch; } const fetchPool = new Pool(100); let _fetch = wrappedFetch(fetchImpl); export { clearCache, _fetch as fetch }; //# sourceMappingURL=fetch.js.map