auth-yes/archive/.forum/poc-g2/dependency_graph_poc.ts

106 lines
3.1 KiB
TypeScript

import { assertEquals } from "https://deno.land/std@0.224.0/testing/asserts.ts";
import * as path from "https://deno.land/std@0.224.0/path/mod.ts";
/**
* Proof of Concept: Dependency Graphing (Gen 2)
*
* Demonstrates extracting a real dependency graph from Deno Info instead of a hardcoded
* matrix, showing how an agent calculates blast radius from live code.
*/
async function getDenoDependencies(entryFile: string) {
const command = new Deno.Command("deno", {
args: ["info", "--json", entryFile],
stdout: "piped",
stderr: "piped",
});
const { stdout } = await command.output();
const outputStr = new TextDecoder().decode(stdout);
return JSON.parse(outputStr);
}
function calculateBlastRadius(info: any, targetFileUrl: string): string[] {
// Build a reverse-dependency map (who imports me?)
const reverseMap = new Map<string, string[]>();
if (info.modules) {
for (const mod of info.modules) {
const specifier = mod.specifier;
if (!reverseMap.has(specifier)) reverseMap.set(specifier, []);
if (mod.dependencies) {
for (const dep of mod.dependencies) {
const importedSpecifier = dep.code?.specifier;
if (importedSpecifier) {
if (!reverseMap.has(importedSpecifier)) {
reverseMap.set(importedSpecifier, []);
}
reverseMap.get(importedSpecifier)!.push(specifier);
}
}
}
}
}
const impacted = new Set<string>();
const queue = [targetFileUrl];
while (queue.length > 0) {
const current = queue.shift()!;
const dependants = reverseMap.get(current) || [];
for (const dep of dependants) {
if (!impacted.has(dep)) {
impacted.add(dep);
queue.push(dep);
}
}
}
return Array.from(impacted);
}
if (import.meta.main) {
console.log("Running Dependency Graphing PoC (Gen 2) tests...");
try {
const dir = await Deno.makeTempDir();
// Create a mock dependency tree: A imports B, B imports C
const fileC = path.join(dir, "C.ts");
const fileB = path.join(dir, "B.ts");
const fileA = path.join(dir, "A.ts");
await Deno.writeTextFile(fileC, "export const c = 1;");
await Deno.writeTextFile(
fileB,
"import { c } from './C.ts'; export const b = c + 1;",
);
await Deno.writeTextFile(
fileA,
"import { b } from './B.ts'; console.log(b);",
);
const info = await getDenoDependencies(fileA);
const targetUrl = path.toFileUrl(fileC).href;
const blastRadius = calculateBlastRadius(info, targetUrl);
console.log(`If ${fileC} changes, the blast radius impacts:`);
blastRadius.forEach((b) => console.log(` - ${b}`));
// B imports C, A imports B. Both should be impacted.
assertEquals(blastRadius.length, 2);
assertEquals(blastRadius.some((b) => b.includes("B.ts")), true);
assertEquals(blastRadius.some((b) => b.includes("A.ts")), true);
console.log(
"✅ Dependency Graphing PoC (Gen 2) successful: Real Deno dependency graph analyzed.",
);
await Deno.remove(dir, { recursive: true });
} catch (err) {
console.error("❌ Dependency Graphing PoC (Gen 2) failed:", err);
Deno.exit(1);
}
}