Release Healer Man 0.1.29

This commit is contained in:
phenom committed 2026-09-11 14:26:15 -04:00
1 parent 8c9809d3e0
commit 544c1bee91
120 files changed
+4363 -1036

No files matched your search

+64 -3
View File
@@ -1,8 +1,10 @@
import { createHash } from "node:crypto";
import { gzipSync, gunzipSync } from "node:zlib";
import { Capacitor } from "@capacitor/core";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
BROWSER_CONTENT_CACHE_NAME,
browserContentObjectPath,
checkForContentUpdates,
contentPackHasUpdate,
contentPackIsBundled,
@@ -120,7 +122,8 @@ describe("browser content packs", () => {
error: "",
});
const active = storage.stores.get(BROWSER_CONTENT_CACHE_NAME);
expect(await (await active?.match("http://localhost/assets/characters/test.glb"))?.text()).toBe("first-model");
expect(await (await active?.match(browserContentObjectPath(manifest.files["/assets/characters/test.glb"])))?.text()).toBe("first-model");
const originalObject = browserContentObjectPath(manifest.files["/assets/characters/test.glb"]);
manifest = contentManifest("content-two", "updated.glb", updatedBytes);
await checkForContentUpdates();
@@ -129,12 +132,13 @@ describe("browser content packs", () => {
corruptUpdate = true;
await expect(installContentPack("characters:test")).rejects.toThrow(/verification/i);
expect(await (await active?.match("http://localhost/assets/characters/test.glb"))?.text()).toBe("first-model");
expect(await (await active?.match(originalObject))?.text()).toBe("first-model");
corruptUpdate = false;
await installContentPack("characters:test");
expect(contentPackHasUpdate("characters:test")).toBe(false);
expect(await (await active?.match("http://localhost/assets/characters/test.glb"))?.text()).toBe("updated-model");
expect(await (await active?.match(browserContentObjectPath(manifest.files["/assets/characters/test.glb"])))?.text()).toBe("updated-model");
expect(await active?.match(originalObject)).toBeUndefined();
await removeContentPack("characters:test");
expect(contentPackIsInstalled("characters:test")).toBe(false);
@@ -151,6 +155,63 @@ describe("browser content packs", () => {
expect(contentPackIsUsable("core:ui")).toBe(true);
});
it("stores a compressed duplicate once and retains it until its last pack is removed", async () => {
const original = new TextEncoder().encode("original shared geometry ".repeat(200));
const compressed = gzipSync(original);
const firstPath = "/assets/characters/shared-first.glb";
const secondPath = "/assets/characters/shared-second.glb";
const thirdPath = "/assets/characters/shared-third.glb";
const base = contentManifest("compressed", "original.glb", original);
const entry = { ...base.files["/assets/characters/test.glb"], download: {
url: "/content/objects/shared.glb.gz", encoding: "gzip" as const, sha256: sha256(compressed), bytes: compressed.length,
} };
const manifest: ContentManifest = { ...base, packs: [
{ ...base.packs[0], id: "characters:shared-first", fileCount: 2 },
{ ...base.packs[0], id: "characters:shared-second" },
], files: {
[firstPath]: { ...entry, packId: "characters:shared-first" },
[secondPath]: { ...entry, packId: "characters:shared-first" },
[thirdPath]: { ...entry, packId: "characters:shared-second" },
} };
const storage = new MemoryCacheStorage();
vi.spyOn(Capacitor, "isNativePlatform").mockReturnValue(false);
vi.stubGlobal("caches", storage as unknown as CacheStorage);
const fetcher = vi.fn(async (url: RequestInfo | URL) => String(url).endsWith("manifest.json") ? Response.json(manifest) : new Response(compressed));
vi.stubGlobal("fetch", fetcher);
await checkForContentUpdates();
await installContentPack("characters:shared-first");
const active = storage.stores.get(BROWSER_CONTENT_CACHE_NAME)!;
expect(active.responses.size).toBe(3);
const objectPath = browserContentObjectPath(entry);
expect(gunzipSync(Buffer.from(await (await active.match(objectPath))!.arrayBuffer()))).toEqual(Buffer.from(original));
expect(getContentManagerSnapshot().totalBytes).toBe(compressed.length);
await installContentPack("characters:shared-second");
expect(getContentManagerSnapshot().totalBytes).toBe(0);
expect(fetcher.mock.calls.filter(([url]) => String(url).endsWith(".gz"))).toHaveLength(1);
await removeContentPack("characters:shared-first");
expect(await active.match(objectPath)).toBeDefined();
expect(contentPackIsUsable("characters:shared-second")).toBe(true);
await removeContentPack("characters:shared-second");
expect(active.responses.size).toBe(0);
});
it("rejects a valid gzip payload that expands to the wrong original model", async () => {
const original = new TextEncoder().encode("expected model");
const compressed = gzipSync("different model");
const base = contentManifest("bad-compressed", "model.glb", original);
const manifest = { ...base, files: { "/assets/characters/test.glb": { ...base.files["/assets/characters/test.glb"],
download: { url: "/content/objects/model.glb.gz", encoding: "gzip", sha256: sha256(compressed), bytes: compressed.length },
} } };
const storage = new MemoryCacheStorage();
vi.spyOn(Capacitor, "isNativePlatform").mockReturnValue(false);
vi.stubGlobal("caches", storage as unknown as CacheStorage);
vi.stubGlobal("fetch", vi.fn(async (url: RequestInfo | URL) => String(url).endsWith("manifest.json") ? Response.json(manifest) : new Response(compressed)));
await checkForContentUpdates();
await expect(installContentPack("characters:test")).rejects.toThrow(/expanded content failed verification/i);
expect(storage.stores.get(BROWSER_CONTENT_CACHE_NAME)?.responses.size).toBe(0);
expect(contentPackIsUsable("characters:test")).toBe(false);
});
it("uses source-served dungeons in development and the content gate in shipped builds", () => {
vi.spyOn(Capacitor, "isNativePlatform").mockReturnValue(false);
vi.stubEnv("DEV", true);