Files
reverse-engineering-anything/src/domain/javascript/javascriptRouteInventory.test.ts
T

678 lines
22 KiB
TypeScript

import { expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "../../../tests/fixtures/javascriptStaticAnalysis.js";
import { completeSemanticSteps } from "./javascriptSemanticTraversal.js";
import {
buildJavaScriptRouteInventorySteps,
javaScriptRouteInventorySchema,
type JavaScriptRouteInventoryInput,
} from "./javascriptRouteInventory.js";
const source = (
key: string,
text: string,
url: string | null = null,
): JavaScriptRouteInventoryInput => ({
key,
path: url === null ? key : null,
url,
sha256: "a".repeat(64),
container_sha256: null,
status: "complete",
reason: null,
facts: analyzeJavaScriptStaticSource(text).routes!,
...(url === null && key.startsWith("app/")
? {
next_app_context: {
kind: "app-directory" as const,
directory: "app",
package: {
path: "package.json",
sha256: "b".repeat(64),
container_sha256: "c".repeat(64),
},
},
}
: {}),
});
const build = (inputs: JavaScriptRouteInventoryInput[], bytes?: number) =>
javaScriptRouteInventorySchema.parse(
completeSemanticSteps(buildJavaScriptRouteInventorySteps(inputs, bytes)),
);
it("keeps rejected program-var paths out of independent route inventory", () => {
const result = build([
source(
"routes.js",
"var PATH='/reports/stale';globalThis.PATH='/other';createBrowserRouter([{path:PATH},{path:'/reports'}]);const independent='/reports/help';",
),
]);
expect(result.routes.map(({ path }) => path)).toEqual([
"/reports",
"/reports/help",
]);
expect(result.unknowns).toEqual([
expect.objectContaining({
kind: "interpretation",
reason: expect.stringContaining("parent segment is unknown"),
}),
]);
expect(result.coverage).toMatchObject({
source_parsing: "complete",
candidate_enumeration: "partial",
});
});
it.each([
["hello world", "hello%20world"],
["team?admin", "team%3Fadmin"],
["team#admin", "team%23admin"],
] as const)(
"preserves Next filename pattern %s and its source identity",
(segment, encoded) => {
const url = `https://example.test/_next/static/chunks/app/${encoded}/page-abc.js`;
const result = build([
source(`app/${segment}/page.js`, ""),
source("captured.js", "", url),
source(
"runtime.js",
`globalThis.TURBOPACK=[];const page=${JSON.stringify(url)};`,
),
]);
expect(result.routes).toMatchObject([
{ path: `/${segment}`, source_count: 3, occurrence_count: 3 },
]);
expect(result.routes).toHaveLength(1);
expect(result.occurrences.map(({ source_id }) => source_id).sort()).toEqual(
result.sources.map(({ id }) => id).sort(),
);
expect(result.sources).toEqual(
expect.arrayContaining([
expect.objectContaining({ path: `app/${segment}/page.js`, url: null }),
expect.objectContaining({ path: null, url }),
]),
);
expect(result.unknowns).toEqual([]);
expect(result.coverage).toEqual({
scope: "provided-code-sources",
source_parsing: "complete",
candidate_enumeration: "complete",
application_routes: "unknown",
});
},
);
it.each(["%00", "%09", "%0A", "%7F"])(
"retains an interpretation gap for a Next filename containing %s",
(encoded) => {
const url = `https://example.test/_next/static/chunks/app/hello${encoded}world/page-abc.js`;
const result = build([source("captured.js", "", url)]);
expect(result.routes).toEqual([]);
expect(result.unknowns).toMatchObject([
{
source_id: result.sources[0]!.id,
kind: "interpretation",
reason: expect.stringContaining("control character"),
},
]);
expect(result.sources[0]!.url).toBe(url);
expect(result.coverage.candidate_enumeration).toBe("partial");
},
);
it.each(["app/page.js", "app/hello\nworld/page.js"])(
"does not promote page-shaped path %j without source-router applicability",
(path) => {
const unknown = source(path, "");
delete (unknown as { next_app_context?: unknown }).next_app_context;
const result = build([unknown]);
expect(result.routes).toEqual([]);
expect(result.unknowns).toMatchObject([
{
source_id: result.sources[0]!.id,
kind: "interpretation",
reason: expect.stringContaining("not measured"),
},
]);
expect(result.sources[0]!.path).toBe(path);
expect(result.coverage.candidate_enumeration).toBe("partial");
},
);
it("binds source identity to the package evidence establishing router applicability", () => {
const first = source("app/page.js", "");
const context = first.next_app_context;
if (context?.kind !== "app-directory")
throw new Error("Expected declared fixture context");
const second = {
...first,
next_app_context: {
...context,
package: { ...context.package, sha256: "d".repeat(64) },
},
};
expect(build([first]).sources[0]!.id).not.toBe(
build([second]).sources[0]!.id,
);
});
it("coalesces exact patterns while retaining independent occurrences and stable identities", () => {
const inputs = [
source("one.js", `router.push('/reports');`),
source(
"two.js",
`createBrowserRouter([{path:'/reports',children:[{path:'settings'}]}]);`,
),
];
const result = build(inputs),
repeated = build([...inputs].reverse());
expect(result.routes.map(({ path }) => path)).toEqual([
"/reports",
"/reports/settings",
]);
expect(result.routes[0]).toMatchObject({
occurrence_count: 2,
source_count: 2,
});
const reportOccurrences = result.occurrences.filter(
({ route_id }) => route_id === result.routes[0]!.id,
);
expect(
reportOccurrences
.map(({ source_id, mechanism, segments }) => ({
source: result.sources.find(({ id }) => id === source_id)?.key,
mechanism,
segments: segments.map(({ value }) => value),
}))
.sort((left, right) => left.source!.localeCompare(right.source!)),
).toEqual([
{
source: "one.js",
mechanism: "navigation-target",
segments: ["/reports"],
},
{
source: "two.js",
mechanism: "nested-route-object",
segments: ["/reports"],
},
]);
expect(new Set(result.occurrences.map(({ id }) => id)).size).toBe(
result.occurrences.length,
);
expect(result.routes.map(({ id }) => id)).toEqual(
repeated.routes.map(({ id }) => id),
);
expect(result.source_manifest_sha256).toBe(repeated.source_manifest_sha256);
expect(result.inventory_id).toBe(repeated.inventory_id);
expect(result.coverage.application_routes).toBe("unknown");
});
it("retains resolved index routes with source provenance and complete enumeration", () => {
const result = build([
source(
"routes.ts",
`const INDEX=true; createBrowserRouter([{index:(INDEX as boolean),Component:Home},{path:'/parent',children:[{index:INDEX,Component:Home}]}]);`,
),
]);
expect(result.routes.map(({ path }) => path)).toEqual(["/", "/parent"]);
expect(result.occurrences).toHaveLength(3);
expect(result.occurrences).toEqual(
expect.arrayContaining([
expect.objectContaining({
route_id: result.routes[0]!.id,
unprefixed_path: "/",
origin: "source-syntax",
mechanism: "nested-route-object",
source_range: expect.objectContaining({ start: expect.any(Object) }),
}),
]),
);
expect(result.unknowns).toEqual([]);
expect(result.coverage).toMatchObject({
source_parsing: "complete",
candidate_enumeration: "complete",
application_routes: "unknown",
});
});
it.each([
"const INDEX=choose();",
"var INDEX=true;globalThis.INDEX=false;",
"var INDEX=true;const host=globalThis;host.INDEX=false;",
])(
"marks unproven index interpretation as partial with child paths: %s",
(prefix) => {
const result = build([
source(
"routes.js",
`${prefix}createBrowserRouter([{index:INDEX,children:[{path:'child'}]}]);`,
),
]);
expect(result.routes.map(({ path }) => path)).toEqual(["/child"]);
expect(result.unknowns).toEqual([
expect.objectContaining({
kind: "interpretation",
reason: expect.stringContaining("index-route flag"),
source_range: expect.objectContaining({ start: expect.any(Object) }),
}),
]);
expect(result.coverage).toMatchObject({
source_parsing: "complete",
candidate_enumeration: "partial",
application_routes: "unknown",
});
},
);
it("chooses deterministic primary provenance when sources have equal confidence", () => {
const inputs = [
source("one.js", `router.push('/reports');`),
source("two.js", `router.push('/reports');`),
];
expect(build(inputs)).toEqual(build([...inputs].reverse()));
});
it("binds ordinary constants to application segments across sources and excludes framework filesystem noise", () => {
const result = build([
source("navigation.js", `router.push('/reports');`),
source(
"runtime.js",
`globalThis.TURBOPACK=[];const routes=['/reports/:id','/ROOT/node_modules/next/process/','/_not-found'];`,
),
]);
expect(result.routes.map(({ path }) => path)).toEqual([
"/reports",
"/reports/:id",
]);
expect(result.statistics.unanchored_literals_excluded).toBe(2);
});
it.each(["api", "assets", "static", "_next"])(
"retains constants under an anchored %s segment without promoting unanchored paths",
(prefix) => {
const result = build([
source("route.js", `router.push('/${prefix}/status');`),
source(
"runtime.js",
`globalThis.TURBOPACK=[];const same='/${prefix}/status';const help='/${prefix}/help';const other='/unanchored/noise';`,
),
]);
const anchored = result.routes.find(
({ path }) => path === `/${prefix}/status`,
);
const descendant = result.routes.find(
({ path }) => path === `/${prefix}/help`,
);
expect(anchored).toMatchObject({ occurrence_count: 2, source_count: 2 });
expect(descendant).toMatchObject({
confidence: "low",
classification: "unknown",
});
expect(result.routes.some(({ path }) => path === "/unanchored/noise")).toBe(
false,
);
expect(result.coverage.application_routes).toBe("unknown");
},
);
it("retains inferred Next script bases and the unprefixed path", () => {
const result = build([
source(
"script",
`router.push('/planner');`,
"https://example.test/app/_next/static/chunks/123.js",
),
]);
expect(result.routes.map(({ path }) => path)).toEqual(["/app/planner"]);
expect(result.occurrences[0]).toMatchObject({
unprefixed_path: "/planner",
inferred_base_path: "/app",
confidence: "low",
});
expect(result.sources[0]!.url).toBe(
"https://example.test/app/_next/static/chunks/123.js",
);
expect(result.unknowns.some(({ kind }) => kind === "inferred-base")).toBe(
true,
);
});
it("reports missing source facts as partial and preserves filename-only discoveries without fake AST ranges", () => {
const result = build([
{
...source("app/reports/[id]/page.js", ""),
status: "unavailable",
reason: "source was not selected",
facts: null,
},
]);
expect(result.coverage).toMatchObject({
source_parsing: "partial",
candidate_enumeration: "partial",
application_routes: "unknown",
});
expect(result.routes[0]!.path).toBe("/reports/:id");
expect(result.occurrences[0]).toMatchObject({
origin: "source-path",
source_range: null,
});
});
it("does not interpret the URL authority or file URL prefix as an App Router directory or routing base", () => {
const result = build([
source("script", `router.push('/reports');`, "https://app/reports/page.js"),
]);
expect(result.routes.map(({ path }) => path)).toEqual(["/reports"]);
const local = build([
source("file", `router.push('/reports');`, "file:///tmp/_next/123.js"),
]);
expect(local.sources[0]!.base_path).toBeNull();
expect(local.routes[0]!.path).toBe("/reports");
});
it("rejects forged aggregate counts and detached provenance", () => {
const result = build([source("one.js", `router.push('/reports');`)]);
expect(
javaScriptRouteInventorySchema.safeParse({
...result,
routes: result.routes.map((route) => ({
...route,
occurrence_count: 99,
})),
}).success,
).toBe(false);
expect(
javaScriptRouteInventorySchema.safeParse({ ...result, sources: [] })
.success,
).toBe(false);
});
it("retains a bounded prefix with explicit capacity and counts only retained facts", () => {
const inputs = [
source(
"many.js",
Array.from(
{ length: 100 },
(_, index) => `router.push('/reports/${index}');`,
).join("\n"),
),
];
const result = build(inputs, 8192);
expect(result.routes.length).toBeGreaterThan(0);
expect(result.routes.length).toBeLessThan(100);
expect(result.coverage.candidate_enumeration).toBe("partial");
expect(result.capacity?.limit_bytes).toBe(8192);
expect(Buffer.byteLength(JSON.stringify(result))).toBeLessThanOrEqual(8192);
expect(() => build(inputs, 1)).toThrow(RangeError);
});
it("anchors generic constants before applying an inferred asset base", () => {
const result = build([
source(
"prefixed.js",
`globalThis.TURBOPACK=[];createBrowserRouter([{path:'/reports'}]);const paths=['/reports/:id','/noise'];`,
"https://example.test/app/_next/static/chunks/one.js",
),
]);
expect(result.routes.map((row) => row.path)).toEqual([
"/app/reports",
"/app/reports/:id",
]);
expect(result.statistics.unanchored_literals_excluded).toBe(1);
});
it("classifies typed page configuration while generic server route calls remain unknown", () => {
const result = build([
source(
"typed.js",
`defineRoutes({reports:{_$:'reports',settings:{_$:'settings'}}});app.route('/api/users');`,
),
]);
expect(
result.routes
.filter((row) => row.classification === "page")
.map((row) => row.path),
).toEqual(["/reports", "/reports/settings"]);
expect(result.routes.find((row) => row.path === "/api/users")).toMatchObject({
classification: "unknown",
classification_basis: "unknown",
});
});
it("keeps TypeScript page filename provenance separate from syntax ranges", () => {
const result = build([
source(
"app/(account)/reports/[id]/page.tsx",
"export default function Page() { return null; }",
),
]);
expect(result.routes).toHaveLength(1);
expect(result.routes[0]).toMatchObject({
path: "/reports/:id",
classification: "page",
classification_basis: "next-app-page",
});
expect(result.occurrences[0]).toMatchObject({
origin: "source-path",
source_range: null,
});
});
it("coalesces inferred deployment roots without rewriting explicit trailing slashes", () => {
const result = build([
source(
"root-call",
"router.push('/');",
"https://example.test/app/_next/static/chunks/runtime.js",
),
source(
"root-page",
"",
"https://example.test/app/_next/static/chunks/app/page.tsx",
),
source("explicit.js", "router.push('/app');router.push('/app/');"),
]);
expect(result.routes.map(({ path }) => path)).toEqual(["/app", "/app/"]);
const root = result.routes.find(({ path }) => path === "/app")!;
expect(root.source_count).toBe(3);
const page = result.occurrences.find(({ origin }) => origin === "source-url");
expect(page).toMatchObject({
route_id: root.id,
unprefixed_path: "/",
inferred_base_path: "/app",
source_range: null,
});
expect(result.unknowns.some(({ kind }) => kind === "inferred-base")).toBe(
true,
);
});
it("keeps enumeration partial when a recognized navigation target is unresolved", () => {
const result = build([source("navigation.js", "router.navigate(target);")]);
expect(result.routes).toEqual([]);
expect(result.coverage.candidate_enumeration).toBe("partial");
expect(result.unknowns).toEqual([
expect.objectContaining({
kind: "interpretation",
source_id: result.sources[0]!.id,
}),
]);
});
it.each(["/app/reports", "/reports"])(
"matches based and unbased constants against application segments for %s",
(anchor) => {
const result = build([
source(
"mixed.js",
`globalThis.TURBOPACK=[];router.push('${anchor}');const paths=['/reports/settings','/app/reports/history','/noise','/app/noise','/ROOT/node_modules/framework'];`,
"https://example.test/app/_next/static/chunks/one.js",
),
]);
expect(result.routes.map(({ path }) => path)).toEqual([
"/app/reports",
"/app/reports/history",
"/app/reports/settings",
]);
expect(result.occurrences).toEqual(
expect.arrayContaining([
expect.objectContaining({
unprefixed_path: "/reports/settings",
inferred_base_path: "/app",
}),
expect.objectContaining({
unprefixed_path: "/app/reports/history",
inferred_base_path: null,
}),
]),
);
},
);
it("keeps filename and module-derived anchors application-relative when a deployment base matches an application segment", () => {
const result = build([
source(
"page",
"",
"https://example.test/app/_next/static/chunks/app/app/page-abc.js",
),
source(
"root-runtime",
"globalThis.TURBOPACK=[];const paths=['/app/settings','/noise'];",
"https://example.test/_next/static/chunks/root.js",
),
source(
"module-runtime",
"globalThis.TURBOPACK=[];const modulePath='app/app/page.js';",
"https://example.test/app/_next/static/chunks/runtime.js",
),
]);
expect(result.routes.map(({ path }) => path)).toEqual([
"/app/app",
"/app/settings",
]);
const page = result.routes.find(({ path }) => path === "/app/app")!;
expect(page.source_count).toBe(2);
for (const origin of ["source-url", "source-syntax"])
expect(result.occurrences).toEqual(
expect.arrayContaining([
expect.objectContaining({
route_id: page.id,
origin,
unprefixed_path: "/app",
inferred_base_path: "/app",
}),
]),
);
expect(result.statistics.unanchored_literals_excluded).toBe(1);
});
it("reports generated typed configurations as partial even without retained candidates", () => {
const result = build([
source("generated.js", "defineRoutes({reports:buildRoutes()});"),
]);
expect(result.routes).toEqual([]);
expect(result.coverage.candidate_enumeration).toBe("partial");
expect(result.unknowns).toHaveLength(1);
expect(result.unknowns[0]!.kind).toBe("interpretation");
});
it.each([
["app/feed/(.)photo/page.tsx", null],
[
"page.js",
"https://example.test/_next/static/chunks/app/feed/(.)photo/page-123.js",
],
] as const)(
"retains intercepted filename metadata as partial: %s",
(path, url) => {
const result = build([
source(path, "export default function Photo(){return null;}", url),
]);
expect(result.routes).toEqual([]);
expect(result.coverage.candidate_enumeration).toBe("partial");
expect(result.unknowns).toEqual(
expect.arrayContaining([
expect.objectContaining({
kind: "interpretation",
reason: expect.stringContaining("intercepted"),
source_range: null,
}),
]),
);
},
);
it("decodes captured Next script URL segments once while retaining source URL identity", () => {
const url =
"https://example.test/base/_next/static/chunks/app/%28shop%29/blog/%5Bid%5D/page-abc.js";
const result = build([
source("captured-page", "", url),
source("app/blog/%5Bid%5D/page.js", ""),
]);
expect(result.routes.map(({ path }) => path)).toEqual([
"/base/blog/:id",
"/blog/%5Bid%5D",
]);
expect(result.sources.find((entry) => entry.url === url)?.url).toBe(url);
expect(result.occurrences).toEqual(
expect.arrayContaining([
expect.objectContaining({
origin: "source-url",
unprefixed_path: "/blog/:id",
inferred_base_path: "/base",
}),
]),
);
});
it.each(["team%2Fadmin", "team%5Cadmin", "%E0%A4%A"])(
"keeps malformed Next URL segment %s unknown alongside usable script facts",
(segment) => {
const url = `https://example.test/_next/static/chunks/app/${segment}/page-abc.js`;
const result = build([source("page", "router.push('/usable');", url)]);
expect(result.routes.map(({ path }) => path)).toEqual(["/usable"]);
expect(result.sources[0]?.url).toBe(url);
expect(result.coverage.candidate_enumeration).toBe("partial");
expect(result.unknowns).toEqual(
expect.arrayContaining([
expect.objectContaining({
kind: "interpretation",
reason: expect.stringContaining("Next script URL"),
}),
]),
);
},
);
it("preserves literal percent encoding after one script URL decode", () => {
const result = build([
source(
"page",
"",
"https://example.test/_next/static/chunks/app/%255Bid%255D/page-abc.js",
),
]);
expect(result.routes.map(({ path }) => path)).toEqual(["/%5Bid%5D"]);
});
it.each([
"page%3Fnotes.js",
"page%23notes.js",
"page.js%3Fnotes",
"page.js%23notes",
])("keeps encoded Next script filename delimiters literal: %s", (leaf) => {
const url = `https://example.test/_next/static/chunks/app/dashboard/${leaf}`;
const result = build([source("captured-page", "", url)]);
expect(result.routes).toEqual([]);
expect(result.sources[0]?.url).toBe(url);
});
it.each(["?cache=1", "#section", "?cache=1#section"])(
"parses actual Next script URL suffix %s before classifying the filename",
(suffix) => {
const url = `https://example.test/_next/static/chunks/app/dashboard/page-abc.js${suffix}`;
const result = build([source("captured-page", "", url)]);
expect(result.routes.map(({ path }) => path)).toEqual(["/dashboard"]);
expect(result.sources[0]?.url).toBe(url);
},
);
it.each(["page?notes.js", "page#notes.js", "page.js?notes", "page.js#notes"])(
"keeps filesystem Next page filename delimiters literal: %s",
(leaf) => {
const result = build([source(`app/dashboard/${leaf}`, "")]);
expect(result.routes).toEqual([]);
},
);