| 1 | import assert from "node:assert/strict"; |
| 2 | import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs"; |
| 3 | import { tmpdir } from "node:os"; |
| 4 | import { join, dirname } from "node:path"; |
| 5 | import { checkDesktopHostBoundary, desktopHostViolations } from "./check-desktop-host-boundary.mjs"; |
| 6 | |
| 7 | const fixture = mkdtempSync(join(tmpdir(), "reasonix-host-boundary-")); |
| 8 | const write = (name, source) => { |
| 9 | const file = join(fixture, name); |
| 10 | mkdirSync(dirname(file), { recursive: true }); |
| 11 | writeFileSync(file, source); |
| 12 | }; |
| 13 | try { |
| 14 | const flagged = desktopHostViolations(` |
| 15 | const a = window.go?.main?.App; |
| 16 | const b = window.runtime?.EventsOn("x", () => {}); |
| 17 | const c = (window as unknown as { runtime?: unknown }).runtime; |
| 18 | const d = globalThis.window.runtime; |
| 19 | const e = window["go"]; |
| 20 | const f = window.reasonixDesktop?.browser; |
| 21 | import { EventsOn } from "../../wailsjs/runtime/runtime"; |
| 22 | export { X } from "@wailsapp/runtime"; |
| 23 | const lazy = () => import("../wailsjs/go/main/App"); |
| 24 | `, "flagged.ts"); |
| 25 | assert.deepEqual(flagged.map((entry) => entry.replace(/^\d+: /, "")), [ |
| 26 | "window.go", "window.runtime", "window.runtime", "window.runtime", 'window["go"]', "window.reasonixDesktop", |
| 27 | "import from ../../wailsjs/runtime/runtime", "export from @wailsapp/runtime", "dynamic import of ../wailsjs/go/main/App", |
| 28 | ]); |
| 29 | |
| 30 | const clean = desktopHostViolations(` |
| 31 | // window.go and window.runtime are only mentioned in this comment. |
| 32 | const paths = ["frontend/wailsjs/runtime/runtime.js", "window.runtime"]; |
| 33 | const wails = window.wails; |
| 34 | const phase = tab.runtime.phase; |
| 35 | const goCount = stats.go; |
| 36 | const host = shell.reasonixDesktop; |
| 37 | `, "clean.ts"); |
| 38 | assert.deepEqual(clean, [], "comments, strings and unrelated members are not host access"); |
| 39 | |
| 40 | const typeOnly = desktopHostViolations(` |
| 41 | import type * as GeneratedApp from "../../wailsjs/go/main/App"; |
| 42 | `, "type-only.ts"); |
| 43 | assert.deepEqual(typeOnly.map((entry) => entry.replace(/^\d+: /, "")), ["import from ../../wailsjs/go/main/App"], |
| 44 | "retired shell modules are rejected even when imported type-only"); |
| 45 | |
| 46 | write("lib/desktopHost.ts", "export const host = window.go?.main?.App && window.runtime;"); |
| 47 | write("__tests__/x.test.ts", "window.runtime = {};"); |
| 48 | write("components/Thing.tsx", "export const v = window.runtime;"); |
| 49 | write("app-runtime/ok.ts", "export const v = 1;"); |
| 50 | assert.deepEqual(checkDesktopHostBoundary(fixture), ["components/Thing.tsx:1: window.runtime"], |
| 51 | "only lib/desktopHost.ts and tests may reach the shell globals"); |
| 52 | console.log("PASS desktop host boundary gate flags shell-global access outside lib/desktopHost.ts"); |
| 53 | } finally { |
| 54 | rmSync(fixture, { recursive: true, force: true }); |
| 55 | } |
| 56 |