Spaces:
Running
Running
Download src/data-loader.js from lordhet/fruit-fly-simulation: direct link, hf CLI and curl.
- Browser
- Download file 5.68 kB
-
https://huggingface.co/spaces/lordhet/fruit-fly-simulation/resolve/main/src/data-loader.js
- Command line
-
hf download hf://spaces/lordhet/fruit-fly-simulation/src/data-loader.js
-
curl -L -o data-loader.js https://huggingface.co/spaces/lordhet/fruit-fly-simulation/resolve/main/src/data-loader.js
5.68 kB
| // The page supplies the public asset root to its bundled worker. This keeps | |
| // runtime downloads correct in development and under any production subpath. | |
| let assetBase; | |
| export function configureAssetBase(url) { | |
| assetBase = new URL(url).href; | |
| } | |
| export function assetURL(path) { | |
| if (!assetBase) throw Error('Asset base has not been configured'); | |
| return new URL(path, assetBase).href; | |
| } | |
| const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); | |
| const transient = new Set([408, 429, 500, 502, 503, 504]); | |
| /** Retry the same file, honoring server cooldowns. Downloaded chunks survive retries/reloads. */ | |
| export async function requestBytes( | |
| url, | |
| { fetcher = fetch, wait = sleep, notice = () => {}, attempts = 6 } = {}, | |
| ) { | |
| url = assetURL(url); | |
| for (let attempt = 0; attempt < attempts; attempt++) { | |
| const controller = new AbortController(); | |
| const timer = setTimeout(() => controller.abort(), 45000); | |
| let delay = 0; | |
| try { | |
| const response = await fetcher(url, { signal: controller.signal }); | |
| if (response.ok) return await response.arrayBuffer(); | |
| const retryAfter = response.headers.get('Retry-After'); | |
| const seconds = retryAfter === null ? NaN : Number(retryAfter); | |
| delay = Number.isFinite(seconds) | |
| ? seconds * 1000 | |
| : Math.max(0, Date.parse(retryAfter) - Date.now()) || 0; | |
| await response.body?.cancel(); | |
| if (!transient.has(response.status)) | |
| throw Object.assign( | |
| Error(`Data download failed (HTTP ${response.status}). Please try again later.`), | |
| { permanent: true }, | |
| ); | |
| if (attempt === attempts - 1) | |
| throw Object.assign( | |
| Error( | |
| 'The data server is still busy. Try loading again shortly; saved chunks will be reused.', | |
| ), | |
| { permanent: true }, | |
| ); | |
| } catch (error) { | |
| if (error.permanent) throw error; | |
| if (attempt === attempts - 1) | |
| throw Error( | |
| 'The download was interrupted. Check your connection and try again; saved chunks will be reused.', | |
| ); | |
| } finally { | |
| clearTimeout(timer); | |
| } | |
| delay = Math.max(delay, Math.min(30000, 1000 * 2 ** attempt)); | |
| // Heartbeats keep the initialization watchdog informed during Retry-After waits. | |
| while (delay > 0) { | |
| notice( | |
| `Download paused · retrying in ${Math.ceil(delay / 1000)}s. Completed files are kept.`, | |
| ); | |
| const step = Math.min(delay, 10000); | |
| await wait(step); | |
| delay -= step; | |
| } | |
| } | |
| } | |
| export async function loadGraph(progress = () => {}, notice = () => {}) { | |
| const manifestBytes = await requestBytes('./data/manifest.json', { notice }); | |
| const manifest = JSON.parse(new TextDecoder().decode(manifestBytes)); | |
| let cache; | |
| try { | |
| cache = await globalThis.caches?.open('malecns-verified-data-v1'); | |
| } catch { | |
| /* Private browsing or storage restrictions: proceed without persistent cache. */ | |
| } | |
| const manifestHash = await digest(manifestBytes); | |
| let lastRequest = 0; | |
| const pacedFetch = async (...args) => { | |
| await sleep(Math.max(0, 250 - (Date.now() - lastRequest))); | |
| lastRequest = Date.now(); | |
| return fetch(...args); | |
| }; | |
| async function unpack(file, hash) { | |
| const url = './data/' + file, | |
| key = new URL(assetURL(url)); | |
| key.searchParams.set('content', hash ?? manifestHash); | |
| let bytes; | |
| try { | |
| const saved = await cache?.match(key.href); | |
| if (saved) bytes = await saved.arrayBuffer(); | |
| } catch { | |
| /* Cache is optional. */ | |
| } | |
| if (bytes && hash && (await digest(bytes)) !== hash) { | |
| await cache?.delete(key.href).catch(() => {}); | |
| bytes = null; | |
| } | |
| if (!bytes) { | |
| bytes = await requestBytes(url, { notice, fetcher: pacedFetch }); | |
| if (hash && (await digest(bytes)) !== hash) | |
| throw Error('A downloaded data file failed its checksum. Please retry loading.'); | |
| } | |
| let unpacked; | |
| try { | |
| unpacked = await new Response( | |
| new Blob([bytes]).stream().pipeThrough(new DecompressionStream('gzip')), | |
| ).arrayBuffer(); | |
| } catch { | |
| try { | |
| await cache?.delete(key.href); | |
| } catch {} | |
| throw Error('A data file could not be decompressed. Retry loading to download it again.'); | |
| } | |
| try { | |
| if (!(await cache?.match(key.href))) await cache?.put(key.href, new Response(bytes)); | |
| } catch { | |
| /* Storage is optional. */ | |
| } | |
| return unpacked; | |
| } | |
| notice('Loading saved files and downloading remaining connectivity…'); | |
| const neurons = JSON.parse(new TextDecoder().decode(await unpack(manifest.metadata))); | |
| const graph = { | |
| n: manifest.neurons, | |
| neurons, | |
| manifest, | |
| sign: Int32Array.from(neurons, (row) => row[5]), | |
| }; | |
| let done = 0; | |
| const total = manifest.arrays.reduce((sum, array) => sum + array.parts.length, 0); | |
| for (const array of manifest.arrays) { | |
| const values = new Uint32Array(array.length); | |
| let offset = 0; | |
| for (const part of array.parts) { | |
| const chunk = new Uint32Array(await unpack(part.file, part.sha256)); | |
| values.set(chunk, offset); | |
| offset += chunk.length; | |
| progress(++done / total); | |
| } | |
| if (offset !== values.length) throw Error('Invalid data length for ' + array.name); | |
| graph[array.name] = values; | |
| } | |
| if ( | |
| graph.offsets.length !== graph.n + 1 || | |
| graph.offsets[graph.n] !== graph.sources.length || | |
| graph.counts.length !== graph.sources.length | |
| ) | |
| throw Error('Invalid CSR structure'); | |
| return graph; | |
| } | |
| async function digest(bytes) { | |
| return Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', bytes)), (x) => | |
| x.toString(16).padStart(2, '0'), | |
| ).join(''); | |
| } | |