import {LRUCache} from 'lru-cache'; const cache = new LRUCache({ max: 128, }); export async function computeMissing(current, manifest) { const missing = []; for (const path in manifest) { if (!current || !current[path] || current[path].hash !== manifest[path]) { missing.push(path); } } return missing; } const octets = []; for (let i = 0; i < 256; ++i) { octets.push(i.toString(16).padStart(2, '0')); } export async function parseResources(resources, buffer) { const view = new DataView(buffer); let caret = 0; const count = view.getUint32(caret); caret += 4; const manifest = {}; for (let i = 0; i < count; ++i) { let hash = ''; for (let j = 0; j < 20; ++j) { hash += octets[view.getUint8(caret)] caret += 1; } const byteLength = view.getUint32(caret); caret += 4; const asset = new ArrayBuffer(byteLength); const assetView = new DataView(asset); for (let j = 0; j < asset.byteLength; ++j) { assetView.setUint8(j, view.getUint8(caret)); caret += 1; } manifest[resources[i]] = {asset, hash}; } return manifest; } export async function fetchResources(paths, {signal} = {}) { const response = await fetch('/resources/stream', { body: JSON.stringify(paths), method: 'post', signal, }); if (signal.aborted) { return undefined; } return parseResources(paths, await response.arrayBuffer()); } export async function get() { const {get} = await import('idb-keyval'); const resources = await get('$$silphius_resources'); return resources || {}; } export async function set(resources) { const {set} = await import('idb-keyval'); cache.clear(); await set('$$silphius_resources', resources); } export async function readAsset(path) { if (!cache.has(path)) { const {pathname} = new URL(path, 'http://example.org'); const resourcePath = pathname.slice('/resources/'.length); cache.set(path, get().then((resources) => resources[resourcePath]?.asset)); } return cache.get(path); }