Add initial VibeGuard browser extension

This commit is contained in:
Jordan Wages 2026-08-24 15:27:46 -05:00
commit 9c65a61ebb
43 changed files with 125275 additions and 2 deletions

16
tests/filter.test.ts Normal file
View file

@ -0,0 +1,16 @@
import { describe, expect, it } from "vitest";
import { applyResult, restore } from "../src/content/filter";
import { DEFAULT_SETTINGS } from "../src/shared/types";
describe("content filtering", () => {
it("collapses toxic content and restores it", () => {
const element = document.createElement("article");
document.body.append(element);
applyResult(element, { requestId: "1", id: "1", textHash: "x", label: "toxic", probability: .91, navigationId: "n" }, DEFAULT_SETTINGS);
expect(element.style.display).toBe("none");
expect(document.querySelector("[data-vibeguard-placeholder]")).not.toBeNull();
restore(element);
expect(element.style.display).toBe("");
expect(document.querySelector("[data-vibeguard-placeholder]")).toBeNull();
});
});

9
tests/hash.test.ts Normal file
View file

@ -0,0 +1,9 @@
import { describe, expect, it } from "vitest";
import { hashText, normalizeText } from "../src/shared/hash";
describe("text hashing", () => {
it("normalizes whitespace and casing", () => {
expect(normalizeText(" Hello\n WORLD ")).toBe("hello world");
expect(hashText("hello world")).toBe(hashText("HELLO\nWORLD"));
});
});

View file

@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";
import { validateManifest } from "../src/inference/model-metadata";
import { resolveOutputIndex } from "../src/inference/classifier";
const manifest = {
source: "wagesj45/toxic-comment-classifier",
revision: "abc123",
architecture: "DistilBertForSequenceClassification",
labels: { toxic: 1, nonToxic: 0, names: { toxic: 1, non_toxic: 0 } },
maxLength: 512,
quantization: "int8-dynamic",
runtime: "onnxruntime-web-wasm"
};
describe("model contract", () => {
it("accepts a binary manifest and resolves common output labels", () => {
expect(validateManifest(manifest)).toEqual(manifest);
expect(resolveOutputIndex("LABEL_1", manifest)).toBe(1);
expect(resolveOutputIndex("not-toxic", manifest)).toBe(0);
expect(resolveOutputIndex("toxic", manifest)).toBe(1);
});
it("rejects incomplete metadata", () => {
expect(() => validateManifest({ ...manifest, labels: { toxic: 1, nonToxic: 1, names: {} } })).toThrow();
expect(() => validateManifest({ ...manifest, maxLength: 0 })).toThrow();
expect(() => validateManifest({ ...manifest, labels: undefined as unknown as typeof manifest.labels })).toThrow();
});
});

23
tests/queue.test.ts Normal file
View file

@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { InferenceQueue } from "../src/inference/queue";
import type { InferenceRequest } from "../src/shared/types";
const request = (id: string, priority: 0 | 1 | 2 | 3): InferenceRequest => ({
id, text: `text ${id}`, site: "reddit", priority, requestId: id, navigationId: "nav"
});
describe("InferenceQueue", () => {
it("batches requests and caches equivalent text", async () => {
let calls = 0;
const queue = new InferenceQueue(async (requests) => {
calls += 1;
return requests.map((item) => ({ requestId: item.requestId, id: item.id, textHash: "hash", label: "toxic" as const, probability: .9, navigationId: item.navigationId }));
}, undefined, 10, 8, 0);
const first = await queue.enqueue(request("a", 0));
const second = await queue.enqueue({ ...request("b", 0), text: "text a" });
expect(first.probability).toBe(.9);
expect(second.requestId).toBe("b");
expect(calls).toBe(1);
expect(queue.cacheSize).toBe(1);
});
});