Release Healer Man 0.1.27
This commit is contained in:
1 parent
147c2804d4
commit
ed8b9e3719
6 files changed
+86
-6
No files matched your search
@@ -1992,3 +1992,26 @@ Vite's existing large-chunk advisory remains informational.
|
||||
eight-heading pixel comparisons, profiles, before/after timing JSON, and
|
||||
screenshots. Measurements use the same desktop Edge/RX 9070 XT setup as the
|
||||
preceding audit. These CPU improvements do not certify Thor performance.
|
||||
|
||||
# 2026-09-09 — bundled arena assets blocked by the browser content worker
|
||||
|
||||
- Reproduced the reported collision-matched fallback in the production build
|
||||
with the content worker active. Warnorken's `visual.glb?arena-runtime=1`
|
||||
returned 404 even though the file was bundled and directly served with 200.
|
||||
The worker omitted `/assets/game/arenas/` from its bundled asset prefixes and
|
||||
incorrectly rejected these files as uninstalled optional content.
|
||||
- Added the arena prefix to the worker. Installed updates still take priority;
|
||||
missing optional dungeon content still returns 404. Behavioral worker tests
|
||||
failed for all eight arenas before the fix and now cover visual, collision,
|
||||
and navigation requests with the runtime query parameter.
|
||||
- The production build passed. Reloading the same browser session activated
|
||||
the fixed worker without clearing storage or downloading content. Warnorken
|
||||
rendered textured stone geometry and Nagrand rendered its authored arena
|
||||
geometry; neither showed the loading/fallback banner after loading, and no
|
||||
new console errors were recorded. Checks used offline practice in the Codex
|
||||
browser at `http://127.0.0.1:4174/`, backed by a local test server on port 4173.
|
||||
- All 22 content tests and the eight-arena asset validator passed. Screenshots,
|
||||
before/after error logs, asset validation, and build output are saved under
|
||||
`playtest-artifacts/2026-09-09-arena-loading/`. The existing large-bundle
|
||||
advisory remains. This verifies the local browser build, not a deployment
|
||||
or an Android package.
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "healer-man",
|
||||
"version": "0.1.26",
|
||||
"version": "0.1.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "healer-man",
|
||||
"version": "0.1.26",
|
||||
"version": "0.1.27",
|
||||
"dependencies": {
|
||||
"@capacitor/android": "8.4.1",
|
||||
"@capacitor/core": "8.4.1",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "healer-man",
|
||||
"private": true,
|
||||
"version": "0.1.26",
|
||||
"version": "0.1.27",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ const BUNDLED_ASSET_PREFIXES = [
|
||||
"/assets/characters/wow335a-v1/manifest.json",
|
||||
"/assets/characters/wow335a-v1/human/",
|
||||
"/assets/creatures/wailing-caverns/",
|
||||
"/assets/game/arenas/",
|
||||
"/assets/game/dungeons/wailing-caverns/",
|
||||
];
|
||||
const BUNDLED_ASSET_PATHS = new Set([
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"versionName": "0.1.26",
|
||||
"versionCode": 1026
|
||||
"versionName": "0.1.27",
|
||||
"versionCode": 1027
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { runInNewContext } from "node:vm";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ARENA_STAGES } from "../game/arenaStages";
|
||||
import { BROWSER_CONTENT_CACHE_NAME } from "./contentManager";
|
||||
|
||||
const workerSource = readFileSync(
|
||||
@@ -7,7 +9,61 @@ const workerSource = readFileSync(
|
||||
"utf8",
|
||||
);
|
||||
|
||||
function contentWorker(cached?: Response) {
|
||||
const handlers = new Map<string, (event: unknown) => void>();
|
||||
const fetch = vi.fn(async () => new Response("bundled arena", { status: 200 }));
|
||||
const match = vi.fn(async () => cached);
|
||||
runInNewContext(workerSource, {
|
||||
self: {
|
||||
location: { origin: "https://healer.test" },
|
||||
addEventListener: (type: string, handler: (event: unknown) => void) => handlers.set(type, handler),
|
||||
},
|
||||
caches: { open: async () => ({ match }) },
|
||||
fetch,
|
||||
URL,
|
||||
Response,
|
||||
});
|
||||
return {
|
||||
fetch,
|
||||
match,
|
||||
request: (path: string) => {
|
||||
let response: Promise<Response> | undefined;
|
||||
handlers.get("fetch")!({
|
||||
request: new Request(new URL(path, "https://healer.test")),
|
||||
respondWith: (value: Promise<Response>) => { response = value; },
|
||||
});
|
||||
return response;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("browser content cache worker", () => {
|
||||
it.each(ARENA_STAGES)("loads bundled $name assets without an optional content install", async (stage) => {
|
||||
const worker = contentWorker();
|
||||
for (const asset of stage.assets) {
|
||||
const response = await worker.request(`${asset.url}?arena-runtime=1`);
|
||||
expect(response?.status).toBe(200);
|
||||
expect(await response?.text()).toBe("bundled arena");
|
||||
}
|
||||
expect(worker.fetch).toHaveBeenCalledTimes(stage.assets.length);
|
||||
});
|
||||
|
||||
it("prefers installed arena updates over bundled files, ignoring the runtime query", async () => {
|
||||
const worker = contentWorker(new Response("updated arena"));
|
||||
const response = await worker.request(`${ARENA_STAGES[0].assets[0].url}?arena-runtime=1`);
|
||||
expect(await response?.text()).toBe("updated arena");
|
||||
expect(worker.match).toHaveBeenCalledWith(expect.any(Request), { ignoreSearch: true });
|
||||
expect(worker.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("still blocks optional dungeon assets that have not been installed", async () => {
|
||||
const worker = contentWorker();
|
||||
const response = await worker.request("/assets/game/manastorm/optional/visual.glb");
|
||||
expect(response?.status).toBe(404);
|
||||
expect(await response?.text()).toBe("Optional content is not installed.");
|
||||
expect(worker.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("serves only cached same-origin asset requests from the active content cache", () => {
|
||||
expect(workerSource).toContain(`const CONTENT_CACHE_NAME = "${BROWSER_CONTENT_CACHE_NAME}";`);
|
||||
expect(workerSource).toContain('request.method !== "GET"');
|
||||
|
||||
Reference in new issue
Block a user