UNPKG

@jspm/generator

Version:

Package Import Map Generation Tool

138 lines (136 loc) 4.86 kB
// @ts-ignore import { fetch as fetchImpl, clearCache } from '#fetch'; let retryCount = 5, poolSize = 100; export function setRetryCount(count) { retryCount = count; } export function setFetchPoolSize(size) { poolSize = 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 pushFetchPool(); while(true){ try { return await fetch(url, ...args); } catch (e) { if (retries++ >= retryCount) throw e; } } } finally{ popFetchPool(); } }; return wrappedFetch; } // restrict in-flight fetches to a pool of 100 let p = []; let c = 0; function pushFetchPool() { if (++c > poolSize) return new Promise((r)=>p.push(r)); } function popFetchPool() { c--; if (p.length) p.shift()(); } let _fetch = wrappedFetch(fetchImpl); export { clearCache, _fetch as fetch }; //# sourceMappingURL=fetch.js.map