@mochi.js/profiles
Advanced tools
@@ -111,3 +111,3 @@ { | ||
| "path": "bot.chromeRuntime", | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill is task 0140 (stealth conformance) — tracked separately." | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill." | ||
| }, | ||
@@ -114,0 +114,0 @@ { |
@@ -19,9 +19,9 @@ { | ||
| "model": "generic-x64", | ||
| "cpuFamily": "intel-core", | ||
| "cores": 32, | ||
| "memoryGB": 64 | ||
| "cpuFamily": "intel-core-i7", | ||
| "cores": 8, | ||
| "memoryGB": 16 | ||
| }, | ||
| "display": { | ||
| "width": 1280, | ||
| "height": 800, | ||
| "width": 1920, | ||
| "height": 1080, | ||
| "dpr": 1, | ||
@@ -32,6 +32,6 @@ "colorDepth": 24, | ||
| "gpu": { | ||
| "vendor": "Google Inc.", | ||
| "renderer": "Generic Renderer", | ||
| "webglUnmaskedVendor": "Google Inc.", | ||
| "webglUnmaskedRenderer": "ANGLE (Generic)", | ||
| "vendor": "Intel Inc.", | ||
| "renderer": "Intel Iris Xe Graphics", | ||
| "webglUnmaskedVendor": "Google Inc. (Intel Inc.)", | ||
| "webglUnmaskedRenderer": "ANGLE (Intel Inc., Intel Iris Xe Graphics, OpenGL 4.1)", | ||
| "webglMaxTextureSize": 16384, | ||
@@ -77,3 +77,3 @@ "webglMaxColorAttachments": 8, | ||
| "uaCh": { | ||
| "sec-ch-ua": "\"Chromium\";v=\"147\", \"Not.A/Brand\";v=\"8\"", | ||
| "sec-ch-ua": "\"Google Chrome\";v=\"147\", \"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"147\"", | ||
| "sec-ch-ua-mobile": "?0", | ||
@@ -85,4 +85,3 @@ "sec-ch-ua-platform": "\"Linux\"", | ||
| "sec-ch-ua-model": "\"\"", | ||
| "sec-ch-ua-full-version": "\"147\"", | ||
| "sec-ch-ua-os": "\"linux\"" | ||
| "sec-ch-ua-full-version": "\"147.0.7727.138\"" | ||
| }, | ||
@@ -89,0 +88,0 @@ "entropyBudget": { |
@@ -15,3 +15,3 @@ # PROVENANCE — linux-chrome-stable | ||
| | imported at (UTC) | 2026-05-08T20:50:37.035Z | | ||
| | importer | `mochi profiles import` (task 0260) | | ||
| | importer | `mochi profiles import` | | ||
@@ -28,3 +28,3 @@ ## Multi-snapshot policy | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` (task 0070) are absent here. | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` are absent here. | ||
@@ -111,3 +111,3 @@ { | ||
| "path": "bot.chromeRuntime", | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill is task 0140 (stealth conformance) — tracked separately." | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill." | ||
| }, | ||
@@ -114,0 +114,0 @@ { |
@@ -15,3 +15,3 @@ # PROVENANCE — mac-brave-stable | ||
| | imported at (UTC) | 2026-05-08T20:50:38.368Z | | ||
| | importer | `mochi profiles import` (task 0260) | | ||
| | importer | `mochi profiles import` | | ||
@@ -28,3 +28,3 @@ ## Multi-snapshot policy | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` (task 0070) are absent here. | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` are absent here. | ||
@@ -111,3 +111,3 @@ { | ||
| "path": "bot.chromeRuntime", | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill is task 0140 (stealth conformance) — tracked separately." | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill." | ||
| }, | ||
@@ -114,0 +114,0 @@ { |
@@ -15,3 +15,3 @@ # PROVENANCE — mac-chrome-beta | ||
| | imported at (UTC) | 2026-05-08T20:50:35.794Z | | ||
| | importer | `mochi profiles import` (task 0260) | | ||
| | importer | `mochi profiles import` | | ||
@@ -28,3 +28,3 @@ ## Multi-snapshot policy | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` (task 0070) are absent here. | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` are absent here. | ||
@@ -111,3 +111,3 @@ { | ||
| "path": "bot.chromeRuntime", | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill is task 0140 (stealth conformance) — tracked separately." | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill." | ||
| }, | ||
@@ -114,0 +114,0 @@ { |
@@ -15,3 +15,3 @@ # PROVENANCE — mac-chrome-stable | ||
| | imported at (UTC) | 2026-05-08T20:50:33.447Z | | ||
| | importer | `mochi profiles import` (task 0260) | | ||
| | importer | `mochi profiles import` | | ||
@@ -28,3 +28,3 @@ ## Multi-snapshot policy | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` (task 0070) are absent here. | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` are absent here. | ||
@@ -17,3 +17,3 @@ # PROVENANCE — mac-m4-chrome-stable | ||
| - 2026-05-08, task 0051: `uaCh.sec-ch-ua-model` corrected from `"\"Mac\""` | ||
| - 2026-05-08, `uaCh.sec-ch-ua-model` corrected from `"\"Mac\""` | ||
| to `"\"\""` (empty quoted string). Real Chrome 147 desktop reports | ||
@@ -26,3 +26,3 @@ `model: ""` from `getHighEntropyValues`; the v0.4 capture incorrectly | ||
| - 2026-05-08, task 0070: headless-mode-leak corrections in | ||
| - 2026-05-08, headless-mode-leak corrections in | ||
| `baseline.manifest.json` + `profile.json`. The original capture ran | ||
@@ -29,0 +29,0 @@ against `--headless=new` Chromium-for-Testing, which emits several |
@@ -111,3 +111,3 @@ { | ||
| "path": "bot.chromeRuntime", | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill is task 0140 (stealth conformance) — tracked separately." | ||
| "comment": "Real Chrome exposes window.chrome.runtime; Chromium-for-Testing does not. Restoring chrome.runtime polyfill." | ||
| }, | ||
@@ -114,0 +114,0 @@ { |
@@ -15,3 +15,3 @@ # PROVENANCE — windows-chrome-stable | ||
| | imported at (UTC) | 2026-05-08T20:50:36.301Z | | ||
| | importer | `mochi profiles import` (task 0260) | | ||
| | importer | `mochi profiles import` | | ||
@@ -28,3 +28,3 @@ ## Multi-snapshot policy | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` (task 0070) are absent here. | ||
| None at import time. The harvester's `navigator` snapshot is captured by real Chrome (not headless), so the `--headless=new` artifacts that needed manual correction in `mac-m4-chrome-stable` are absent here. | ||
+2
-2
| { | ||
| "name": "@mochi.js/profiles", | ||
| "version": "0.1.1", | ||
| "version": "0.2.0", | ||
| "description": "Profile data fixtures for mochi — captured baselines (profile.json + Probe Manifest + audio bytes + canvas hashes) per device class.", | ||
@@ -42,3 +42,3 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@mochi.js/consistency": "^0.1.3" | ||
| "@mochi.js/consistency": "^0.1.4" | ||
| }, | ||
@@ -45,0 +45,0 @@ "publishConfig": { |
| import { describe, expect, it } from "bun:test"; | ||
| import { getProfile, KNOWN_PROFILE_IDS, VERSION } from "../index"; | ||
| import { | ||
| getProfile, | ||
| hasProfile, | ||
| KNOWN_PROFILE_IDS, | ||
| ProfileBaselineMissingError, | ||
| UnknownProfileIdError, | ||
| VERSION, | ||
| } from "../index"; | ||
| describe("@mochi.js/profiles (claim release)", () => { | ||
| describe("@mochi.js/profiles", () => { | ||
| it("exports VERSION", () => { | ||
@@ -14,5 +21,48 @@ expect(VERSION).toMatch(/^\d+\.\d+\.\d+/); | ||
| it("getProfile throws until phase 0.4", () => { | ||
| expect(() => getProfile("mac-m2-chrome-stable")).toThrow(/not yet implemented/); | ||
| it("getProfile loads a captured baseline (linux-chrome-stable)", async () => { | ||
| const p = await getProfile("linux-chrome-stable"); | ||
| expect(p.id).toBe("linux-chrome-stable"); | ||
| expect(p.engine).toBe("chromium"); | ||
| // The captured Linux baseline pins Chrome 147 — guards against the | ||
| // regression where launch.ts's placeholder shipped Chrome/131 to users. | ||
| expect(p.browser.name).toBe("chrome"); | ||
| expect(Number.parseInt(p.browser.minVersion, 10)).toBeGreaterThanOrEqual(147); | ||
| expect(p.os.name).toBe("linux"); | ||
| expect(p.userAgent).toMatch(/Chrome\/\d+/); | ||
| }); | ||
| it("getProfile loads every captured baseline declared in PROFILES_WITH_CAPTURED_BASELINE", async () => { | ||
| // Sanity: every id reported as having a baseline by hasProfile() must | ||
| // also load via getProfile() without throwing. | ||
| for (const id of KNOWN_PROFILE_IDS) { | ||
| const has = await hasProfile(id); | ||
| if (!has) continue; | ||
| const p = await getProfile(id); | ||
| expect(p.id).toBe(id); | ||
| } | ||
| }); | ||
| it("hasProfile returns true for ids with a captured baseline", async () => { | ||
| expect(await hasProfile("linux-chrome-stable")).toBe(true); | ||
| expect(await hasProfile("mac-m4-chrome-stable")).toBe(true); | ||
| }); | ||
| it("hasProfile returns false for known ids without a captured baseline", async () => { | ||
| expect(await hasProfile("mac-m2-chrome-stable")).toBe(false); | ||
| }); | ||
| it("hasProfile returns false for unknown ids", async () => { | ||
| expect(await hasProfile("not-a-real-profile-id")).toBe(false); | ||
| }); | ||
| it("getProfile throws ProfileBaselineMissingError for known ids without a baseline", async () => { | ||
| await expect(getProfile("mac-m2-chrome-stable")).rejects.toThrow(ProfileBaselineMissingError); | ||
| }); | ||
| it("getProfile throws UnknownProfileIdError for ids outside the catalog", async () => { | ||
| // Cast through unknown — callers using `as ProfileId` may slip past TS. | ||
| await expect(getProfile("not-a-real-profile-id" as never)).rejects.toThrow( | ||
| UnknownProfileIdError, | ||
| ); | ||
| }); | ||
| }); |
| // AUTO-GENERATED — do not edit. Run `bun run codegen` to regenerate. | ||
| // Source schema lives in schemas/. See scripts/codegen.ts and tasks/0003-schemas-and-codegen.md. | ||
| // Source schema lives in schemas/. See scripts/codegen.ts. | ||
| // Re-export of the canonical ProfileV1 type from @mochi.js/consistency. | ||
@@ -4,0 +4,0 @@ // @mochi.js/profiles is a data-fixture package; the type lives in consistency |
+105
-9
@@ -11,6 +11,6 @@ /** | ||
| * | ||
| * v0.0.1 claim release; first profile (`mac-m2-chrome-stable`) lands in phase 0.4. | ||
| * | ||
| * @see PLAN.md §5.6 and §12 | ||
| */ | ||
| import { join } from "node:path"; | ||
| export const VERSION = "0.0.1" as const; | ||
@@ -23,2 +23,4 @@ | ||
| import type { ProfileV1 } from "./generated/profile"; | ||
| /** ProfileV1 IDs that ship in the v1 catalog. */ | ||
@@ -33,3 +35,3 @@ export const KNOWN_PROFILE_IDS = [ | ||
| "linux-chrome-stable", | ||
| // Imported from harvester corpus per task 0260. | ||
| // Imported from harvester corpus per | ||
| "mac-chrome-stable", | ||
@@ -44,9 +46,103 @@ "mac-chrome-beta", | ||
| /** | ||
| * Resolve a profile by id. Lands in phase 0.4 when the first baseline is captured. | ||
| * The subset of {@link KNOWN_PROFILE_IDS} for which a captured baseline | ||
| * (`data/<id>/profile.json`) actually ships in the npm package. The rest are | ||
| * declared in the catalog so the type system tracks them, but {@link getProfile} | ||
| * throws {@link ProfileBaselineMissingError} for those — callers should | ||
| * fall back to a placeholder synthesis (see `@mochi.js/core.launch`'s | ||
| * `synthesizePlaceholderProfile` for the pattern). | ||
| */ | ||
| export function getProfile(_id: ProfileId): never { | ||
| throw new Error( | ||
| "@mochi.js/profiles.getProfile is not yet implemented (v0.0.1 claim). " + | ||
| "Lands in phase 0.4; see PLAN.md §12.", | ||
| ); | ||
| const PROFILES_WITH_CAPTURED_BASELINE: ReadonlySet<ProfileId> = new Set([ | ||
| "mac-m4-chrome-stable", | ||
| "mac-chrome-stable", | ||
| "mac-chrome-beta", | ||
| "linux-chrome-stable", | ||
| "mac-brave-stable", | ||
| "windows-chrome-stable", | ||
| ]); | ||
| /** Thrown when `id` isn't in {@link KNOWN_PROFILE_IDS} at all. */ | ||
| export class UnknownProfileIdError extends Error { | ||
| override readonly name = "UnknownProfileIdError"; | ||
| readonly id: string; | ||
| constructor(id: string) { | ||
| super( | ||
| `@mochi.js/profiles: unknown profile id "${id}". ` + | ||
| `Expected one of: ${KNOWN_PROFILE_IDS.join(", ")}.`, | ||
| ); | ||
| this.id = id; | ||
| } | ||
| } | ||
| /** | ||
| * Thrown when `id` is a known catalog entry but no captured baseline | ||
| * (`data/<id>/profile.json`) ships in the package — typically a | ||
| * declared-but-not-yet-captured device class. Callers may catch this | ||
| * and fall back to a synthesized placeholder profile. | ||
| */ | ||
| export class ProfileBaselineMissingError extends Error { | ||
| override readonly name = "ProfileBaselineMissingError"; | ||
| readonly id: ProfileId; | ||
| constructor(id: ProfileId) { | ||
| super( | ||
| `@mochi.js/profiles: profile "${id}" is declared in KNOWN_PROFILE_IDS ` + | ||
| `but no captured baseline ships in the package. ` + | ||
| `Profiles with captured baselines: ${[...PROFILES_WITH_CAPTURED_BASELINE].join(", ")}.`, | ||
| ); | ||
| this.id = id; | ||
| } | ||
| } | ||
| /** True when `id` is a known catalog entry. */ | ||
| function isKnownProfileId(id: string): id is ProfileId { | ||
| return (KNOWN_PROFILE_IDS as readonly string[]).includes(id); | ||
| } | ||
| /** | ||
| * True when {@link getProfile} can successfully load `id` (i.e. the id is | ||
| * known AND a captured baseline ships). Useful for callers that want to | ||
| * decide between the real-baseline path and a placeholder synthesis without | ||
| * catching exceptions. | ||
| */ | ||
| export async function hasProfile(id: string): Promise<boolean> { | ||
| if (!isKnownProfileId(id)) return false; | ||
| if (!PROFILES_WITH_CAPTURED_BASELINE.has(id)) return false; | ||
| return await Bun.file(profilePath(id)).exists(); | ||
| } | ||
| /** | ||
| * Resolve a profile by id, loading the captured `data/<id>/profile.json` | ||
| * baseline that ships with this package. | ||
| * | ||
| * Throws: | ||
| * - {@link UnknownProfileIdError} if `id` isn't in {@link KNOWN_PROFILE_IDS}. | ||
| * - {@link ProfileBaselineMissingError} if `id` is known but no baseline | ||
| * ships in the package — callers may fall back to a placeholder. | ||
| */ | ||
| export async function getProfile(id: ProfileId): Promise<ProfileV1> { | ||
| if (!isKnownProfileId(id)) { | ||
| // Defensive: callers using `as ProfileId` may sneak unknown values past | ||
| // the type system. Surface a precise error rather than a file-not-found. | ||
| throw new UnknownProfileIdError(id); | ||
| } | ||
| if (!PROFILES_WITH_CAPTURED_BASELINE.has(id)) { | ||
| throw new ProfileBaselineMissingError(id); | ||
| } | ||
| const file = Bun.file(profilePath(id)); | ||
| if (!(await file.exists())) { | ||
| // Inconsistency between the in-source set and the on-disk data — | ||
| // surface as the same baseline-missing error so callers' fallback | ||
| // logic still kicks in. This shouldn't happen in published builds. | ||
| throw new ProfileBaselineMissingError(id); | ||
| } | ||
| return (await file.json()) as ProfileV1; | ||
| } | ||
| /** | ||
| * Absolute path to a profile's captured `profile.json`. Uses | ||
| * `import.meta.dir` so the lookup works both in-source (running from | ||
| * `packages/profiles/src/`) and after publish (the `data/` dir ships as a | ||
| * sibling of `src/` per the package's `files` array). | ||
| */ | ||
| function profilePath(id: ProfileId): string { | ||
| return join(import.meta.dir, "..", "data", id, "profile.json"); | ||
| } |
321823
1.64%11173
1.19%Updated