All files / src sth.js

95.83% Statements 23/24
91.66% Branches 11/12
100% Functions 6/6
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53          2x     5x 5x 1x   4x 4x 1x   4x             2x           11x 11x         6x 4x       2x 2x 2x 2x 2x 2x   1x 1x        
import { unzipDiscImage } from "./archive.js";
 
// Always https, whatever the page was loaded over: the mirror redirects plain
// http, so following the page's protocol would cost a redirect on every request
// when developing over http, and Electron reports "file:" anyway.
const mirrorBase = "https://bbc.xania.org/archive/sth";
 
async function _fetchManifest(url) {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`Network response was not ok (${response.status})`);
    }
    const data = await response.json();
    if (!Array.isArray(data?.files)) {
        throw new Error("Invalid manifest: missing files array");
    }
    return data.files.map((f) => f.path).sort();
}
 
// Each path component is encoded individually so slashes survive but special
// characters in filenames (e.g. brackets in "Daxis[droids]-demo.zip") don't
// produce a malformed URL.
function encodePath(path) {
    return path.split("/").map(encodeURIComponent).join("/");
}
 
export class StairwayToHell {
    /** @param {{tapes?: boolean}} [what] the tape images rather than the disc images */
    constructor({ tapes = false } = {}) {
        this._baseUrl = `${mirrorBase}/${tapes ? "tape" : "disk"}images/`;
        this._catalog = [];
    }
 
    /** @returns {Promise<string[]>} every path in the archive, fetched the first time it is asked for */
    async catalogue() {
        if (this._catalog.length === 0) this._catalog = await _fetchManifest(this._baseUrl + "manifest.json");
        return this._catalog;
    }
 
    async fetch(file) {
        const url = this._baseUrl + encodePath(file);
        console.log("Loading ZIP from " + url);
        const response = await fetch(url);
        Iif (!response.ok) throw new Error(`Unable to load ${url}, http code ${response.status}`);
        try {
            return await unzipDiscImage(new Uint8Array(await response.arrayBuffer()));
        } catch (error) {
            console.error("Failed to fetch file:", error);
            throw error;
        }
    }
}