36 lines
1.5 KiB
TypeScript
36 lines
1.5 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { Glob } from "bun";
|
|
|
|
// AGENTS.md: src/core runs in a browser, a userscript and Bun.
|
|
const CORE_FORBIDDEN: [RegExp, string][] = [
|
|
[/\bdocument\b|\bwindow\b|\blocalStorage\b|\bsessionStorage\b/, "DOM global"],
|
|
[/\bBun\./, "Bun API"],
|
|
[/from\s+["']node:|import\(["']node:/, "node:* import"],
|
|
[/\bGM_\w+|\bGM\./, "GM_* API"],
|
|
[/from\s+["'](?:\.\.\/)+(?:web|server|userscript|cli)\//, "import from a front end"],
|
|
];
|
|
// spec §4: these server files stay web-standard so a Workers entry can be added later.
|
|
const PORTABLE_SERVER = ["app.ts", "storage.ts", "rate-limit.ts", "config.ts", "assets-path.ts"];
|
|
const PORTABLE_FORBIDDEN: [RegExp, string][] = [
|
|
[/\bBun\./, "Bun API"],
|
|
[/from\s+["']node:|import\(["']node:/, "node:* import"],
|
|
];
|
|
|
|
async function violations(pattern: string, rules: [RegExp, string][], only?: string[]) {
|
|
const problems: string[] = [];
|
|
for await (const file of new Glob(pattern).scan(".")) {
|
|
if (only && !only.some((name) => file.endsWith(`/${name}`))) continue;
|
|
const text = await Bun.file(file).text();
|
|
for (const [re, what] of rules) if (re.test(text)) problems.push(`${file}: ${what}`);
|
|
}
|
|
return problems;
|
|
}
|
|
|
|
test("src/core uses no platform APIs and imports no front end", async () => {
|
|
expect(await violations("src/core/**/*.ts", CORE_FORBIDDEN)).toEqual([]);
|
|
});
|
|
|
|
test("portable server files use only web-standard APIs", async () => {
|
|
expect(await violations("src/server/*.ts", PORTABLE_FORBIDDEN, PORTABLE_SERVER)).toEqual([]);
|
|
});
|