ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci / checks (push) Successful in 10m24s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 12m13s
ci / release (push) Successful in 8m57s
modules/update links aego/update (net/http, TLS, x509, archives) only off js; the web build gets a browser backend with the same section, settings and aego/update API, where Check and Apply fail and State stays idle. A test compares both builds' registrations. depcheck: wasm-runtime and wasm-try variants, update in wasmDenied, wasmDeniedStd (net/http, crypto/tls, crypto/x509, archive/*) with the wasm-reaches-std rule. wasmsize -prev: playground-size and try-size print the change since their last run. Loaders show the compressed download size: wasmGzip in an export's files.json, aego-*-gzip and data-wasm-gzip on the site; docstool writes the .wasm.gz copies. Demo game.wasm 33.9 -> 26.0 MB, gzip 8.07 -> 6.25 MB.
124 lines
4.4 KiB
JavaScript
124 lines
4.4 KiB
JavaScript
"use strict";
|
|
|
|
(function () {
|
|
function meta(name) {
|
|
const m = document.querySelector(`meta[name="${name}"]`);
|
|
return m ? m.content : "";
|
|
}
|
|
|
|
const root = meta("aego-root");
|
|
const runtimes = new Map();
|
|
let support = null;
|
|
|
|
function script(src) {
|
|
return new Promise((resolve, reject) => {
|
|
const s = document.createElement("script");
|
|
s.src = src;
|
|
s.onload = resolve;
|
|
s.onerror = () => reject(new Error("cannot load " + src));
|
|
document.head.appendChild(s);
|
|
});
|
|
}
|
|
|
|
function url(dir, file) {
|
|
const version = meta("aego-" + dir);
|
|
return new URL(root + dir + "/" + file + (version ? "?v=" + version : ""), location.href).href;
|
|
}
|
|
|
|
// seen marks a runtime this browser has started, by its versioned URL.
|
|
function seen(u, mark) {
|
|
try {
|
|
const key = "aego-seen:" + u.split("?")[0];
|
|
if (mark) localStorage.setItem(key, u);
|
|
return localStorage.getItem(key) === u;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// cached trusts the mark: versioned runtimes are immutable, and probing the HTTP cache would hold its lock on the same URL.
|
|
function cached(u) {
|
|
return Promise.resolve(seen(u, false));
|
|
}
|
|
|
|
// count reads one branch of the download for progress, reporting at most once per frame.
|
|
async function count(stream, total, wire, report) {
|
|
const reader = stream.getReader();
|
|
let got = 0;
|
|
let queued = false;
|
|
const flush = () => {
|
|
queued = false;
|
|
report({ phase: "download", got, total, wire });
|
|
};
|
|
for (;;) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
got += value.byteLength;
|
|
if (!queued) {
|
|
queued = true;
|
|
requestAnimationFrame(flush);
|
|
}
|
|
}
|
|
report({ phase: "compile", got, total: total || got });
|
|
}
|
|
|
|
async function start(dir, file, readyHook, api, report) {
|
|
const u = url(dir, file);
|
|
const total = Number(meta("aego-" + dir + "-size")) || 0;
|
|
if (!support) {
|
|
const v = meta("aego-" + dir);
|
|
support = script(root + dir + "/wasm_exec.js" + (v ? "?v=" + v : "")).catch((e) => {
|
|
support = null;
|
|
throw e;
|
|
});
|
|
}
|
|
await support;
|
|
const ready = new Promise((resolve) => (globalThis[readyHook] = resolve));
|
|
const warm = seen(u, false);
|
|
if (report) report({ phase: warm ? "cache" : "download", got: 0, total });
|
|
let resp = await fetch(u);
|
|
if (!resp.ok) throw new Error(`${file}: HTTP ${resp.status}`);
|
|
if (report && !warm && resp.body) {
|
|
const [compile, progress] = resp.body.tee();
|
|
// the status counts the compressed size the server sends
|
|
const wire = (resp.headers.get("Content-Encoding") && Number(meta("aego-" + dir + "-gzip"))) || total;
|
|
count(progress, total, wire, report).catch(() => {});
|
|
resp = new Response(compile, { headers: resp.headers });
|
|
}
|
|
const go = new Go();
|
|
const { instance } = WebAssembly.instantiateStreaming
|
|
? await WebAssembly.instantiateStreaming(resp, go.importObject)
|
|
: await WebAssembly.instantiate(await resp.arrayBuffer(), go.importObject);
|
|
if (report) report({ phase: "start", got: total, total });
|
|
go.run(instance);
|
|
await ready;
|
|
seen(u, true);
|
|
const out = globalThis[api];
|
|
if (!out) throw new Error(file + " did not start");
|
|
if (out.error) throw new Error(out.error);
|
|
return out;
|
|
}
|
|
|
|
globalThis.aegoRuntime = function (dir, file, readyHook, api, report) {
|
|
const key = dir + "/" + file;
|
|
if (!runtimes.has(key)) {
|
|
const listeners = new Set();
|
|
const p = start(dir, file, readyHook, api, (s) => {
|
|
for (const fn of listeners) fn(s);
|
|
});
|
|
p.listeners = listeners;
|
|
runtimes.set(key, p);
|
|
p.catch(() => runtimes.delete(key));
|
|
}
|
|
const p = runtimes.get(key);
|
|
if (report) p.listeners.add(report);
|
|
return p;
|
|
};
|
|
|
|
globalThis.aegoRuntimeCached = function (dir, file) {
|
|
return cached(url(dir, file));
|
|
};
|
|
|
|
globalThis.aegoMB = (n) => (n / 1048576).toFixed(1) + " MB";
|
|
})();
|