#!/usr/bin/env node /** * Domain Benchmark Suite — runs 10 domains through full pipeline. * * For each domain: * 1. Generate fullstack project * 2. npm install + tsc build * 3. Start server, test Health, Auth, CRUD * * Output: benchmark-report.md */ import { execSync, spawn } from "node:child_process"; import { existsSync, rmSync, readFileSync, writeFileSync } from "node:fs"; import { resolve, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import * as http from "node:http"; const __dirname = dirname(resolve(fileURLToPath(import.meta.url))); const WORKSPACE = resolve(__dirname, "..", ".."); const BENCH_DIR = resolve(WORKSPACE, ".tmp-benchmark"); const DOMAINS = [ { name: "PetCare", label: "宠物管理", prd: { projectName: "PetCare", domain: "pet", summary: "宠物健康管理应用", features: [{ name: "宠物档案", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "pets", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "name", type: "TEXT", constraints: "NOT NULL" }, { name: "species", type: "TEXT" }, { name: "breed", type: "TEXT" }, { name: "weight_kg", type: "REAL" }, { name: "created_at", type: "TEXT" }] }, { table: "schedules", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "pet_id", type: "TEXT", constraints: "NOT NULL, FK → pets.id" }, { name: "type", type: "TEXT", constraints: "NOT NULL" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "scheduled_at", type: "TEXT", constraints: "NOT NULL" }, { name: "completed", type: "INTEGER" }, { name: "created_at", type: "TEXT" }] }, { table: "daily_logs", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "pet_id", type: "TEXT", constraints: "NOT NULL, FK → pets.id" }, { name: "category", type: "TEXT", constraints: "NOT NULL" }, { name: "value", type: "TEXT", constraints: "NOT NULL" }, { name: "logged_at", type: "TEXT" }] }], apiDesign: [{ resource: "pets", basePath: "/api/pets", endpoints: [{ method: "GET", path: "/api/pets" }, { method: "POST", path: "/api/pets" }, { method: "GET", path: "/api/pets/:id" }, { method: "PUT", path: "/api/pets/:id" }, { method: "DELETE", path: "/api/pets/:id" }] }] }, }, { name: "CRM", label: "客户关系管理", prd: { projectName: "CRM", domain: "enterprise", summary: "CRM系统", features: [{ name: "客户管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "customers", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "name", type: "TEXT", constraints: "NOT NULL" }, { name: "company", type: "TEXT" }, { name: "email", type: "TEXT" }, { name: "phone", type: "TEXT" }, { name: "status", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "deals", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "customer_id", type: "TEXT", constraints: "NOT NULL, FK → customers.id" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "amount", type: "REAL" }, { name: "stage", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "activities", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "customer_id", type: "TEXT", constraints: "NOT NULL, FK → customers.id" }, { name: "type", type: "TEXT", constraints: "NOT NULL" }, { name: "note", type: "TEXT", constraints: "NOT NULL" }, { name: "created_at", type: "TEXT" }] }], apiDesign: [{ resource: "customers", basePath: "/api/customers", endpoints: [{ method: "GET", path: "/api/customers" }, { method: "POST", path: "/api/customers" }, { method: "GET", path: "/api/customers/:id" }, { method: "PUT", path: "/api/customers/:id" }, { method: "DELETE", path: "/api/customers/:id" }] }] }, }, { name: "Inventory", label: "库存管理", prd: { projectName: "Inventory", domain: "ecommerce", summary: "库存管理", features: [{ name: "商品管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "products", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "name", type: "TEXT", constraints: "NOT NULL" }, { name: "sku", type: "TEXT" }, { name: "quantity", type: "INTEGER" }, { name: "price", type: "REAL" }, { name: "created_at", type: "TEXT" }] }, { table: "transactions", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "product_id", type: "TEXT", constraints: "NOT NULL, FK → products.id" }, { name: "type", type: "TEXT", constraints: "NOT NULL" }, { name: "quantity", type: "INTEGER", constraints: "NOT NULL" }, { name: "note", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }], apiDesign: [{ resource: "products", basePath: "/api/products", endpoints: [{ method: "GET", path: "/api/products" }, { method: "POST", path: "/api/products" }, { method: "GET", path: "/api/products/:id" }, { method: "PUT", path: "/api/products/:id" }, { method: "DELETE", path: "/api/products/:id" }] }] }, }, { name: "TicketSystem", label: "工单系统", prd: { projectName: "TicketSystem", domain: "enterprise", summary: "工单管理", features: [{ name: "工单管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "tickets", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "description", type: "TEXT", constraints: "NOT NULL" }, { name: "priority", type: "TEXT" }, { name: "status", type: "TEXT" }, { name: "assignee_id", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "comments", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "ticket_id", type: "TEXT", constraints: "NOT NULL, FK → tickets.id" }, { name: "user_id", type: "TEXT", constraints: "NOT NULL" }, { name: "content", type: "TEXT", constraints: "NOT NULL" }, { name: "created_at", type: "TEXT" }] }], apiDesign: [{ resource: "tickets", basePath: "/api/tickets", endpoints: [{ method: "GET", path: "/api/tickets" }, { method: "POST", path: "/api/tickets" }, { method: "GET", path: "/api/tickets/:id" }, { method: "PUT", path: "/api/tickets/:id" }, { method: "DELETE", path: "/api/tickets/:id" }] }] }, }, { name: "BlogCMS", label: "博客CMS", prd: { projectName: "BlogCMS", domain: "note", summary: "博客系统", features: [{ name: "文章管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "posts", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "author_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "content", type: "TEXT", constraints: "NOT NULL" }, { name: "slug", type: "TEXT" }, { name: "status", type: "TEXT" }, { name: "created_at", type: "TEXT" }, { name: "updated_at", type: "TEXT" }] }, { table: "tags", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "name", type: "TEXT", constraints: "UNIQUE NOT NULL" }, { name: "created_at", type: "TEXT" }] }, { table: "post_tags", fields: [{ name: "post_id", type: "TEXT", constraints: "NOT NULL, FK → posts.id" }, { name: "tag_id", type: "TEXT", constraints: "NOT NULL, FK → tags.id" }] }], apiDesign: [{ resource: "posts", basePath: "/api/posts", endpoints: [{ method: "GET", path: "/api/posts" }, { method: "POST", path: "/api/posts" }, { method: "GET", path: "/api/posts/:id" }, { method: "PUT", path: "/api/posts/:id" }, { method: "DELETE", path: "/api/posts/:id" }] }] }, }, { name: "ProjectMgmt", label: "项目管理", prd: { projectName: "ProjectMgmt", domain: "enterprise", summary: "项目管理", features: [{ name: "项目管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "projects", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "name", type: "TEXT", constraints: "NOT NULL" }, { name: "description", type: "TEXT" }, { name: "status", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "tasks", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "project_id", type: "TEXT", constraints: "NOT NULL, FK → projects.id" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "assignee_id", type: "TEXT" }, { name: "status", type: "TEXT" }, { name: "priority", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }], apiDesign: [{ resource: "projects", basePath: "/api/projects", endpoints: [{ method: "GET", path: "/api/projects" }, { method: "POST", path: "/api/projects" }, { method: "GET", path: "/api/projects/:id" }, { method: "PUT", path: "/api/projects/:id" }, { method: "DELETE", path: "/api/projects/:id" }] }] }, }, { name: "HRSystem", label: "人力资源", prd: { projectName: "HRSystem", domain: "enterprise", summary: "人力资源管理", features: [{ name: "员工管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "employees", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "user_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "name", type: "TEXT", constraints: "NOT NULL" }, { name: "department", type: "TEXT", constraints: "NOT NULL" }, { name: "position", type: "TEXT", constraints: "NOT NULL" }, { name: "hire_date", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "attendances", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "employee_id", type: "TEXT", constraints: "NOT NULL, FK → employees.id" }, { name: "check_in", type: "TEXT" }, { name: "check_out", type: "TEXT" }, { name: "date", type: "TEXT", constraints: "NOT NULL" }, { name: "status", type: "TEXT" }] }, { table: "leaves", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "employee_id", type: "TEXT", constraints: "NOT NULL, FK → employees.id" }, { name: "type", type: "TEXT", constraints: "NOT NULL" }, { name: "start_date", type: "TEXT", constraints: "NOT NULL" }, { name: "end_date", type: "TEXT", constraints: "NOT NULL" }, { name: "status", type: "TEXT" }, { name: "reason", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }], apiDesign: [{ resource: "employees", basePath: "/api/employees", endpoints: [{ method: "GET", path: "/api/employees" }, { method: "POST", path: "/api/employees" }, { method: "GET", path: "/api/employees/:id" }, { method: "PUT", path: "/api/employees/:id" }, { method: "DELETE", path: "/api/employees/:id" }] }] }, }, { name: "AssetMgmt", label: "资产管理", prd: { projectName: "AssetMgmt", domain: "enterprise", summary: "资产管理", features: [{ name: "资产管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "assets", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "name", type: "TEXT", constraints: "NOT NULL" }, { name: "category", type: "TEXT", constraints: "NOT NULL" }, { name: "serial_number", type: "TEXT" }, { name: "status", type: "TEXT" }, { name: "assigned_to", type: "TEXT" }, { name: "purchase_date", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "assignments", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "asset_id", type: "TEXT", constraints: "NOT NULL, FK → assets.id" }, { name: "user_id", type: "TEXT", constraints: "NOT NULL" }, { name: "assigned_at", type: "TEXT" }, { name: "returned_at", type: "TEXT" }, { name: "note", type: "TEXT" }] }], apiDesign: [{ resource: "assets", basePath: "/api/assets", endpoints: [{ method: "GET", path: "/api/assets" }, { method: "POST", path: "/api/assets" }, { method: "GET", path: "/api/assets/:id" }, { method: "PUT", path: "/api/assets/:id" }, { method: "DELETE", path: "/api/assets/:id" }] }] }, }, { name: "CourseMgmt", label: "课程管理", prd: { projectName: "CourseMgmt", domain: "education", summary: "课程管理", features: [{ name: "课程管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "courses", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "instructor_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "description", type: "TEXT" }, { name: "price", type: "REAL" }, { name: "status", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "lessons", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "course_id", type: "TEXT", constraints: "NOT NULL, FK → courses.id" }, { name: "title", type: "TEXT", constraints: "NOT NULL" }, { name: "content", type: "TEXT" }, { name: "sort_order", type: "INTEGER" }, { name: "created_at", type: "TEXT" }] }, { table: "enrollments", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "course_id", type: "TEXT", constraints: "NOT NULL, FK → courses.id" }, { name: "user_id", type: "TEXT", constraints: "NOT NULL" }, { name: "enrolled_at", type: "TEXT" }, { name: "progress", type: "INTEGER" }] }], apiDesign: [{ resource: "courses", basePath: "/api/courses", endpoints: [{ method: "GET", path: "/api/courses" }, { method: "POST", path: "/api/courses" }, { method: "GET", path: "/api/courses/:id" }, { method: "PUT", path: "/api/courses/:id" }, { method: "DELETE", path: "/api/courses/:id" }] }] }, }, { name: "AppointmentScheduling", label: "预约排程", prd: { projectName: "Appointment", domain: "enterprise", summary: "预约管理", features: [{ name: "预约管理", priority: "P0" }], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] }, arch: { databaseSchema: [{ table: "appointments", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "user_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" }, { name: "provider_id", type: "TEXT" }, { name: "service", type: "TEXT", constraints: "NOT NULL" }, { name: "scheduled_at", type: "TEXT", constraints: "NOT NULL" }, { name: "duration_min", type: "INTEGER" }, { name: "status", type: "TEXT" }, { name: "notes", type: "TEXT" }, { name: "created_at", type: "TEXT" }] }, { table: "availability", fields: [{ name: "id", type: "TEXT", constraints: "PK" }, { name: "provider_id", type: "TEXT", constraints: "NOT NULL" }, { name: "day_of_week", type: "INTEGER", constraints: "NOT NULL" }, { name: "start_time", type: "TEXT", constraints: "NOT NULL" }, { name: "end_time", type: "TEXT", constraints: "NOT NULL" }] }], apiDesign: [{ resource: "appointments", basePath: "/api/appointments", endpoints: [{ method: "GET", path: "/api/appointments" }, { method: "POST", path: "/api/appointments" }, { method: "GET", path: "/api/appointments/:id" }, { method: "PUT", path: "/api/appointments/:id" }, { method: "DELETE", path: "/api/appointments/:id" }] }] }, }, ]; // --- HTTP helpers --- function httpPost(url, body, token) { return new Promise((resolve, reject) => { const payload = JSON.stringify(body); const u = new URL(url); const headers = { "Content-Type": "application/json", "Content-Length": payload.length.toString() }; if (token) headers["Authorization"] = `Bearer ${token}`; const req = http.request({ hostname: u.hostname, port: u.port, path: u.pathname, method: "POST", headers, timeout: 10000 }, (res) => { let d = ""; res.on("data", c => d += c); res.on("end", () => { try { resolve(JSON.parse(d)); } catch { resolve({ raw: d, status: res.statusCode }); } }); }); req.on("error", reject); req.write(payload); req.end(); }); } function httpGet(url, token) { return new Promise((resolve, reject) => { const u = new URL(url); const headers = {}; if (token) headers["Authorization"] = `Bearer ${token}`; http.get({ hostname: u.hostname, port: u.port, path: u.pathname, headers, timeout: 10000 }, (res) => { let d = ""; res.on("data", c => d += c); res.on("end", () => { try { resolve(JSON.parse(d)); } catch { resolve({ raw: d, status: res.statusCode }); } }); }).on("error", reject); }); } function httpPut(url, body, token) { return new Promise((resolve, reject) => { const payload = JSON.stringify(body); const u = new URL(url); const headers = { "Content-Type": "application/json", "Content-Length": payload.length.toString() }; if (token) headers["Authorization"] = `Bearer ${token}`; const req = http.request({ hostname: u.hostname, port: u.port, path: u.pathname, method: "PUT", headers, timeout: 10000 }, (res) => { let d = ""; res.on("data", c => d += c); res.on("end", () => { try { resolve(JSON.parse(d)); } catch { resolve({ raw: d, status: res.statusCode }); } }); }); req.on("error", reject); req.write(payload); req.end(); }); } function waitForServer(url, timeoutMs = 20000) { return new Promise((resolve, reject) => { const start = Date.now(); function check() { http.get(url, (res) => { res.resume(); res.on("end", resolve); }).on("error", () => { if (Date.now() - start > timeoutMs) reject(new Error("Server never started")); else setTimeout(check, 500); }); } check(); }); } async function runCustomCommand(domainLabel, tmpDir, installOnly = false) { try { execSync("npm install --no-audit --no-fund", { cwd: tmpDir, encoding: "utf-8", timeout: 180000, stdio: "pipe" }); return true; } catch { return false; } } async function runDomain(i, domain) { console.log(`\n${"─".repeat(60)}`); console.log(`[${i + 1}/${DOMAINS.length}] ${domain.label} (${domain.name})`); console.log(`${"─".repeat(60)}`); const result = { name: domain.name, label: domain.label, files: 0, build: false, health: false, register: false, login: false, create: false, read: false, update: false, delete: false, errors: [], timeMs: 0, }; const t0 = Date.now(); const tmpDir = `${BENCH_DIR}/${domain.name}`; rmSync(tmpDir, { recursive: true, force: true }); // 1. Generate try { const { composeFullstack, writeFullstack } = await import("../../scripts/fullstack-composer-agent.mjs"); const genResult = await composeFullstack(domain.prd, domain.arch); if (genResult.error) { result.errors.push(`Generation: ${genResult.error} ${genResult.message}`); result.timeMs = Date.now() - t0; return result; } writeFullstack(genResult, tmpDir); result.files = genResult.stats.totalFiles; console.log(` Generated ${result.files} files`); } catch (e) { result.errors.push(`Generation: ${e.message}`); result.timeMs = Date.now() - t0; return result; } // Clean stale db try { rmSync(tmpDir + "/apps/api/data", { recursive: true, force: true }); } catch {} // 2. Install console.log(" npm install..."); try { execSync("npm install --no-audit --no-fund", { cwd: tmpDir, encoding: "utf-8", timeout: 180000, stdio: "pipe" }); console.log(" ✅ npm install"); } catch (e) { result.errors.push(`npm install: ${(e.stderr || "").slice(0, 200)}`); console.log(" ❌ npm install failed"); result.timeMs = Date.now() - t0; return result; } // 3. Build (tsc) console.log(" tsc..."); try { execSync("npx tsc --noEmit", { cwd: tmpDir + "/apps/api", encoding: "utf-8", timeout: 60000, stdio: "pipe" }); result.build = true; console.log(" ✅ tsc"); } catch (e) { result.errors.push(`tsc: ${(e.stdout || e.stderr || "").slice(0, 200)}`); console.log(" ❌ tsc failed"); result.timeMs = Date.now() - t0; return result; } // 4. Server + API const port = 4100 + i; const proc = spawn("npx", ["tsx", "src/index.ts"], { cwd: tmpDir + "/apps/api", env: { ...process.env, JWT_SECRET: `bench-${i}`, DATABASE_URL: ":memory:", PORT: String(port) }, stdio: "pipe", }); try { await waitForServer(`http://localhost:${port}/api/health`); console.log(" ✅ Server started"); } catch (e) { result.errors.push(`Server start: ${e.message}`); console.log(" ❌ Server failed to start"); proc.kill("SIGTERM"); result.timeMs = Date.now() - t0; return result; } result.health = true; // Auth const username = `u${Date.now()}`; try { const reg = await httpPost(`http://localhost:${port}/api/auth/register`, { username, password: "123456" }); if (!reg.token || !reg.user?.id) throw new Error(`malformed: ${JSON.stringify(reg).slice(0, 100)}`); const token = reg.token; const userId = reg.user.id; result.register = true; console.log(" ✅ Register"); // Login try { const login = await httpPost(`http://localhost:${port}/api/auth/login`, { username, password: "123456" }); if (login.token) result.login = true; console.log(" ✅ Login"); } catch (e) { result.errors.push(`Login: ${e.message}`); console.log(" ❌ Login"); } // CRUD — find resource endpoint with POST const resource = (domain.arch.apiDesign || [])[0]; const crudPath = resource?.basePath || ""; const base = `http://localhost:${port}${crudPath}`; // Build create payload: FK→userId, required fields→sample const table = (domain.arch.databaseSchema || [])[0]; const payload = {}; for (const f of (table?.fields || [])) { if (f.name === "id") continue; const fname = f.name.replace(/_([a-z])/g, (_, c) => c.toUpperCase()); if (f.constraints?.includes("FK")) { payload[fname] = userId; } else if (f.constraints?.includes("NOT NULL")) { if (f.type?.toUpperCase().includes("INT") || f.type?.toUpperCase().includes("REAL")) payload[fname] = 42; else if (f.type?.toUpperCase().includes("TEXT")) payload[fname] = `bench-${domain.name}`; } } // Create let createdId; try { const create = await httpPost(base, payload, token); createdId = create.data?.id; if (createdId) { result.create = true; console.log(" ✅ Create"); } else { result.errors.push(`Create: no id in ${JSON.stringify(create).slice(0, 200)}`); console.log(" ❌ Create: no id"); } } catch (e) { result.errors.push(`Create: ${e.message}`); console.log(" ❌ Create"); } // Read if (createdId) { try { const list = await httpGet(base, token); if (list.data?.length > 0) result.read = true; console.log(` ${result.read ? "✅" : "❌"} Read`); } catch (e) { result.errors.push(`Read: ${e.message}`); console.log(" ❌ Read"); } // Update try { const upd = await httpPut(`${base}/${createdId}`, { ...payload }, token); if (upd.data?.id) result.update = true; console.log(` ${result.update ? "✅" : "❌"} Update`); // Delete try { const del = await httpRequest("DELETE", `${base}/${createdId}`, null, token); if (del >= 200 && del < 300) result.delete = true; console.log(` ${result.delete ? "✅" : "❌"} Delete (HTTP ${del})`); } catch (e) { result.errors.push(`Delete: ${e.message}`); console.log(" ❌ Delete"); } } catch (e) { // PUT failed but try DELETE anyway with POST (some frameworks lack PUT) try { const del = await httpRequest("DELETE", `${base}/${createdId}`, null, token); if (del >= 200 && del < 300) result.delete = true; console.log(` ${result.delete ? "✅" : "❌"} Delete (HTTP ${del})`); } catch (e2) { result.errors.push(`Update/Delete: ${e.message}; ${e2.message}`); } } } } catch (e) { result.errors.push(`Auth: ${e.message}`); console.log(" ❌ Auth failed"); } proc.kill("SIGTERM"); result.timeMs = Date.now() - t0; return result; } function httpRequest(method, url, body, token) { return new Promise((resolve, reject) => { const u = new URL(url); const headers = {}; if (body) { const p = JSON.stringify(body); headers["Content-Type"] = "application/json"; headers["Content-Length"] = p.length.toString(); } if (token) headers["Authorization"] = `Bearer ${token}`; const req = http.request({ hostname: u.hostname, port: u.port, path: u.pathname, method, headers, timeout: 10000 }, (res) => { let d = ""; res.on("data", c => d += c); res.on("end", () => resolve(res.statusCode)); }); req.on("error", reject); if (body) req.write(JSON.stringify(body)); req.end(); }); } // --- Report --- function generateReport(results, totalMs) { const pass = r => r.build && r.create && r.read && r.update && r.delete; const passed = results.filter(pass); const lines = []; lines.push("# SF-05 Domain Benchmark Report"); lines.push(""); lines.push(`> **Generated:** ${new Date().toISOString()}`); lines.push(`> **Domains Tested:** ${results.length}`); lines.push(`> **Total Time:** ${(totalMs / 1000).toFixed(1)}s`); lines.push(""); lines.push("## Summary"); lines.push(""); lines.push(`| Metric | Value |`); lines.push(`|--------|-------|`); lines.push(`| Total Domains | ${results.length} |`); lines.push(`| Build Pass | ${results.filter(r => r.build).length}/${results.length} |`); lines.push(`| CRUD Pass | ${passed.length}/${results.length} |`); lines.push(`| PASS RATE | **${((passed.length / results.length) * 100).toFixed(0)}%** |`); lines.push(`| Avg Files | ${(results.reduce((s, r) => s + r.files, 0) / results.length).toFixed(0)} |`); lines.push(""); lines.push("## Domain Results"); lines.push(""); lines.push(`| # | Domain | Files | Build | Health | Register | Login | Create | Read | Update | Delete | Result |`); lines.push(`|---|--------|-------|-------|--------|----------|-------|--------|------|--------|--------|--------|`); for (let i = 0; i < results.length; i++) { const r = results[i]; const ik = v => v ? "✅" : "❌"; const status = pass(r) ? "✅ PASS" : r.build ? "⚠️ PARTIAL" : "❌ FAIL"; lines.push(`| ${i + 1} | ${r.name} | ${r.files} | ${ik(r.build)} | ${ik(r.health)} | ${ik(r.register)} | ${ik(r.login)} | ${ik(r.create)} | ${ik(r.read)} | ${ik(r.update)} | ${ik(r.delete)} | ${status} |`); } lines.push(""); // Failures const failed = results.filter(r => !pass(r)); if (failed.length > 0) { lines.push("## Failure Analysis"); lines.push(""); for (const r of failed) { lines.push(`### ${r.name} (${r.label})`); for (const e of r.errors) lines.push(`- ${e}`); lines.push(""); } } lines.push("## ✅ Passed"); if (passed.length > 0) { for (const r of passed) lines.push(`- **${r.name}** (${r.label}) — ${r.files} files, ${(r.timeMs / 1000).toFixed(1)}s`); } else { lines.push("None — all domains have issues."); } lines.push(""); lines.push("## Required Fixes"); const errorCats = {}; for (const r of failed) for (const e of r.errors) { const cat = e.split(":")[0]; errorCats[cat] = (errorCats[cat] || 0) + 1; } for (const [cat, count] of Object.entries(errorCats).sort((a, b) => b[1] - a[1])) { lines.push(`- **${cat}**: ${count} occurrence(s)`); } return lines.join("\n"); } // --- Main --- async function main() { console.log("=".repeat(60)); console.log("SF-05 Domain Benchmark Suite"); console.log("=".repeat(60)); const t0 = Date.now(); const results = []; // Clear temp rmSync(BENCH_DIR, { recursive: true, force: true }); for (let i = 0; i < DOMAINS.length; i++) { const r = await runDomain(i, DOMAINS[i]); results.push(r); } const elapsed = Date.now() - t0; const report = generateReport(results, elapsed); writeFileSync(resolve(WORKSPACE, "benchmark-report.md"), report); console.log(`\n${"=".repeat(60)}`); console.log(`📊 Report: benchmark-report.md`); console.log(`${"=".repeat(60)}`); console.log(report); } main().catch(err => { console.error("FAIL:", err); process.exit(1); });