fix(javascript): retain unresolved network targets with provenance

Resolve stable lexical and URL targets using shared effects, and preserve unresolved request expressions as partial facts. Carry their uncertainty through workers, artifact graphs, and the CLI. Scope partial endpoint identity to the source site.

Refs: #3451, #3534

Co-authored-by: Hemang Varshney <218273197+hemv-857@users.noreply.github.com>
This commit is contained in:
morluto
2026-10-16 08:14:52 +08:00
parent 1b9b725c4c
commit 018ab2c5d9
15 changed files with 737 additions and 27 deletions
@@ -0,0 +1,265 @@
import { expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
const endpoints = (source: string) =>
analyzeJavaScriptStaticSource(source).endpoints.map((endpoint) => ({
kind: endpoint.kind,
value: endpoint.value,
mechanism: endpoint.mechanism,
resolution: endpoint.resolution,
}));
it("resolves unchanged lexical strings and object properties", () => {
expect(
endpoints(`
const target = "/items";
const config = { url: "/accounts" };
fetch(target);
fetch("/direct");
fetch(config.url);
`),
).toEqual([
{
kind: "network",
value: "/items",
mechanism: "call:fetch",
resolution: undefined,
},
{
kind: "network",
value: "/direct",
mechanism: "call:fetch",
resolution: undefined,
},
{
kind: "network",
value: "/accounts",
mechanism: "call:fetch",
resolution: undefined,
},
]);
});
it("resolves unchanged URL objects and aliases using URL serialization", () => {
expect(
endpoints(`
const socket = new URL("HTTPS://EXAMPLE.COM:443");
const alias = socket;
const relative = new URL("/items", "https://api.example.test/base");
fetch(alias);
fetch(relative);
`),
).toEqual([
{
kind: "network",
value: "https://example.com/",
mechanism: "call:fetch",
resolution: undefined,
},
{
kind: "network",
value: "https://api.example.test/items",
mechanism: "call:fetch",
resolution: undefined,
},
]);
});
it("keeps an interpolated request expression verbatim", () => {
expect(
endpoints("fetch(`https://${hosts['a@b']}/items/${id}`);"),
).toContainEqual({
kind: "network",
value: "`https://${hosts['a@b']}/items/${id}`",
mechanism: "call:fetch",
resolution: "partial",
});
});
it("keeps writes to a config property partial instead of resolving its initializer", () => {
expect(
endpoints(`
const config = { url: "https://old.example.test" };
config.url = "https://new.example.test";
fetch(config.url);
`),
).toContainEqual({
kind: "network",
value: "config.url",
mechanism: "call:fetch",
resolution: "partial",
});
});
it("keeps a reassigned endpoint binding partial", () => {
expect(
endpoints(`
let endpoint = "https://old.example.test";
endpoint = "https://new.example.test";
fetch(endpoint);
`),
).toContainEqual({
kind: "network",
value: "endpoint",
mechanism: "call:fetch",
resolution: "partial",
});
});
it("does not resolve an endpoint from a declaration after the request", () => {
expect(
endpoints(`
fetch(endpoint);
const endpoint = "https://api.example.test/items";
`),
).toEqual([
{
kind: "network",
value: "endpoint",
mechanism: "call:fetch",
resolution: "partial",
},
]);
});
it("keeps URLSearchParams mutations partial", () => {
expect(
endpoints(`
const socket = new URL("wss://old.example.test/socket");
socket.searchParams.set("token", token);
new WebSocket(socket);
`),
).toContainEqual({
kind: "network",
value: "socket",
mechanism: "call:WebSocket",
resolution: "partial",
});
});
it("resolves a URL object against a literal base", () => {
expect(
endpoints(`
const socket = new URL("/socket", "wss://api.example.test");
new WebSocket(socket);
`),
).toEqual([
{
kind: "network",
value: "wss://api.example.test/socket",
mechanism: "call:WebSocket",
resolution: undefined,
},
]);
});
it("resolves an unchanged URL object's toString target", () => {
expect(
endpoints(`
const socket = new URL("wss://api.example.test/socket");
new WebSocket(socket.toString());
`),
).toEqual([
{
kind: "network",
value: "wss://api.example.test/socket",
mechanism: "call:WebSocket",
resolution: undefined,
},
]);
});
it("keeps a URL object partial when its base is unresolved", () => {
expect(
endpoints(`
const socket = new URL("/socket", apiOrigin);
new WebSocket(socket);
`),
).toContainEqual({
kind: "network",
value: "socket",
mechanism: "call:WebSocket",
resolution: "partial",
});
});
it("keeps URL aliases partial after a member mutation", () => {
expect(
endpoints(`
const socket = new URL("wss://old.example.test/socket");
const alias = socket;
alias.hostname = "new.example.test";
fetch(socket);
`),
).toContainEqual({
kind: "network",
value: "socket",
mechanism: "call:fetch",
resolution: "partial",
});
});
it("keeps an escaped URL object partial", () => {
expect(
endpoints(`
const socket = new URL("wss://old.example.test/socket");
configure(socket);
new WebSocket(socket);
`),
).toContainEqual({
kind: "network",
value: "socket",
mechanism: "call:WebSocket",
resolution: "partial",
});
});
it("retains dynamic targets on known global fetch receivers", () => {
expect(
endpoints(`
window.fetch(endpoint);
globalThis.fetch(endpoint);
self.fetch(endpoint);
`),
).toEqual([
{
kind: "network",
value: "endpoint",
mechanism: "call:window.fetch",
resolution: "partial",
},
{
kind: "network",
value: "endpoint",
mechanism: "call:globalThis.fetch",
resolution: "partial",
},
{
kind: "network",
value: "endpoint",
mechanism: "call:self.fetch",
resolution: "partial",
},
]);
});
it("does not treat a locally bound fetch function as a network API", () => {
expect(
endpoints(`
function fetch(key) { return records[key]; }
fetch(key);
`),
).toEqual([]);
});
it("keeps exact literal targets exact", () => {
expect(endpoints('fetch("https://api.example.test/items");')).toEqual([
{
kind: "network",
value: "https://api.example.test/items",
mechanism: "call:fetch",
resolution: undefined,
},
]);
});