import { readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import { listFiles } from "./service-worker.mjs"; const ICONS = new Set([ "icon-192.png", "icon-512.png", "icon-maskable-512.png", "apple-touch-icon.png", ]); let files = 0, size = 0; function inspect(directory) { for (const name of readdirSync(directory)) { const path = join(directory, name); if (statSync(path).isDirectory()) { inspect(path); continue; } // The app icons are the only images: rendered from icon.svg, small. if (ICONS.has(name)) { if (statSync(path).size > 32_000) throw new Error(`App icon is unexpectedly large: ${path}`); files++; size += statSync(path).size; continue; } if (!/\.(html|js|css|svg|webmanifest)$/.test(name)) throw new Error(`Unexpected production asset: ${path}`); if (/reference|vendor|binjgb/i.test(path)) throw new Error(`Reference tool in production: ${path}`); const bytes = readFileSync(path), text = bytes.toString("utf8"); if (/Binjgb|_emulator_|binjgb\.wasm|\.reference\/pokecrystal/.test(text)) throw new Error(`Reference dependency in production: ${path}`); if (/data:(image|audio|application\/octet-stream)[^,]*;base64,/i.test(text)) throw new Error(`Embedded binary asset in production: ${path}`); files++; size += bytes.length; } } inspect("dist"); // The service worker precaches exactly the shell: every built file, and // nothing that could hold game data. const worker = readFileSync("dist/sw.js", "utf8"); const shell = JSON.parse(/const SHELL = (\[.*\]);/.exec(worker)?.[1] ?? "null"); if (!Array.isArray(shell)) throw new Error("dist/sw.js has no app shell list."); const expected = [ "./", ...listFiles("dist") .filter((file) => file !== "sw.js" && file !== "index.html") .map((file) => `./${file}`), ].sort(); if (JSON.stringify([...shell].sort()) !== JSON.stringify(expected)) throw new Error("The service worker's shell does not match the build."); if (shell.some((file) => /\.(gbc?|rom|sav)$|crystal-save/i.test(file))) throw new Error("The service worker would cache game data."); console.log( `Native release verified: ${files} files (HTML, JS, CSS, SVG, manifest, service worker and ${ICONS.size} icons), ${size} bytes; no emulator, ROM, extracted binary assets or reference checkout.`, );