Spaces:
Running
Running
Download app.js from SlayerLab/Slayer1B-Benchmarks: direct link, hf CLI and curl.
- Browser
- Download file 24 kB
-
https://huggingface.co/spaces/SlayerLab/Slayer1B-Benchmarks/resolve/main/app.js
- Command line
-
hf download hf://spaces/SlayerLab/Slayer1B-Benchmarks/app.js
-
curl -L -o app.js https://huggingface.co/spaces/SlayerLab/Slayer1B-Benchmarks/resolve/main/app.js
24 kB
| ; | |
| (() => { | |
| const contract = window.BenchContract; | |
| const T = { | |
| en: { | |
| search:"Search benchmark", language:"Language", show:"Show", all:"All", withResults:"With a published number", clear:"Clear filters", | |
| benchmark:"Benchmark", results:"Results", notPublished:"Not published yet", | |
| noPacket:"No results are published yet. The list below is the plan: 50 benchmarks, their sources and metrics. For models whose training data we have, a number appears only after a contamination scan.", | |
| loadError:"A pinned file could not be verified, so nothing is shown.", rejected:"Results are hidden because the result file did not pass the checks", | |
| empty:"No benchmark matches the filters.", shown:"Showing", of:"of", benchmarks:"benchmarks", | |
| pills:{total:"benchmarks", langs:"38 EN · 12 PL", models:"models", published:"with a published number"}, | |
| higher:"higher is better", lower:"lower is better", ci:"95% CI", noCI:"no interval", random:"random", majority:"majority", delta:"difference", | |
| majorityShare:"predicts its most common class in", none:"none", refMissing:"reference: not available", | |
| scan:{clean:"clean", flagged:"flagged", partially_scanned:"scan incomplete", unscanned:"scan pending", unknown:"training data unknown"}, | |
| noNumber:{partially_scanned:"No number: the scan did not cover enough items.", unscanned:"No number until the contamination scan is done."}, | |
| hitFree:"Without items found in training data", hitFreeAnswer:"Without hits in question or answer fields", hits:"items with hits", n:"n", | |
| noHitFree:"no items without hits", tooFew:"too few items for a number", | |
| mostly:"fewer than 50 items without hits in training data", inSlayer:"in the Slayer training corpus", slayerSubset:"subsets defined by the Slayer training corpus", mostlyVerdict:"No better/worse from the full sample here: only a paired test on the items without answer-field hits can give one.", | |
| pending:n => `${n} benchmarks are not measured yet; adding them cannot change the verdicts shown; it can settle rows now inconclusive and adds verdicts for the new rows.`, | |
| constant:"constant answer", constantNote:"One class for all items: not compared between models.", | |
| states:{ok:"Measured",not_run:"Not run",blocked_access:"Data access blocked",blocked_runtime:"Runtime blocked",blocked_security:"Blocked by security review",blocked_judge:"No accepted judge",unsupported:"Not supported",failed:"Run failed",skipped:"Skipped"}, | |
| verdicts:{better:"better than",worse:"worse than",inconclusive:"inconclusive vs"}, verdictNote:"paired test, Holm-adjusted", verdictNoteSubset:"paired test on items without answer-field hits, Holm-adjusted", | |
| amendments:{answer_leading_space_strip:"Scoring corrected after first run (leading space), see METHOD.", other:"Scoring amended after first run, see METHOD."}, | |
| scoringNotes:{source_extractor_floor:"Floor: the source 0-shot extractor reads the text between the first and last $, so answers in \\boxed{} score 0. Not a comparison of ability, see METHOD.", other:"Scoring note, see METHOD."}, | |
| details:"Source and sample", population:"population / sample / scored", revision:"revision", licence:"licence (dataset card)", files:"Pinned files", | |
| packet:"result file", run:"run", protocol:"protocol" | |
| }, | |
| pl: { | |
| search:"Szukaj benchmarku", language:"Język", show:"Pokaż", all:"Wszystkie", withResults:"Z opublikowaną liczbą", clear:"Wyczyść filtry", | |
| benchmark:"Benchmark", results:"Wyniki", notPublished:"Jeszcze nie opublikowano", | |
| noPacket:"Nie ma jeszcze opublikowanych wyników. Poniżej jest plan: 50 benchmarków, ich źródła i miary. Dla modeli, których dane treningowe mamy, liczba pojawia się dopiero po skanie kontaminacji.", | |
| loadError:"Nie udało się zweryfikować przypiętego pliku, więc niczego nie pokazuję.", rejected:"Wyniki ukryte, bo plik wyników nie przeszedł kontroli", | |
| empty:"Żaden benchmark nie pasuje do filtrów.", shown:"Pokazano", of:"z", benchmarks:"benchmarków", | |
| pills:{total:"benchmarków", langs:"38 EN · 12 PL", models:"modele", published:"z opublikowaną liczbą"}, | |
| higher:"więcej = lepiej", lower:"mniej = lepiej", ci:"95% CI", noCI:"bez przedziału", random:"losowo", majority:"większość", delta:"różnica", | |
| majorityShare:"najczęstsza własna klasa w", none:"brak", refMissing:"odniesienie: brak", | |
| scan:{clean:"czysty", flagged:"trafienia", partially_scanned:"skan niepełny", unscanned:"skan w toku", unknown:"dane treningowe nieznane"}, | |
| noNumber:{partially_scanned:"Bez liczby: skan nie objął dość pozycji.", unscanned:"Bez liczby do czasu skanu kontaminacji."}, | |
| hitFree:"Bez pozycji znalezionych w danych treningowych", hitFreeAnswer:"Bez trafień w polach pytania i odpowiedzi", hits:"pozycji z trafieniem", n:"n", | |
| noHitFree:"brak pozycji bez trafień", tooFew:"za mało pozycji na liczbę", | |
| mostly:"mniej niż 50 pozycji bez trafień w danych treningowych", inSlayer:"w korpusie treningowym Slayera", slayerSubset:"podzbiory wyznaczone korpusem treningowym Slayera", mostlyVerdict:"Tu nie ma werdyktu lepszy/gorszy z całej próbki: może go dać tylko test sparowany na pozycjach bez trafień w polach odpowiedzi.", | |
| pending:n => `${n} benchmarków nie jest jeszcze zmierzonych; ich dodanie nie zmieni pokazanych werdyktów; może rozstrzygnąć wiersze dziś nierozstrzygnięte i doda werdykty dla nowych wierszy.`, | |
| constant:"stała odpowiedź", constantNote:"Jedna klasa dla wszystkich pozycji: nie porównujemy między modelami.", | |
| states:{ok:"Pomiar",not_run:"Nie uruchomiono",blocked_access:"Brak dostępu do danych",blocked_runtime:"Blokada środowiska",blocked_security:"Zablokowane przez przegląd bezpieczeństwa",blocked_judge:"Brak przyjętego sędziego",unsupported:"Nieobsługiwane",failed:"Błąd biegu",skipped:"Pominięte"}, | |
| verdicts:{better:"lepszy niż",worse:"gorszy niż",inconclusive:"nierozstrzygnięte wobec"}, verdictNote:"test sparowany, poprawka Holma", verdictNoteSubset:"test sparowany na pozycjach bez trafień w polach odpowiedzi, poprawka Holma", | |
| amendments:{answer_leading_space_strip:"Punktacja poprawiona po pierwszym biegu (spacja na początku), zob. METHOD.", other:"Punktacja zmieniona po pierwszym biegu, zob. METHOD."}, | |
| scoringNotes:{source_extractor_floor:"Podłoga: ekstraktor źródła (0-shot) czyta tekst między pierwszym a ostatnim $, więc odpowiedzi w \\boxed{} dostają 0. To nie jest porównanie umiejętności, zob. METHOD.", other:"Uwaga o punktacji, zob. METHOD."}, | |
| details:"Źródło i próbka", population:"populacja / próbka / ocenione", revision:"rewizja", licence:"licencja (karta zbioru)", files:"Przypięte pliki", | |
| packet:"plik wyników", run:"bieg", protocol:"protokół" | |
| } | |
| }; | |
| const state = {lang:"en", tasks:null, packet:null, index:null, packetSHA:null, error:null, files:[], query:"", language:"all", show:"all"}; | |
| function el(tag, text, cls) { | |
| const n = document.createElement(tag); | |
| if (text !== undefined && text !== null) n.textContent = String(text); | |
| if (cls) n.className = cls; | |
| return n; | |
| } | |
| function fmt(v, unit) { | |
| const digits = unit === "byte_ppl" ? 3 : unit === "percent" ? 1 : 3; | |
| return new Intl.NumberFormat(state.lang === "pl" ? "pl-PL" : "en-GB", {minimumFractionDigits:digits, maximumFractionDigits:digits}).format(v); | |
| } | |
| const unitLabel = u => u === "percent" ? "%" : u === "byte_ppl" ? "byte-PPL" : ""; | |
| const pct = v => new Intl.NumberFormat(state.lang === "pl" ? "pl-PL" : "en-GB", {style:"percent", maximumFractionDigits:1}).format(v); | |
| function modelName(m) { return m.repo.split("/").pop(); } | |
| // Thresholds and the "most items found in training data" class come from the contract, which also guarantees that every | |
| // non-withheld verdict in an accepted packet follows the display rules (binding rules 2026-10-29 16:55 and 17:07). | |
| const MIN_HIT_FREE_N = contract.MIN_HIT_FREE_N; | |
| const mostlyTrained = taskId => state.index ? contract.mostlyTrained(state.index.observations, taskId, state.packet.protocol_id) : null; | |
| // Whether the cell carries a number at all, and why not otherwise. | |
| function numberRule(o) { | |
| if (o.state !== "ok") return {show:false, reason:null}; | |
| if (["clean","flagged","unknown"].includes(o.contamination)) return {show:true}; | |
| return {show:false, reason:o.contamination}; | |
| } | |
| function hitFreeLine(label, value, n, rate, ci, unit, t) { | |
| const tail = `${t.n} ${n}; ${pct(rate)} ${t.hits}`; | |
| if (n === 0) return `${label}: ${t.noHitFree}`; | |
| if (n < MIN_HIT_FREE_N) return `${label}: ${t.tooFew} (${tail})`; | |
| const interval = ci ? `${t.ci} ${fmt(ci.lower, unit)} – ${fmt(ci.upper, unit)}` : t.noCI; | |
| return `${label}: ${fmt(value, unit)}, ${interval} (${tail})`; | |
| } | |
| const isConstant = x => !!(x && x.reference && x.reference.constant_prediction === true); | |
| function cell(task, model, t) { | |
| const td = el("td"); | |
| const o = state.index && state.index.observations.get(contract.key(task.id, model.model_id, state.packet.protocol_id)); | |
| if (!o) { td.append(el("span", t.notPublished, "state")); return td; } | |
| const mostly = mostlyTrained(task.id); | |
| const rule = numberRule(o); | |
| const tags = el("div", undefined, "tags"); | |
| if (o.state !== "ok") { | |
| td.append(el("span", t.states[o.state], "state")); | |
| if (o.reason) td.append(el("span", o.reason, "meta")); | |
| } else if (!rule.show) { | |
| td.append(el("span", t.noNumber[rule.reason], "state")); | |
| } else { | |
| const v = el("span", fmt(o.value, o.unit), mostly === null ? "value" : "value dim"); | |
| if (unitLabel(o.unit)) v.append(el("small", unitLabel(o.unit))); | |
| td.append(v); | |
| td.append(el("span", o.ci95 ? `${t.ci} ${fmt(o.ci95.lower, o.unit)} – ${fmt(o.ci95.upper, o.unit)}` : t.noCI, "meta")); | |
| if (mostly !== null) td.append(el("span", `${t.mostly} (${pct(mostly)} ${t.hits}${o.contamination === "unknown" ? `, ${t.inSlayer}` : ""})`, "mostly")); | |
| const hasA = Number.isInteger(o.n_hit_free), hasB = Number.isInteger(o.n_hit_free_answer_fields); | |
| if (hasA) td.append(el("span", hitFreeLine(t.hitFree, o.value_hit_free, o.n_hit_free, o.hit_rate, o.ci95_hit_free, o.unit, t), "ref")); | |
| if (hasB) td.append(el("span", hitFreeLine(t.hitFreeAnswer, o.value_hit_free_answer_fields, o.n_hit_free_answer_fields, o.hit_rate_answer_fields, o.ci95_hit_free_answer_fields, o.unit, t), "ref")); | |
| if ((hasA || hasB) && o.contamination === "unknown") td.append(el("span", t.slayerSubset, "meta")); | |
| const r = o.reference; | |
| const refs = []; | |
| if (r && r.chance_value !== null) refs.push(`${t.random} ${fmt(r.chance_value, o.unit)}`); | |
| if (r && r.majority_value !== null) refs.push(`${t.majority} ${fmt(r.majority_value, o.unit)}`); | |
| td.append(el("span", refs.length ? refs.join(" · ") : t.refMissing, "ref")); | |
| if (r && r.majority_prediction_share !== null) td.append(el("span", `${t.majorityShare} ${pct(r.majority_prediction_share)}`, "meta")); | |
| if (isConstant(o)) tags.append(el("span", t.constant, "tag constant")); | |
| } | |
| const scanClass = {clean:"clean", flagged:"flagged", unknown:"unknown"}[o.contamination] || "pending"; | |
| tags.append(el("span", t.scan[o.contamination], `tag ${scanClass}`)); | |
| td.append(tags); | |
| if (rule.show && isConstant(o)) td.append(el("span", t.constantNote, "meta")); | |
| if (rule.show && mostly !== null) td.append(el("span", t.mostlyVerdict, "meta")); | |
| for (const c of state.index.contrasts) { | |
| if (c.task_id !== task.id || c.model_id !== model.model_id || c.verdict === "withheld") continue; | |
| const ref = state.index.models.get(c.model_ref); | |
| const p = new Intl.NumberFormat(state.lang === "pl" ? "pl-PL" : "en-GB", {maximumSignificantDigits:2}).format(c.p_holm); | |
| const note = c.subset === "full" ? t.verdictNote : `${t.verdictNoteSubset}, ${t.n} ${c.n}`; | |
| const signed = v => `${v > 0 ? "+" : ""}${fmt(v, c.unit)}`; | |
| const isNum = v => typeof v === "number" && Number.isFinite(v); | |
| const diff = isNum(c.delta) ? `${t.delta} ${signed(c.delta)}${c.ci95 ? ` (${t.ci} ${signed(c.ci95.lower)} – ${signed(c.ci95.upper)})` : ""} · ` : ""; | |
| td.append(el("span", `${t.verdicts[c.verdict]} ${modelName(ref)}`, "verdict"), el("span", `${diff}${note}: p = ${p}`, "meta")); | |
| } | |
| const d = el("details"); | |
| d.append(el("summary", t.details)); | |
| d.append(el("p", `${t.population}: ${o.n_population ?? "—"} / ${o.n_selected ?? "—"} / ${o.n_scored}`)); | |
| d.append(el("p", `results_sha256 ${o.results_sha256 ?? "—"}`)); | |
| if (o.contamination_receipt_sha256) d.append(el("p", `scan receipt ${o.contamination_receipt_sha256}`)); | |
| td.append(d); | |
| return td; | |
| } | |
| // Polish labels for the tasks_50 domain values; the English view shows the value itself. | |
| const DOMAIN_PL = { | |
| syntax:"składnia", science_reasoning:"rozumowanie naukowe", language_modeling:"modelowanie języka", morphosyntax:"morfoskładnia", | |
| commonsense:"zdrowy rozsądek", physical_reasoning:"rozumowanie fizyczne", coreference:"koreferencja", science_knowledge:"wiedza naukowa", | |
| reading:"czytanie", causal_reasoning:"rozumowanie przyczynowe", entailment:"wynikanie", word_sense:"znaczenie słowa", | |
| adversarial_entailment:"wynikanie (adwersaryjne)", contextual_prediction:"przewidywanie z kontekstu", logical_reasoning:"rozumowanie logiczne", | |
| mathematics_mc:"matematyka (wybór)", sentiment:"wydźwięk", topic_classification:"klasyfikacja tematu", question_relevance:"trafność pytania", | |
| paraphrase:"parafraza", summary_matching:"dopasowanie streszczenia", toxicity_detection:"wykrywanie toksyczności", | |
| sentence_entity_classification:"klasyfikacja zdań i encji", question_answering:"odpowiadanie na pytania", academic_knowledge:"wiedza akademicka", | |
| mathematics_generation:"matematyka (generowanie)", competition_mathematics:"matematyka konkursowa", truthfulness:"prawdomówność", | |
| instruction_following:"wykonywanie poleceń", native_exam_knowledge:"wiedza z egzaminów krajowych", unanswerable_reading:"czytanie z pytaniami bez odpowiedzi", | |
| discrete_reading:"czytanie z liczeniem", conversational_reading:"czytanie w dialogu", closed_book_knowledge:"wiedza bez kontekstu", | |
| biomedical_reading:"czytanie biomedyczne", medical_exam_knowledge:"wiedza z egzaminów medycznych", social_reasoning:"rozumowanie społeczne", | |
| formal_reasoning:"rozumowanie formalne", adversarial_paraphrase:"parafraza (adwersaryjna)", acceptability:"poprawność zdania", entity_reading:"czytanie o encjach" | |
| }; | |
| const domainLabel = d => (state.lang === "pl" && DOMAIN_PL[d]) || d.replace(/_/g, " "); | |
| function taskHeader(task, t) { | |
| const th = el("th", task.name, "task"); | |
| th.scope = "row"; | |
| th.append(el("span", `${task.language} · ${domainLabel(task.domain)}`, "meta")); | |
| th.append(el("span", `${task.metric.split(",")[0]} · ${task.higher_is_better ? t.higher : t.lower}`, "meta")); | |
| // Public scoring amendments are a property of the pinned benchmark list, never of a result file. | |
| for (const code of task.scoring_amendments || []) th.append(el("span", t.amendments[code] || t.amendments.other, "amend")); | |
| for (const code of task.scoring_notes || []) th.append(el("span", t.scoringNotes[code] || t.scoringNotes.other, "amend")); | |
| const d = el("details"); | |
| d.append(el("summary", t.details)); | |
| for (const s of task.license.source_revisions) { | |
| const p = el("p"); | |
| const a = el("a", s.repo_id); | |
| a.href = `https://huggingface.co/datasets/${s.repo_id}`; | |
| a.rel = "noopener"; | |
| p.append(a, document.createTextNode(` · ${t.revision} ${s.revision.slice(0, 8)}`)); | |
| d.append(p); | |
| } | |
| d.append(el("p", `${t.licence}: ${task.license.declared_tags.join(", ") || t.none}`)); | |
| d.append(el("p", `${task.split} · ${task.shots}-shot · ${task.instrument}`)); | |
| th.append(d); | |
| return th; | |
| } | |
| function render() { | |
| const t = T[state.lang]; | |
| const app = document.getElementById("app"); | |
| const pills = document.getElementById("pills"); | |
| pills.replaceChildren(); | |
| const pill = (b, txt) => { const p = el("span", undefined, "pill"); p.append(el("b", b), document.createTextNode(` ${txt}`)); pills.append(p); }; | |
| if (!state.tasks) { | |
| app.replaceChildren(el("p", `${t.loadError} (${state.error})`, "notice error")); | |
| return; | |
| } | |
| const rows = state.tasks.rows; | |
| // Models whose training corpus was scanned come first; models with unknown training data follow, in packet order. | |
| const scanned = m => state.index && [...state.index.observations.values()].some(o => o.model_id === m.model_id && o.contamination !== "unknown"); | |
| const models = state.packet ? [...state.packet.models].sort((a, b) => scanned(b) - scanned(a)) : []; | |
| const published = new Set(); | |
| if (state.index) for (const o of state.index.observations.values()) if (numberRule(o).show) published.add(o.task_id); | |
| pill(rows.length, t.pills.total); | |
| pills.append(el("span", t.pills.langs, "pill")); | |
| if (models.length) pill(models.length, t.pills.models); | |
| pill(published.size, t.pills.published); | |
| // Benchmarks where some model has no measurement yet (binding sentence 2026-10-29 20:18). | |
| const pendingEl = document.getElementById("pending"); | |
| const unmeasured = state.index ? rows.filter(r => models.some(m => { const o = state.index.observations.get(contract.key(r.id, m.model_id, state.packet.protocol_id)); return !o || o.state !== "ok"; })).length : 0; | |
| pendingEl.hidden = unmeasured === 0; | |
| pendingEl.textContent = unmeasured ? t.pending(unmeasured) : ""; | |
| const nodes = []; | |
| if (state.error && state.tasks) nodes.push(el("p", `${t.rejected}: ${state.error}`, "notice error")); | |
| else if (!state.packet) nodes.push(el("p", t.noPacket, "notice")); | |
| const controls = el("div", undefined, "controls"); | |
| const search = el("input"); search.type = "search"; search.value = state.query; | |
| const lang = el("select"); | |
| for (const [v, txt] of [["all", t.all], ["EN", "EN"], ["PL", "PL"]]) { const op = el("option", txt); op.value = v; lang.append(op); } | |
| lang.value = state.language; | |
| const show = el("select"); | |
| for (const [v, txt] of [["all", t.all], ["published", t.withResults]]) { const op = el("option", txt); op.value = v; show.append(op); } | |
| show.value = state.show; | |
| show.disabled = !state.packet; | |
| const clear = el("button", t.clear, "clear"); clear.type = "button"; | |
| const lab = (txt, input) => { const l = el("label", txt); l.append(input); return l; }; | |
| controls.append(lab(t.search, search), lab(t.language, lang), lab(t.show, show), clear); | |
| const summary = el("p", undefined, "summary"); | |
| summary.setAttribute("role", "status"); | |
| const wrap = el("div", undefined, "wrap"); | |
| wrap.tabIndex = 0; | |
| wrap.setAttribute("role", "region"); | |
| wrap.setAttribute("aria-label", t.results); | |
| const table = el("table"); | |
| table.setAttribute("aria-labelledby", "results-title"); | |
| const head = el("tr"); | |
| const th0 = el("th", t.benchmark); th0.scope = "col"; head.append(th0); | |
| if (models.length) for (const m of models) { | |
| const th = el("th", modelName(m)); th.scope = "col"; | |
| th.append(el("span", m.revision ? `${t.revision} ${m.revision.slice(0, 8)}` : "", "meta")); | |
| head.append(th); | |
| } else { const th = el("th", t.results); th.scope = "col"; head.append(th); } | |
| const thead = el("thead"); thead.append(head); | |
| const tbody = el("tbody"); | |
| table.append(thead, tbody); | |
| wrap.append(table); | |
| const empty = el("p", t.empty, "summary"); | |
| nodes.push(controls, summary, wrap, empty); | |
| app.replaceChildren(...nodes); | |
| function fill() { | |
| const q = state.query.trim().toLocaleLowerCase(state.lang); | |
| const visible = rows.filter(r => (state.language === "all" || r.language === state.language) && | |
| (state.show === "all" || published.has(r.id)) && | |
| `${r.name} ${r.benchmark_id} ${r.domain}`.toLocaleLowerCase(state.lang).includes(q)); | |
| const frag = document.createDocumentFragment(); | |
| for (const r of visible) { | |
| const tr = el("tr"); | |
| tr.append(taskHeader(r, t)); | |
| if (models.length) for (const m of models) tr.append(cell(r, m, t)); | |
| else tr.append(el("td", t.notPublished, "state")); | |
| frag.append(tr); | |
| } | |
| tbody.replaceChildren(frag); | |
| summary.textContent = `${t.shown} ${visible.length} ${t.of} ${rows.length} ${t.benchmarks}`; | |
| empty.hidden = visible.length !== 0; | |
| wrap.hidden = visible.length === 0; | |
| } | |
| search.addEventListener("input", () => { state.query = search.value; fill(); }); | |
| lang.addEventListener("change", () => { state.language = lang.value; fill(); }); | |
| show.addEventListener("change", () => { state.show = show.value; fill(); }); | |
| clear.addEventListener("click", () => { state.query = ""; state.language = "all"; state.show = "all"; search.value = ""; lang.value = "all"; show.value = "all"; fill(); search.focus(); }); | |
| fill(); | |
| const files = document.getElementById("files"); | |
| files.replaceChildren(el("p", t.files)); | |
| for (const f of state.files) { const p = el("p"); p.append(el("code", `${f.name} · sha256 ${f.sha}`)); files.append(p); } | |
| if (state.packet) files.append(el("p", `${t.packet}: ${t.run} ${state.packet.run_id} · ${t.protocol} ${state.packet.protocol_id}`)); | |
| } | |
| async function read(path, pin) { | |
| const res = await fetch(path, {cache:"no-store"}); | |
| if (!res.ok) throw new Error(`${path}: HTTP ${res.status}`); | |
| const bytes = await res.arrayBuffer(); | |
| const digest = await crypto.subtle.digest("SHA-256", bytes); | |
| const sha = [...new Uint8Array(digest)].map(b => b.toString(16).padStart(2, "0")).join(""); | |
| if (pin && sha !== pin) throw new Error(`${path}: pinned bytes changed`); | |
| state.files.push({name:path, sha}); | |
| return {bytes, sha}; | |
| } | |
| const json = r => JSON.parse(new TextDecoder().decode(r.bytes)); | |
| function setLang(l) { | |
| state.lang = l === "pl" ? "pl" : "en"; | |
| document.body.dataset.lang = state.lang; | |
| document.documentElement.lang = state.lang; | |
| document.querySelectorAll(".lang button").forEach(b => b.setAttribute("aria-pressed", String(b.dataset.set === state.lang))); | |
| try { localStorage.setItem("slayer-lang", state.lang); } catch (e) {} | |
| render(); | |
| } | |
| async function start() { | |
| try { | |
| const tasks = await read("tasks_50.json", contract.PINS.tasks); | |
| await read("results.schema.json", contract.PINS.schema); | |
| await read("METHOD.md", contract.PINS.method); | |
| await read("contamination_field_roles.json", contract.PINS.fieldRoles); | |
| state.tasks = json(tasks); | |
| if (state.tasks.rows.length !== 50) throw new Error("tasks_50.json: expected 50 rows"); | |
| } catch (e) { state.error = e.message; state.tasks = null; return; } | |
| try { | |
| const feed = json(await read("results-feed.json")); | |
| if (feed.results !== null) { | |
| if (feed.results !== "results.json") throw new Error("only results.json next to this page is allowed"); | |
| // The feed names the exact reviewed packet; any other bytes are refused. | |
| if (!/^[0-9a-f]{64}$/.test(feed.sha256 || "")) throw new Error("results-feed.json: missing sha256 of results.json"); | |
| const res = await read("results.json", feed.sha256); | |
| const packet = json(res); | |
| state.index = contract.validate(packet, state.tasks); | |
| state.packet = packet; | |
| state.packetSHA = res.sha; | |
| } | |
| } catch (e) { state.error = e.message; state.packet = null; state.index = null; } | |
| } | |
| document.querySelectorAll(".lang button").forEach(b => b.addEventListener("click", () => setLang(b.dataset.set))); | |
| let saved = null; try { saved = localStorage.getItem("slayer-lang"); } catch (e) {} | |
| const q = new URLSearchParams(location.search).get("lang"); | |
| start().finally(() => setLang(q || saved || "en")); | |
| })(); | |