Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
72 lines
2.0 KiB
JavaScript
72 lines
2.0 KiB
JavaScript
import { readFile, readdir } from "node:fs/promises";
|
|
import { extname, join, relative, resolve } from "node:path";
|
|
|
|
const javascriptExtensions = new Set([".cjs", ".js", ".mjs"]);
|
|
const rawSvgDataUrlPattern =
|
|
/data:image\/svg\+xml(?:;charset=[^,]+)?,\s*\x3csvg\b/i;
|
|
const relativeSvgUrlPattern = /["']\.\/[^"']+\.svg["']/gi;
|
|
|
|
export async function packedFilesWithRawSvgDataUrls(packageRoot) {
|
|
const normalizedRoot = resolve(packageRoot);
|
|
const files = await listFiles(normalizedRoot);
|
|
const violations = [];
|
|
|
|
for (const file of files) {
|
|
if (!javascriptExtensions.has(extname(file))) {
|
|
continue;
|
|
}
|
|
|
|
const source = await readFile(file, "utf8");
|
|
if (rawSvgDataUrlPattern.test(source)) {
|
|
violations.push(relative(normalizedRoot, file).replaceAll("\\", "/"));
|
|
}
|
|
}
|
|
|
|
return violations.sort();
|
|
}
|
|
|
|
export async function packedFilesWithRelativeSvgUrls(packageRoot) {
|
|
const normalizedRoot = resolve(packageRoot);
|
|
const files = await listFiles(normalizedRoot);
|
|
const violations = [];
|
|
|
|
for (const file of files) {
|
|
if (!javascriptExtensions.has(extname(file))) {
|
|
continue;
|
|
}
|
|
|
|
const source = await readFile(file, "utf8");
|
|
if (hasConsumerUnresolvableRelativeSvgUrl(source)) {
|
|
violations.push(relative(normalizedRoot, file).replaceAll("\\", "/"));
|
|
}
|
|
}
|
|
|
|
return violations.sort();
|
|
}
|
|
|
|
function hasConsumerUnresolvableRelativeSvgUrl(source) {
|
|
for (const match of source.matchAll(relativeSvgUrlPattern)) {
|
|
const prefix = source.slice(Math.max(0, match.index - 32), match.index);
|
|
if (!/new URL\(\s*$/.test(prefix)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async function listFiles(directory) {
|
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
const files = [];
|
|
|
|
for (const entry of entries) {
|
|
const path = join(directory, entry.name);
|
|
if (entry.isDirectory()) {
|
|
files.push(...(await listFiles(path)));
|
|
} else if (entry.isFile()) {
|
|
files.push(path);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|