@descryy/runtime-graph-correlator 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/confirm-observed-caller.d.ts +92 -0
- package/dist/confirm-observed-caller.d.ts.map +1 -0
- package/dist/confirm-observed-caller.js +117 -0
- package/dist/confirm-observed-caller.js.map +1 -0
- package/dist/database-table.d.ts +40 -0
- package/dist/database-table.d.ts.map +1 -0
- package/dist/database-table.js +52 -0
- package/dist/database-table.js.map +1 -0
- package/dist/endpoint.d.ts +73 -0
- package/dist/endpoint.d.ts.map +1 -0
- package/dist/endpoint.js +115 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/frontend-caller.d.ts +67 -0
- package/dist/frontend-caller.d.ts.map +1 -0
- package/dist/frontend-caller.js +87 -0
- package/dist/frontend-caller.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/log-endpoint.d.ts +67 -0
- package/dist/log-endpoint.d.ts.map +1 -0
- package/dist/log-endpoint.js +97 -0
- package/dist/log-endpoint.js.map +1 -0
- package/dist/observed-frontend-caller.d.ts +70 -0
- package/dist/observed-frontend-caller.d.ts.map +1 -0
- package/dist/observed-frontend-caller.js +103 -0
- package/dist/observed-frontend-caller.js.map +1 -0
- package/dist/symbol.d.ts +244 -0
- package/dist/symbol.d.ts.map +1 -0
- package/dist/symbol.js +344 -0
- package/dist/symbol.js.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The **first leg** of the path the product requires:
|
|
3
|
+
* `Frontend FUNCTION → API_ENDPOINT → API_ROUTE → Backend FUNCTION`.
|
|
4
|
+
*
|
|
5
|
+
* `RUNTIME-CHECKLIST.md` §11 states that path and its own boxes covered
|
|
6
|
+
* only the last three legs — the audit added a box for the first and marked
|
|
7
|
+
* it absent. The graph has had the edge all along: `adapter-typescript`
|
|
8
|
+
* emits `USES_API` from a frontend function to the endpoint it calls, read
|
|
9
|
+
* from source. Nothing correlated it.
|
|
10
|
+
*
|
|
11
|
+
* ---
|
|
12
|
+
*
|
|
13
|
+
* **This is a structural claim, and calling it anything else would be the
|
|
14
|
+
* central lie this project exists not to tell.**
|
|
15
|
+
*
|
|
16
|
+
* `USES_API` says *this function contains a call to that endpoint*. It does
|
|
17
|
+
* **not** say this function issued the request that was just observed. When
|
|
18
|
+
* three components call one endpoint, the graph names all three and the
|
|
19
|
+
* browser saw one — and nothing in a `NETWORK_REQUEST` says which. Playwright
|
|
20
|
+
* exposes an initiator chain that could narrow it; the network collector
|
|
21
|
+
* does not capture it today (§6 records that as a gap), and until it does,
|
|
22
|
+
* *which* caller ran is not something this repo knows.
|
|
23
|
+
*
|
|
24
|
+
* So the outcome vocabulary says so at the type level: a single caller is
|
|
25
|
+
* `sole-structural-caller`, never `resolved`. A reader who sees `resolved`
|
|
26
|
+
* elsewhere in this package is looking at an observation; here they are
|
|
27
|
+
* looking at the only candidate the code admits, which is a different and
|
|
28
|
+
* weaker fact even when it happens to be right.
|
|
29
|
+
*
|
|
30
|
+
* **Consequence for reliability, per the architecture's resolution cap:** a
|
|
31
|
+
* finding whose path rests on this leg is capped by the `USES_API` edge's
|
|
32
|
+
* own resolution, exactly as any other structural hop is. Extending the
|
|
33
|
+
* path does not upgrade the evidence.
|
|
34
|
+
*/
|
|
35
|
+
import { incoming } from "@descryy/core";
|
|
36
|
+
import { getNode } from "@descryy/core";
|
|
37
|
+
/**
|
|
38
|
+
* Every frontend function the graph says calls this endpoint.
|
|
39
|
+
*
|
|
40
|
+
* Ambiguity is returned to the caller rather than ranked, the same way
|
|
41
|
+
* `resolveSymbolNode` and `resolveEndpointNode` refuse it. There is a
|
|
42
|
+
* tempting heuristic here — prefer the caller in the file the browser most
|
|
43
|
+
* recently loaded, or the one whose line number is nearest the event — and
|
|
44
|
+
* both are timing and locality standing in for causality, which §27's own
|
|
45
|
+
* rule forbids in one sentence: *similarity must never be treated as
|
|
46
|
+
* causality*.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveFrontendCallers(driver, endpointNodeId) {
|
|
49
|
+
const edges = incoming(driver, endpointNodeId, { types: ["USES_API"] });
|
|
50
|
+
const callers = [];
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
for (const edge of edges) {
|
|
53
|
+
if (seen.has(edge.from))
|
|
54
|
+
continue;
|
|
55
|
+
seen.add(edge.from);
|
|
56
|
+
const node = getNode(driver, edge.from);
|
|
57
|
+
if (node === null || node === undefined)
|
|
58
|
+
continue; // an edge whose source is not in this store: a disclosed gap, not a caller to invent
|
|
59
|
+
callers.push(node);
|
|
60
|
+
}
|
|
61
|
+
if (callers.length === 0) {
|
|
62
|
+
return {
|
|
63
|
+
outcome: "not-found",
|
|
64
|
+
reason: `No USES_API edge points at endpoint "${endpointNodeId}". The calling code was not read by an adapter, ` +
|
|
65
|
+
"or the call is not a direct one this extractor recognises. Reported as a gap in the graph rather than " +
|
|
66
|
+
"as an absence of callers.",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (callers.length > 1) {
|
|
70
|
+
return {
|
|
71
|
+
outcome: "ambiguous",
|
|
72
|
+
candidates: callers,
|
|
73
|
+
reason: `${callers.length} frontend functions call this endpoint: ${callers.map((c) => c.name).join(", ")}. ` +
|
|
74
|
+
"The graph names all of them and the browser observed one request; nothing in that request says which " +
|
|
75
|
+
"caller issued it. Refused rather than ranked — preferring the nearest line or the most recently loaded " +
|
|
76
|
+
"file would be locality standing in for causality.",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
outcome: "sole-structural-caller",
|
|
81
|
+
node: callers[0],
|
|
82
|
+
reason: `"${callers[0].name}" is the only function the graph says calls this endpoint. That makes it the sole ` +
|
|
83
|
+
"candidate, not a confirmed caller: USES_API is read from source and states that a call exists, not that " +
|
|
84
|
+
"this call is the one that ran.",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=frontend-caller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend-caller.js","sourceRoot":"","sources":["../src/frontend-caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,QAAQ,EAAkB,MAAM,eAAe,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAUxC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAiB,EAAE,cAAsB;IAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAExE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS,CAAC,qFAAqF;QACxI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM,EACJ,wCAAwC,cAAc,kDAAkD;gBACxG,wGAAwG;gBACxG,2BAA2B;SAC9B,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,UAAU,EAAE,OAAO;YACnB,MAAM,EACJ,GAAG,OAAO,CAAC,MAAM,2CAA2C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBACrG,uGAAuG;gBACvG,yGAAyG;gBACzG,mDAAmD;SACtD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,wBAAwB;QACjC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAE;QACjB,MAAM,EACJ,IAAI,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,oFAAoF;YACxG,0GAA0G;YAC1G,gCAAgC;KACnC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The graph correlator: resolves runtime evidence to descry-core graph
|
|
3
|
+
* nodes. Deliberately does not re-wrap graph-path building — `traceApi`
|
|
4
|
+
* (`@descryy/core`) already answers "how does this reach that" end to end,
|
|
5
|
+
* `direction: "both"` already crosses the `API_ENDPOINT` join with no extra
|
|
6
|
+
* option needed (DEC-228 traced this directly; `crossApiEndpoint` is only
|
|
7
|
+
* for `impactOf`'s single-direction walk). A caller resolves both ends here,
|
|
8
|
+
* then calls `traceApi` from `@descryy/core` directly — adding a wrapper
|
|
9
|
+
* around a function that already does the whole job would be an
|
|
10
|
+
* abstraction with nothing behind it.
|
|
11
|
+
*/
|
|
12
|
+
export { resolveEndpointNode, resolveEndpointNodesByPath, type EndpointResolution } from "./endpoint.ts";
|
|
13
|
+
export { resolveSymbolNode, resolveSymbolAcrossRepos, resolveContainingModule, resolveTestCaseNode, type SymbolResolution, type CrossRepoSymbolResolution, type ModuleResolution, type TestCaseResolution, } from "./symbol.ts";
|
|
14
|
+
export { resolveEndpointFromLogText, findEndpointMentions, type LogEndpointResolution, type LogEndpointMention, } from "./log-endpoint.ts";
|
|
15
|
+
export { resolveFrontendCallers, type FrontendCallerResolution } from "./frontend-caller.ts";
|
|
16
|
+
export { resolveObservedFrontendCaller, type ObservedFrontendCallerResolution } from "./observed-frontend-caller.ts";
|
|
17
|
+
export { resolveDatabaseTableNode, type DatabaseTableResolution } from "./database-table.ts";
|
|
18
|
+
export { confirmObservedFrontendCaller, type ConfirmObservedCallerInput, type ConfirmObservedCallerOutcome, } from "./confirm-observed-caller.ts";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,6BAA6B,EAAE,KAAK,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,KAAK,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EACL,6BAA6B,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,4BAA4B,GAClC,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The graph correlator: resolves runtime evidence to descry-core graph
|
|
3
|
+
* nodes. Deliberately does not re-wrap graph-path building — `traceApi`
|
|
4
|
+
* (`@descryy/core`) already answers "how does this reach that" end to end,
|
|
5
|
+
* `direction: "both"` already crosses the `API_ENDPOINT` join with no extra
|
|
6
|
+
* option needed (DEC-228 traced this directly; `crossApiEndpoint` is only
|
|
7
|
+
* for `impactOf`'s single-direction walk). A caller resolves both ends here,
|
|
8
|
+
* then calls `traceApi` from `@descryy/core` directly — adding a wrapper
|
|
9
|
+
* around a function that already does the whole job would be an
|
|
10
|
+
* abstraction with nothing behind it.
|
|
11
|
+
*/
|
|
12
|
+
export { resolveEndpointNode, resolveEndpointNodesByPath } from "./endpoint.js";
|
|
13
|
+
export { resolveSymbolNode, resolveSymbolAcrossRepos, resolveContainingModule, resolveTestCaseNode, } from "./symbol.js";
|
|
14
|
+
export { resolveEndpointFromLogText, findEndpointMentions, } from "./log-endpoint.js";
|
|
15
|
+
export { resolveFrontendCallers } from "./frontend-caller.js";
|
|
16
|
+
export { resolveObservedFrontendCaller } from "./observed-frontend-caller.js";
|
|
17
|
+
export { resolveDatabaseTableNode } from "./database-table.js";
|
|
18
|
+
export { confirmObservedFrontendCaller, } from "./confirm-observed-caller.js";
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAA2B,MAAM,eAAe,CAAC;AACzG,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,GAKpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GAGrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAiC,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,6BAA6B,EAAyC,MAAM,+BAA+B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAgC,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EACL,6BAA6B,GAG9B,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving the API endpoint a **backend log line names in its own text** to
|
|
3
|
+
* an `API_ENDPOINT` node.
|
|
4
|
+
*
|
|
5
|
+
* **Why this exists.** RT-044's measurement, re-run after RT-045, left
|
|
6
|
+
* `confirmed` blocked by one precisely-named thing: four evidence items
|
|
7
|
+
* share the real request id, but three of them are the same source
|
|
8
|
+
* (`browser-network`) and the fourth — the backend exception — resolves to
|
|
9
|
+
* the *function* that threw, via its stack. So no node has two independent
|
|
10
|
+
* sources in one request, and the top report category is unreachable in
|
|
11
|
+
* principle rather than merely unmet.
|
|
12
|
+
*
|
|
13
|
+
* The backend's own header line, measured from the real fixture, is:
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* [broken-invoices] DELETE /api/invoices/3 failed [request-id=...]: TypeError: ...
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* It names the endpoint. A browser saying "I sent DELETE /api/invoices/3"
|
|
20
|
+
* and a backend saying "I failed DELETE /api/invoices/3" are **two
|
|
21
|
+
* independent observations of one request from opposite sides of the wire**,
|
|
22
|
+
* joined by an id the backend itself echoed. That is corroboration in the
|
|
23
|
+
* exact sense the gate means, and missing it was a gap in resolution, not a
|
|
24
|
+
* property of the evidence.
|
|
25
|
+
*
|
|
26
|
+
* **What this deliberately does not do.** It does not scan for a bare path.
|
|
27
|
+
* A log line mentioning `/api/invoices/3` with no method is not a statement
|
|
28
|
+
* that *this* endpoint was exercised — `GET` and `DELETE` on one path are
|
|
29
|
+
* different endpoints, and `endpoint.ts` already refuses a method mismatch
|
|
30
|
+
* for exactly that reason. Requiring an explicit HTTP method in the text is
|
|
31
|
+
* what keeps this from becoming a substring search that resolves whatever
|
|
32
|
+
* happens to look familiar (RT-031's identity-proxy class, applied to prose).
|
|
33
|
+
*/
|
|
34
|
+
import type { SqlDriver } from "@descryy/core";
|
|
35
|
+
export interface LogEndpointMention {
|
|
36
|
+
readonly method: string;
|
|
37
|
+
readonly path: string;
|
|
38
|
+
}
|
|
39
|
+
/** Every `METHOD /path` pair the text states, in order, deduplicated. Empty when the text names none — the common and correct case for an ordinary log line. */
|
|
40
|
+
export declare function findEndpointMentions(text: string): readonly LogEndpointMention[];
|
|
41
|
+
export type LogEndpointResolution = {
|
|
42
|
+
readonly outcome: "resolved";
|
|
43
|
+
readonly nodeId: string;
|
|
44
|
+
readonly mention: LogEndpointMention;
|
|
45
|
+
} | {
|
|
46
|
+
readonly outcome: "no-mention";
|
|
47
|
+
readonly reason: string;
|
|
48
|
+
} | {
|
|
49
|
+
readonly outcome: "ambiguous";
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
readonly mentions: readonly LogEndpointMention[];
|
|
52
|
+
} | {
|
|
53
|
+
readonly outcome: "not-found";
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
readonly mentions: readonly LogEndpointMention[];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The endpoint a log line names, when it names exactly one that resolves.
|
|
59
|
+
*
|
|
60
|
+
* **Two mentions resolving to two different endpoints is refused, not
|
|
61
|
+
* ranked.** A line naming several routes (a proxy summary, a batch report)
|
|
62
|
+
* genuinely does not identify one, and picking the first would be a
|
|
63
|
+
* heuristic standing in for evidence. `endpoint.ts` refuses ambiguity to the
|
|
64
|
+
* caller and this inherits that rather than deciding differently.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveEndpointFromLogText(driver: SqlDriver, text: string): LogEndpointResolution;
|
|
67
|
+
//# sourceMappingURL=log-endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-endpoint.d.ts","sourceRoot":"","sources":["../src/log-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAY/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,gKAAgK;AAChK,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,kBAAkB,EAAE,CAchF;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAA;CAAE,GAC5G;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAA;CAAE,CAAC;AAEjH;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CA8BjG"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving the API endpoint a **backend log line names in its own text** to
|
|
3
|
+
* an `API_ENDPOINT` node.
|
|
4
|
+
*
|
|
5
|
+
* **Why this exists.** RT-044's measurement, re-run after RT-045, left
|
|
6
|
+
* `confirmed` blocked by one precisely-named thing: four evidence items
|
|
7
|
+
* share the real request id, but three of them are the same source
|
|
8
|
+
* (`browser-network`) and the fourth — the backend exception — resolves to
|
|
9
|
+
* the *function* that threw, via its stack. So no node has two independent
|
|
10
|
+
* sources in one request, and the top report category is unreachable in
|
|
11
|
+
* principle rather than merely unmet.
|
|
12
|
+
*
|
|
13
|
+
* The backend's own header line, measured from the real fixture, is:
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* [broken-invoices] DELETE /api/invoices/3 failed [request-id=...]: TypeError: ...
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* It names the endpoint. A browser saying "I sent DELETE /api/invoices/3"
|
|
20
|
+
* and a backend saying "I failed DELETE /api/invoices/3" are **two
|
|
21
|
+
* independent observations of one request from opposite sides of the wire**,
|
|
22
|
+
* joined by an id the backend itself echoed. That is corroboration in the
|
|
23
|
+
* exact sense the gate means, and missing it was a gap in resolution, not a
|
|
24
|
+
* property of the evidence.
|
|
25
|
+
*
|
|
26
|
+
* **What this deliberately does not do.** It does not scan for a bare path.
|
|
27
|
+
* A log line mentioning `/api/invoices/3` with no method is not a statement
|
|
28
|
+
* that *this* endpoint was exercised — `GET` and `DELETE` on one path are
|
|
29
|
+
* different endpoints, and `endpoint.ts` already refuses a method mismatch
|
|
30
|
+
* for exactly that reason. Requiring an explicit HTTP method in the text is
|
|
31
|
+
* what keeps this from becoming a substring search that resolves whatever
|
|
32
|
+
* happens to look familiar (RT-031's identity-proxy class, applied to prose).
|
|
33
|
+
*/
|
|
34
|
+
import { resolveEndpointNode } from "./endpoint.js";
|
|
35
|
+
/**
|
|
36
|
+
* `DELETE /api/invoices/3` anywhere in a line, method uppercase and
|
|
37
|
+
* standalone. The trailing character class stops at whitespace and at the
|
|
38
|
+
* punctuation that ordinarily follows a path in prose (`failed:`, `(404)`,
|
|
39
|
+
* `"..."`), so a real path is captured without swallowing the sentence.
|
|
40
|
+
*/
|
|
41
|
+
const METHOD_AND_PATH = /\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\/[^\s"'(),;]*)/g;
|
|
42
|
+
/** Every `METHOD /path` pair the text states, in order, deduplicated. Empty when the text names none — the common and correct case for an ordinary log line. */
|
|
43
|
+
export function findEndpointMentions(text) {
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
const mentions = [];
|
|
46
|
+
for (const match of text.matchAll(METHOD_AND_PATH)) {
|
|
47
|
+
const method = match[1] ?? "";
|
|
48
|
+
// A trailing '.' or ':' is sentence punctuation, not part of the path.
|
|
49
|
+
const path = (match[2] ?? "").replace(/[.:,;]+$/, "");
|
|
50
|
+
if (path === "")
|
|
51
|
+
continue;
|
|
52
|
+
const key = `${method} ${path}`;
|
|
53
|
+
if (seen.has(key))
|
|
54
|
+
continue;
|
|
55
|
+
seen.add(key);
|
|
56
|
+
mentions.push({ method, path });
|
|
57
|
+
}
|
|
58
|
+
return mentions;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The endpoint a log line names, when it names exactly one that resolves.
|
|
62
|
+
*
|
|
63
|
+
* **Two mentions resolving to two different endpoints is refused, not
|
|
64
|
+
* ranked.** A line naming several routes (a proxy summary, a batch report)
|
|
65
|
+
* genuinely does not identify one, and picking the first would be a
|
|
66
|
+
* heuristic standing in for evidence. `endpoint.ts` refuses ambiguity to the
|
|
67
|
+
* caller and this inherits that rather than deciding differently.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveEndpointFromLogText(driver, text) {
|
|
70
|
+
const mentions = findEndpointMentions(text);
|
|
71
|
+
if (mentions.length === 0) {
|
|
72
|
+
return { outcome: "no-mention", reason: "The text states no `METHOD /path` pair. A bare path is not a statement that a particular endpoint was exercised." };
|
|
73
|
+
}
|
|
74
|
+
const resolved = [];
|
|
75
|
+
for (const mention of mentions) {
|
|
76
|
+
const resolution = resolveEndpointNode(driver, mention.method, mention.path);
|
|
77
|
+
if (resolution.outcome === "resolved")
|
|
78
|
+
resolved.push({ nodeId: resolution.node.id, mention });
|
|
79
|
+
}
|
|
80
|
+
if (resolved.length === 0) {
|
|
81
|
+
return {
|
|
82
|
+
outcome: "not-found",
|
|
83
|
+
reason: `The text names ${mentions.map((m) => `${m.method} ${m.path}`).join(", ")}, none of which resolves to an API_ENDPOINT in this graph.`,
|
|
84
|
+
mentions,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const distinctNodes = new Set(resolved.map((r) => r.nodeId));
|
|
88
|
+
if (distinctNodes.size > 1) {
|
|
89
|
+
return {
|
|
90
|
+
outcome: "ambiguous",
|
|
91
|
+
reason: `The text names ${distinctNodes.size} different endpoints that all resolve; a line naming several routes does not identify one, and picking the first would be a heuristic standing in for evidence.`,
|
|
92
|
+
mentions,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { outcome: "resolved", nodeId: resolved[0].nodeId, mention: resolved[0].mention };
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=log-endpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-endpoint.js","sourceRoot":"","sources":["../src/log-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,EAAE,mBAAmB,EAA2B,MAAM,eAAe,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,eAAe,GAAG,gEAAgE,CAAC;AAOzF,gKAAgK;AAChK,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,QAAQ,GAAyB,EAAE,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9B,uEAAuE;QACvE,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACtD,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAC1B,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAQD;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAiB,EAAE,IAAY;IACxE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,kHAAkH,EAAE,CAAC;IAC/J,CAAC;IAED,MAAM,QAAQ,GAAsD,EAAE,CAAC;IACvE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAuB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACjG,IAAI,UAAU,CAAC,OAAO,KAAK,UAAU;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,kBAAkB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,4DAA4D;YAC7I,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,kBAAkB,aAAa,CAAC,IAAI,iKAAiK;YAC7M,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAE,CAAC,OAAO,EAAE,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The **observed** half of `frontend-caller.ts`'s deliberately structural
|
|
3
|
+
* answer — closing the gap that file's own doc comment names: "Playwright
|
|
4
|
+
* exposes an initiator chain that could narrow it; the network collector
|
|
5
|
+
* does not capture it today."
|
|
6
|
+
*
|
|
7
|
+
* `@descryhq-wq/runtime-browser`'s `frontend-initiator-capture.ts` now captures
|
|
8
|
+
* a real call-site stack for a `fetch()` request and attaches it to that
|
|
9
|
+
* request's `NETWORK_REQUEST` evidence. This file is what joins that stack
|
|
10
|
+
* to the graph: it resolves the stack's own site of interest
|
|
11
|
+
* (`primaryFrameLocation`, the identical rule every other stack-to-evidence
|
|
12
|
+
* join in this codebase already uses) to a `FUNCTION` node via
|
|
13
|
+
* `resolveSymbolNode`, and checks it against `resolveFrontendCallers`'s own
|
|
14
|
+
* structural candidate set for the endpoint in question.
|
|
15
|
+
*
|
|
16
|
+
* **Additive, not a replacement.** `resolveFrontendCallers` is untouched —
|
|
17
|
+
* every existing caller of it keeps exactly the outcomes it already had.
|
|
18
|
+
* This function calls it internally and only ever *adds* two outcomes on
|
|
19
|
+
* top: `observed`, when the stack resolves to a node the structural side
|
|
20
|
+
* already named as a candidate, and `structural-observed-conflict`, when it
|
|
21
|
+
* resolves to a real node the structural side did not name at all. Outside
|
|
22
|
+
* both — no stack, an unresolvable frame, an ambiguous or missing symbol —
|
|
23
|
+
* this returns exactly what `resolveFrontendCallers` alone would have, so
|
|
24
|
+
* every existing refusal discipline (`sole-structural-caller`, `ambiguous`,
|
|
25
|
+
* `not-found`) stands unchanged.
|
|
26
|
+
*
|
|
27
|
+
* **A disagreement is reported, never adjudicated.** When the resolved node
|
|
28
|
+
* is real but not among the structural candidates, that is the graph and
|
|
29
|
+
* the observation disagreeing — possibly because `USES_API` extraction
|
|
30
|
+
* missed a real call site (a dynamic call, a wrapper the adapter does not
|
|
31
|
+
* recognise), possibly because the resolved frame is itself wrong in some
|
|
32
|
+
* way this function cannot see. Both are worth a reader's attention;
|
|
33
|
+
* neither is silently preferred over the other. This is evidence detail
|
|
34
|
+
* within whichever of this project's five report categories the caller's
|
|
35
|
+
* own claim already lands in — not a sixth category, and not decided here.
|
|
36
|
+
*/
|
|
37
|
+
import type { IRNode } from "@descryy/ir";
|
|
38
|
+
import type { SqlDriver } from "@descryy/core";
|
|
39
|
+
import { type StackTrace } from "@descryy/runtime-contracts";
|
|
40
|
+
import { type FrontendCallerResolution } from "./frontend-caller.ts";
|
|
41
|
+
export type ObservedFrontendCallerResolution = FrontendCallerResolution | {
|
|
42
|
+
/** A real captured call-site stack resolved to a node the structural side already named for this endpoint. The genuinely observed answer, not the sole candidate the source admits. */
|
|
43
|
+
readonly outcome: "observed";
|
|
44
|
+
readonly node: IRNode;
|
|
45
|
+
/** Every structural candidate this observation was checked against, for audit — including when it is a single-element `sole-structural-caller` list. */
|
|
46
|
+
readonly structuralCandidates: readonly IRNode[];
|
|
47
|
+
readonly reason: string;
|
|
48
|
+
} | {
|
|
49
|
+
/** A real captured call-site stack resolved to a real node, and that node is NOT among the structural candidates (possibly none at all) for this endpoint. */
|
|
50
|
+
readonly outcome: "structural-observed-conflict";
|
|
51
|
+
readonly structuralCandidates: readonly IRNode[];
|
|
52
|
+
readonly observedNode: IRNode;
|
|
53
|
+
readonly reason: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* `stackTrace` is exactly `NETWORK_REQUEST.stackTrace` off the observed
|
|
57
|
+
* request that hit `endpointNodeId` — `null` whenever the collector could
|
|
58
|
+
* not attach one (no parser wired, header stripped in transit, nothing
|
|
59
|
+
* buffered for the id), which this function treats identically to "no
|
|
60
|
+
* observation was ever attempted": the structural answer, unchanged.
|
|
61
|
+
*
|
|
62
|
+
* `repo`/`repoRoot` are `resolveSymbolNode`'s own — the frontend repository
|
|
63
|
+
* the observed stack's frames are expected to belong to, since `FUNCTION`
|
|
64
|
+
* is repo-scoped and there is no honest default for it (see `symbol.ts`'s
|
|
65
|
+
* own doc comment).
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveObservedFrontendCaller(driver: SqlDriver, endpointNodeId: string, stackTrace: StackTrace | null, repo: string, options?: {
|
|
68
|
+
readonly repoRoot?: string;
|
|
69
|
+
}): ObservedFrontendCallerResolution;
|
|
70
|
+
//# sourceMappingURL=observed-frontend-caller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observed-frontend-caller.d.ts","sourceRoot":"","sources":["../src/observed-frontend-caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAEnF,OAAO,EAA0B,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAG7F,MAAM,MAAM,gCAAgC,GACxC,wBAAwB,GACxB;IACE,uLAAuL;IACvL,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wJAAwJ;IACxJ,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GACD;IACE,8JAA8J;IAC9J,QAAQ,CAAC,OAAO,EAAE,8BAA8B,CAAC;IACjD,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAQN;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,SAAS,EACjB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,UAAU,GAAG,IAAI,EAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,gCAAgC,CAwDlC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The **observed** half of `frontend-caller.ts`'s deliberately structural
|
|
3
|
+
* answer — closing the gap that file's own doc comment names: "Playwright
|
|
4
|
+
* exposes an initiator chain that could narrow it; the network collector
|
|
5
|
+
* does not capture it today."
|
|
6
|
+
*
|
|
7
|
+
* `@descryhq-wq/runtime-browser`'s `frontend-initiator-capture.ts` now captures
|
|
8
|
+
* a real call-site stack for a `fetch()` request and attaches it to that
|
|
9
|
+
* request's `NETWORK_REQUEST` evidence. This file is what joins that stack
|
|
10
|
+
* to the graph: it resolves the stack's own site of interest
|
|
11
|
+
* (`primaryFrameLocation`, the identical rule every other stack-to-evidence
|
|
12
|
+
* join in this codebase already uses) to a `FUNCTION` node via
|
|
13
|
+
* `resolveSymbolNode`, and checks it against `resolveFrontendCallers`'s own
|
|
14
|
+
* structural candidate set for the endpoint in question.
|
|
15
|
+
*
|
|
16
|
+
* **Additive, not a replacement.** `resolveFrontendCallers` is untouched —
|
|
17
|
+
* every existing caller of it keeps exactly the outcomes it already had.
|
|
18
|
+
* This function calls it internally and only ever *adds* two outcomes on
|
|
19
|
+
* top: `observed`, when the stack resolves to a node the structural side
|
|
20
|
+
* already named as a candidate, and `structural-observed-conflict`, when it
|
|
21
|
+
* resolves to a real node the structural side did not name at all. Outside
|
|
22
|
+
* both — no stack, an unresolvable frame, an ambiguous or missing symbol —
|
|
23
|
+
* this returns exactly what `resolveFrontendCallers` alone would have, so
|
|
24
|
+
* every existing refusal discipline (`sole-structural-caller`, `ambiguous`,
|
|
25
|
+
* `not-found`) stands unchanged.
|
|
26
|
+
*
|
|
27
|
+
* **A disagreement is reported, never adjudicated.** When the resolved node
|
|
28
|
+
* is real but not among the structural candidates, that is the graph and
|
|
29
|
+
* the observation disagreeing — possibly because `USES_API` extraction
|
|
30
|
+
* missed a real call site (a dynamic call, a wrapper the adapter does not
|
|
31
|
+
* recognise), possibly because the resolved frame is itself wrong in some
|
|
32
|
+
* way this function cannot see. Both are worth a reader's attention;
|
|
33
|
+
* neither is silently preferred over the other. This is evidence detail
|
|
34
|
+
* within whichever of this project's five report categories the caller's
|
|
35
|
+
* own claim already lands in — not a sixth category, and not decided here.
|
|
36
|
+
*/
|
|
37
|
+
import { primaryFrameLocation } from "@descryy/runtime-contracts";
|
|
38
|
+
import { resolveFrontendCallers } from "./frontend-caller.js";
|
|
39
|
+
import { resolveSymbolNode } from "./symbol.js";
|
|
40
|
+
function structuralCandidatesOf(structural) {
|
|
41
|
+
if (structural.outcome === "sole-structural-caller")
|
|
42
|
+
return [structural.node];
|
|
43
|
+
if (structural.outcome === "ambiguous")
|
|
44
|
+
return structural.candidates;
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `stackTrace` is exactly `NETWORK_REQUEST.stackTrace` off the observed
|
|
49
|
+
* request that hit `endpointNodeId` — `null` whenever the collector could
|
|
50
|
+
* not attach one (no parser wired, header stripped in transit, nothing
|
|
51
|
+
* buffered for the id), which this function treats identically to "no
|
|
52
|
+
* observation was ever attempted": the structural answer, unchanged.
|
|
53
|
+
*
|
|
54
|
+
* `repo`/`repoRoot` are `resolveSymbolNode`'s own — the frontend repository
|
|
55
|
+
* the observed stack's frames are expected to belong to, since `FUNCTION`
|
|
56
|
+
* is repo-scoped and there is no honest default for it (see `symbol.ts`'s
|
|
57
|
+
* own doc comment).
|
|
58
|
+
*/
|
|
59
|
+
export function resolveObservedFrontendCaller(driver, endpointNodeId, stackTrace, repo, options = {}) {
|
|
60
|
+
const structural = resolveFrontendCallers(driver, endpointNodeId);
|
|
61
|
+
const location = primaryFrameLocation(stackTrace);
|
|
62
|
+
// No stack, an empty stack, or one whose site of interest names nothing
|
|
63
|
+
// (a native/anonymous frame, or one this parser could not symbolicate at
|
|
64
|
+
// all) -- resolveSymbolNode has no name to search for either way, so the
|
|
65
|
+
// observation adds nothing usable. Same "no id, no functionName" fallback
|
|
66
|
+
// `resolveInitiatorStack` on the collector side already degrades to.
|
|
67
|
+
if (location === null || location.file === null || location.functionName === null) {
|
|
68
|
+
return structural;
|
|
69
|
+
}
|
|
70
|
+
const resolved = resolveSymbolNode(driver, location.line === null
|
|
71
|
+
? { file: location.file, symbolName: location.functionName }
|
|
72
|
+
: { file: location.file, symbolName: location.functionName, line: location.line }, repo, options);
|
|
73
|
+
// Unresolved, ambiguous, or a line that does not fall inside the matched
|
|
74
|
+
// node's range: none of those is "the observed function", so none of
|
|
75
|
+
// them can upgrade or conflict with the structural claim. The existing
|
|
76
|
+
// structural-only outcome stands -- this function never invents a
|
|
77
|
+
// caller resolveSymbolNode itself refused to name.
|
|
78
|
+
if (resolved.outcome !== "resolved") {
|
|
79
|
+
return structural;
|
|
80
|
+
}
|
|
81
|
+
const structuralCandidates = structuralCandidatesOf(structural);
|
|
82
|
+
const isStructuralCandidate = structuralCandidates.some((candidate) => candidate.id === resolved.node.id);
|
|
83
|
+
if (isStructuralCandidate) {
|
|
84
|
+
return {
|
|
85
|
+
outcome: "observed",
|
|
86
|
+
node: resolved.node,
|
|
87
|
+
structuralCandidates,
|
|
88
|
+
reason: `"${resolved.node.name}" was resolved from a real call-site stack captured on the observed request, and it is ` +
|
|
89
|
+
`among the function(s) the graph's USES_API edge${structuralCandidates.length > 1 ? "s name" : " names"} for ` +
|
|
90
|
+
"this endpoint -- a genuinely observed answer, not the sole candidate the source admits.",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
outcome: "structural-observed-conflict",
|
|
95
|
+
structuralCandidates,
|
|
96
|
+
observedNode: resolved.node,
|
|
97
|
+
reason: `The observed request's call-site stack resolved to "${resolved.node.name}", which is NOT among the ` +
|
|
98
|
+
`function(s) the graph's USES_API edges name for this endpoint (${structuralCandidates.map((n) => n.name).join(", ") || "none"}). ` +
|
|
99
|
+
"This is a real disagreement between the static graph and what was actually observed -- reported as a " +
|
|
100
|
+
"conflict rather than silently trusted or discarded in either direction.",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=observed-frontend-caller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observed-frontend-caller.js","sourceRoot":"","sources":["../src/observed-frontend-caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAIH,OAAO,EAAE,oBAAoB,EAAmB,MAAM,4BAA4B,CAAC;AAEnF,OAAO,EAAE,sBAAsB,EAAiC,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAoBhD,SAAS,sBAAsB,CAAC,UAAoC;IAClE,IAAI,UAAU,CAAC,OAAO,KAAK,wBAAwB;QAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9E,IAAI,UAAU,CAAC,OAAO,KAAK,WAAW;QAAE,OAAO,UAAU,CAAC,UAAU,CAAC;IACrE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAC3C,MAAiB,EACjB,cAAsB,EACtB,UAA6B,EAC7B,IAAY,EACZ,UAA0C,EAAE;IAE5C,MAAM,UAAU,GAAG,sBAAsB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAElE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAClD,wEAAwE;IACxE,yEAAyE;IACzE,yEAAyE;IACzE,0EAA0E;IAC1E,qEAAqE;IACrE,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QAClF,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,QAAQ,GAAG,iBAAiB,CAChC,MAAM,EACN,QAAQ,CAAC,IAAI,KAAK,IAAI;QACpB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,YAAY,EAAE;QAC5D,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,YAAY,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EACnF,IAAI,EACJ,OAAO,CACR,CAAC;IAEF,yEAAyE;IACzE,qEAAqE;IACrE,uEAAuE;IACvE,kEAAkE;IAClE,mDAAmD;IACnD,IAAI,QAAQ,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,oBAAoB,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;IAChE,MAAM,qBAAqB,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE1G,IAAI,qBAAqB,EAAE,CAAC;QAC1B,OAAO;YACL,OAAO,EAAE,UAAU;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,oBAAoB;YACpB,MAAM,EACJ,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,yFAAyF;gBAC/G,kDAAkD,oBAAoB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,OAAO;gBAC9G,yFAAyF;SAC5F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,8BAA8B;QACvC,oBAAoB;QACpB,YAAY,EAAE,QAAQ,CAAC,IAAI;QAC3B,MAAM,EACJ,uDAAuD,QAAQ,CAAC,IAAI,CAAC,IAAI,4BAA4B;YACrG,kEAAkE,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,KAAK;YACnI,uGAAuG;YACvG,yEAAyE;KAC5E,CAAC;AACJ,CAAC"}
|