#!/usr/bin/env node import { readFile, readdir, writeFile } from "node:fs/promises"; import { dirname, extname, join } from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = join(dirname(fileURLToPath(import.meta.url)), ".."); const sitemapPath = join(repoRoot, "system/public/sitemap.html"); const disksPath = join(repoRoot, "system/public/aesthetic.computer/disks"); const functionsPath = join(repoRoot, "system/netlify/functions"); const publicEndpoints = [ ["POST", "store-kidlisp", "Publish KidLisp source"], ["POST", "store-clock", "Publish a clock melody"], ["GET", "chat-messages", "Read and search public chat"], ["POST", "store-piece", "Publish a JavaScript piece"], ["POST", "track-media", "Register uploaded media"], ]; function escapeHTML(value) { return value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """); } function routeHref(prefix, name) { return `${prefix}/${name.split("/").map(encodeURIComponent).join("/")}`; } function routeList(names, prefix) { return names .map((name) => { const label = escapeHTML(name); const href = escapeHTML(routeHref(prefix, name)); return `
`; }) .join("\n"); } function hasHandler(source) { return /export\s+(?:(?:async\s+)?function|const|let|var)\s+handler\b|export\s+default\s+|exports\.handler\s*=|module\.exports\s*=/.test(source); } const diskFiles = (await readdir(disksPath)) .filter((name) => [".mjs", ".lisp"].includes(extname(name))) .sort((a, b) => a.localeCompare(b)); const javascriptDisks = diskFiles .filter((name) => name.endsWith(".mjs")) .map((name) => name.slice(0, -4)); const kidlispDisks = diskFiles .filter((name) => name.endsWith(".lisp")) .map((name) => name.slice(0, -5)); const javascriptRoutes = new Set(javascriptDisks); const kidlispOnlyDisks = kidlispDisks.filter((name) => !javascriptRoutes.has(name)); const diskRouteCount = javascriptDisks.length + kidlispOnlyDisks.length; const functionFiles = (await readdir(functionsPath)) .filter((name) => [".mjs", ".js"].includes(extname(name))); const functionHandlers = new Set(); for (const file of functionFiles) { const source = await readFile(join(functionsPath, file), "utf8"); if (hasHandler(source)) functionHandlers.add(file.slice(0, -extname(file).length)); } const handlers = [...functionHandlers].sort((a, b) => a.localeCompare(b)); const supportedNames = new Set(publicEndpoints.map(([, name]) => name)); const internalHandlers = handlers.filter((name) => !supportedNames.has(name)); const diskInventory = `/api/:fn; those are not all stable public integrations.