@jshookmcp/jshook 0.2.8 → 0.3.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/README.md +36 -5
- package/README.zh.md +36 -5
- package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-CqGDXmfc.mjs} +160 -54
- package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-BdjRfNx7.mjs} +5 -5
- package/dist/ConsoleMonitor-DykL3IAw.mjs +2269 -0
- package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-ETyy0xyo.mjs} +1 -1
- package/dist/DetailedDataManager-HT49OrvF.mjs +217 -0
- package/dist/EventBus-DFKvADm3.mjs +141 -0
- package/dist/EvidenceGraphBridge-318Oi0Lf.mjs +153 -0
- package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-BDMsY2Dz.mjs} +27 -13
- package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-BN4UQWnX.mjs} +1 -1
- package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-Cc2AFq1Y.mjs} +3 -3
- package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-DruMgsgj.mjs} +21 -21
- package/dist/HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs +566 -0
- package/dist/InstrumentationSession-DLH0vd-z.mjs +244 -0
- package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CMtviNW_.mjs} +3 -3
- package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-ITgb_NMi.mjs} +81 -78
- package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-CiL7Z3ey.mjs} +50 -21
- package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs} +20 -56
- package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-BBlAixF5.mjs} +1 -1
- package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-DMQ44gen.mjs} +16 -16
- package/dist/PageController-BPJNqqBN.mjs +431 -0
- package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-K7wN8Z-w.mjs} +10 -7
- package/dist/PrerequisiteError-TuyZIs6n.mjs +20 -0
- package/dist/ProcessRegistry-zGg12QbE.mjs +74 -0
- package/dist/ResponseBuilder-CJXWmWNw.mjs +143 -0
- package/dist/ReverseEvidenceGraph-C02-gXOh.mjs +269 -0
- package/dist/ScriptManager-ZuWD-0Jg.mjs +3003 -0
- package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-D-z0umeT.mjs} +2 -2
- package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-Cav5AVSL.mjs} +9 -6
- package/dist/ToolCatalog-5OJdMiF0.mjs +582 -0
- package/dist/ToolError-jh9whhMd.mjs +15 -0
- package/dist/ToolProbe-DbCFGyrg.mjs +45 -0
- package/dist/ToolRegistry-B9krbTtI.mjs +180 -0
- package/dist/ToolRouter.policy-BGDAGyeH.mjs +344 -0
- package/dist/TraceRecorder-B41Z5XBj.mjs +1286 -0
- package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-C2kjj0ze.mjs} +19 -13
- package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-CKrGOTpo.mjs} +3 -3
- package/dist/WorkflowEngine-DJ6M4opp.mjs +569 -0
- package/dist/analysis-BHeJW2Nb.mjs +1234 -0
- package/dist/antidebug-BRKeyt27.mjs +1081 -0
- package/dist/artifactRetention-CPXkUJXp.mjs +598 -0
- package/dist/artifacts-DkfosXH3.mjs +59 -0
- package/dist/authorization-schema-DRqyJMSk.mjs +31 -0
- package/dist/betterSqlite3-DLSBZodi.mjs +74 -0
- package/dist/binary-instrument--V3MAhJ4.mjs +971 -0
- package/dist/bind-helpers-ClV34xdn.mjs +42 -0
- package/dist/boringssl-inspector-Bo_LOLaS.mjs +180 -0
- package/dist/browser-Dx3_S2cG.mjs +4369 -0
- package/dist/capabilities-CcHlvWgK.mjs +33 -0
- package/dist/concurrency-Drev_Vz9.mjs +41 -0
- package/dist/{constants-CCvsN80K.mjs → constants-CDZLOoVv.mjs} +105 -48
- package/dist/coordination-DgItD9DL.mjs +259 -0
- package/dist/debugger-RS3RSAqs.mjs +1288 -0
- package/dist/definitions-BEoYofW5.mjs +47 -0
- package/dist/definitions-BRaefg3u.mjs +365 -0
- package/dist/definitions-BbkvZkiv.mjs +96 -0
- package/dist/definitions-BtWSHJ3o.mjs +17 -0
- package/dist/definitions-C1gCHO0i.mjs +43 -0
- package/dist/definitions-CDOg_b-l.mjs +138 -0
- package/dist/definitions-CVPD9hzZ.mjs +54 -0
- package/dist/definitions-Cea8Lgl7.mjs +94 -0
- package/dist/definitions-DAgIyjxM.mjs +10 -0
- package/dist/definitions-DJA27nsL.mjs +66 -0
- package/dist/definitions-DKPFU3LW.mjs +25 -0
- package/dist/definitions-DPRpZQ96.mjs +47 -0
- package/dist/definitions-DUE5gmdn.mjs +18 -0
- package/dist/definitions-DYVjOtxa.mjs +26 -0
- package/dist/definitions-DcYLVLCo.mjs +37 -0
- package/dist/definitions-Pp5LI2H4.mjs +27 -0
- package/dist/definitions-j9KdHVNR.mjs +14 -0
- package/dist/definitions-uzkjBwa7.mjs +258 -0
- package/dist/definitions-va-AnLuQ.mjs +28 -0
- package/dist/encoding-DJeqHmpd.mjs +1079 -0
- package/dist/evidence-graph-bridge-DcYizFk2.mjs +136 -0
- package/dist/{factory-CibqTNC8.mjs → factory-C90tBff6.mjs} +41 -56
- package/dist/flat-target-session-Dgax2Cy3.mjs +29 -0
- package/dist/graphql-CoHrhweh.mjs +1197 -0
- package/dist/handlers-4jmR0nMs.mjs +898 -0
- package/dist/handlers-BAHPxcch.mjs +789 -0
- package/dist/handlers-BOs9b907.mjs +2600 -0
- package/dist/handlers-BWXEy6ef.mjs +917 -0
- package/dist/handlers-Bndn6QvE.mjs +111 -0
- package/dist/handlers-BqC4bD4s.mjs +681 -0
- package/dist/handlers-BtYq60bM2.mjs +276 -0
- package/dist/handlers-BzgcB4iv.mjs +799 -0
- package/dist/handlers-CRyRWj2b.mjs +859 -0
- package/dist/handlers-CVv2H1uq.mjs +592 -0
- package/dist/handlers-Dl5a7JS4.mjs +572 -0
- package/dist/handlers-Dx2d7jt7.mjs +2537 -0
- package/dist/handlers-Dz9PYsCa.mjs +2805 -0
- package/dist/handlers-HujRKC3b.mjs +661 -0
- package/dist/handlers.impl-XWXkQfyi.mjs +807 -0
- package/dist/hooks-B1B8NRHL.mjs +898 -0
- package/dist/index.mjs +491 -259
- package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
- package/dist/maintenance-PRMkLVRW.mjs +835 -0
- package/dist/manifest-67Bok-Si.mjs +58 -0
- package/dist/manifest-6lNTMZAB2.mjs +87 -0
- package/dist/manifest-B2duEHiH.mjs +90 -0
- package/dist/manifest-B6EY9Vm8.mjs +57 -0
- package/dist/manifest-B6nKSbyY.mjs +95 -0
- package/dist/manifest-BL8AQNPF.mjs +106 -0
- package/dist/manifest-BSZvJJmV.mjs +47 -0
- package/dist/manifest-BU7qzUyX.mjs +418 -0
- package/dist/manifest-Bl62e8WK.mjs +49 -0
- package/dist/manifest-Bo5cXjdt.mjs +82 -0
- package/dist/manifest-BpS4gtUK.mjs +1347 -0
- package/dist/manifest-Bv65_e2W.mjs +101 -0
- package/dist/manifest-BytNIF4Z.mjs +117 -0
- package/dist/manifest-C-xtsjS3.mjs +81 -0
- package/dist/manifest-CDYl7OhA.mjs +66 -0
- package/dist/manifest-CRZ3xmkD.mjs +61 -0
- package/dist/manifest-CoW6u4Tp.mjs +132 -0
- package/dist/manifest-Cq5zN_8A.mjs +50 -0
- package/dist/manifest-D7YZM_2e.mjs +194 -0
- package/dist/manifest-DE_VrAeQ.mjs +314 -0
- package/dist/manifest-DGsXSCpT.mjs +39 -0
- package/dist/manifest-DJ2vfEuW.mjs +156 -0
- package/dist/manifest-DPXDYhEu.mjs +80 -0
- package/dist/manifest-Dd4fQb0a.mjs +322 -0
- package/dist/manifest-Deq6opGg.mjs +223 -0
- package/dist/manifest-DfJTafJK.mjs +37 -0
- package/dist/manifest-DgOdgN_j.mjs +50 -0
- package/dist/manifest-DlbMW4v4.mjs +47 -0
- package/dist/manifest-DmVfbH0w.mjs +374 -0
- package/dist/manifest-Dog6Ddjr.mjs +109 -0
- package/dist/manifest-DvgU5FWb.mjs +58 -0
- package/dist/manifest-HsfDBs7j.mjs +50 -0
- package/dist/manifest-I8oQHvCG.mjs +186 -0
- package/dist/manifest-NvH_a-av.mjs +786 -0
- package/dist/manifest-cEJU1v0Z.mjs +129 -0
- package/dist/manifest-wOl5XLB12.mjs +112 -0
- package/dist/modules-tZozf0LQ.mjs +10635 -0
- package/dist/mojo-ipc-DXNEXEqb.mjs +640 -0
- package/dist/network-CPVvwvFg.mjs +3852 -0
- package/dist/{artifacts-BbdOMET5.mjs → outputPaths-um7lCRY3.mjs} +219 -216
- package/dist/parse-args-B4cY5Vx5.mjs +39 -0
- package/dist/platform-CYeFoTWp.mjs +2161 -0
- package/dist/process-BTbgcVc6.mjs +1306 -0
- package/dist/proxy-r8YN6nP1.mjs +192 -0
- package/dist/registry-Bl8ZQW61.mjs +34 -0
- package/dist/response-CWhh2aLo.mjs +34 -0
- package/dist/server/plugin-api.mjs +2 -2
- package/dist/shared-state-board-BoZnSoj-.mjs +586 -0
- package/dist/sourcemap-BIDHUVXy.mjs +934 -0
- package/dist/ssrf-policy-Dsqd-DTX.mjs +166 -0
- package/dist/streaming-Dal6utPp.mjs +725 -0
- package/dist/tool-builder-BHJp32mV.mjs +186 -0
- package/dist/transform-DRVgGG90.mjs +1011 -0
- package/dist/types-Bx92KJfT.mjs +4 -0
- package/dist/wasm-BYx5UOeG.mjs +1044 -0
- package/dist/webcrack-Be0_FccV.mjs +747 -0
- package/dist/workflow-BpuKEtvn.mjs +725 -0
- package/package.json +82 -49
- package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
- package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
- package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-jJFy20G-.mjs} +0 -0
- package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-BWmPgQsv.mjs} +0 -0
- package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-K3V4vGsw.mjs} +0 -0
- package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-nnMvEohD.mjs} +0 -0
- package/dist/{types-BBjOqye-.mjs → types-DDBWs9UP.mjs} +1 -1
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
import { at as GRAPHQL_MAX_SCHEMA_CHARS, it as GRAPHQL_MAX_QUERY_CHARS, nt as GRAPHQL_MAX_GRAPH_NODES, rt as GRAPHQL_MAX_PREVIEW_CHARS, tt as GRAPHQL_MAX_GRAPH_EDGES } from "./constants-CDZLOoVv.mjs";
|
|
2
|
+
import { a as argString, i as argObject, t as argBool } from "./parse-args-B4cY5Vx5.mjs";
|
|
3
|
+
import { s as evaluateWithTimeout } from "./PageController-BPJNqqBN.mjs";
|
|
4
|
+
import { c as isSsrfTarget } from "./ssrf-policy-Dsqd-DTX.mjs";
|
|
5
|
+
import "./definitions-Pp5LI2H4.mjs";
|
|
6
|
+
//#region src/server/domains/graphql/handlers/shared.ts
|
|
7
|
+
function toResponse(payload) {
|
|
8
|
+
return { content: [{
|
|
9
|
+
type: "text",
|
|
10
|
+
text: JSON.stringify(payload, null, 2)
|
|
11
|
+
}] };
|
|
12
|
+
}
|
|
13
|
+
function toError(error, context) {
|
|
14
|
+
const payload = {
|
|
15
|
+
success: false,
|
|
16
|
+
error: getErrorMessage(error)
|
|
17
|
+
};
|
|
18
|
+
if (context) payload.context = context;
|
|
19
|
+
return {
|
|
20
|
+
content: [{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: JSON.stringify(payload, null, 2)
|
|
23
|
+
}],
|
|
24
|
+
isError: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function getErrorMessage(error) {
|
|
28
|
+
if (error instanceof Error) return error.message;
|
|
29
|
+
return String(error);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a number argument from args, supporting string-to-number coercion,
|
|
33
|
+
* min/max clamping, and integer truncation.
|
|
34
|
+
* Preserves behavior from the old GraphQLHandlersBase.getNumberArg().
|
|
35
|
+
*/
|
|
36
|
+
function parseClampedNumber(args, key, defaultValue, min, max) {
|
|
37
|
+
const value = args[key];
|
|
38
|
+
let parsed = defaultValue;
|
|
39
|
+
if (typeof value === "number" && Number.isFinite(value)) parsed = value;
|
|
40
|
+
else if (typeof value === "string") {
|
|
41
|
+
const fromString = Number(value);
|
|
42
|
+
if (Number.isFinite(fromString)) parsed = fromString;
|
|
43
|
+
}
|
|
44
|
+
if (parsed < min) return min;
|
|
45
|
+
if (parsed > max) return max;
|
|
46
|
+
return Math.trunc(parsed);
|
|
47
|
+
}
|
|
48
|
+
function normalizeHeaders(value) {
|
|
49
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
50
|
+
const dangerousKeys = new Set([
|
|
51
|
+
"__proto__",
|
|
52
|
+
"constructor",
|
|
53
|
+
"prototype"
|
|
54
|
+
]);
|
|
55
|
+
const headers = Object.create(null);
|
|
56
|
+
for (const [header, rawValue] of Object.entries(value)) {
|
|
57
|
+
if (dangerousKeys.has(header)) continue;
|
|
58
|
+
if (typeof rawValue === "string") headers[header] = rawValue;
|
|
59
|
+
else if (typeof rawValue === "number" || typeof rawValue === "boolean") headers[header] = String(rawValue);
|
|
60
|
+
}
|
|
61
|
+
return headers;
|
|
62
|
+
}
|
|
63
|
+
function parseEndpoint(endpoint) {
|
|
64
|
+
let parsedEndpoint;
|
|
65
|
+
try {
|
|
66
|
+
parsedEndpoint = new URL(endpoint);
|
|
67
|
+
} catch {
|
|
68
|
+
return { error: `Invalid endpoint URL: ${endpoint}` };
|
|
69
|
+
}
|
|
70
|
+
if (parsedEndpoint.protocol !== "http:" && parsedEndpoint.protocol !== "https:") return { error: `Unsupported endpoint protocol: ${parsedEndpoint.protocol} — only http/https allowed` };
|
|
71
|
+
return { parsedEndpoint };
|
|
72
|
+
}
|
|
73
|
+
async function validateExternalEndpoint(endpoint) {
|
|
74
|
+
const parsed = parseEndpoint(endpoint);
|
|
75
|
+
if ("error" in parsed) return parsed.error;
|
|
76
|
+
if (await isSsrfTarget(parsed.parsedEndpoint.toString())) return `Blocked: endpoint "${endpoint}" resolves to a private/reserved address`;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
async function validateBrowserEndpoint(endpoint, currentPageUrl) {
|
|
80
|
+
const parsed = parseEndpoint(endpoint);
|
|
81
|
+
if ("error" in parsed) return parsed.error;
|
|
82
|
+
if (!await isSsrfTarget(parsed.parsedEndpoint.toString())) return null;
|
|
83
|
+
if (typeof currentPageUrl === "string" && currentPageUrl.length > 0) try {
|
|
84
|
+
if (new URL(currentPageUrl).origin === parsed.parsedEndpoint.origin) return null;
|
|
85
|
+
} catch {}
|
|
86
|
+
return `Blocked: endpoint "${endpoint}" resolves to a private/reserved address`;
|
|
87
|
+
}
|
|
88
|
+
function createPreview(text, maxChars) {
|
|
89
|
+
if (text.length <= maxChars) return {
|
|
90
|
+
preview: text,
|
|
91
|
+
truncated: false,
|
|
92
|
+
totalLength: text.length
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
preview: `${text.slice(0, maxChars)}\n... (truncated)`,
|
|
96
|
+
truncated: true,
|
|
97
|
+
totalLength: text.length
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function serializeForPreview(value, maxChars) {
|
|
101
|
+
let serialized;
|
|
102
|
+
if (typeof value === "string") serialized = value;
|
|
103
|
+
else try {
|
|
104
|
+
serialized = JSON.stringify(value, null, 2);
|
|
105
|
+
} catch {
|
|
106
|
+
serialized = String(value);
|
|
107
|
+
}
|
|
108
|
+
return createPreview(serialized, maxChars);
|
|
109
|
+
}
|
|
110
|
+
function parseMatchType(value) {
|
|
111
|
+
if (value === "exact" || value === "contains" || value === "regex") return value;
|
|
112
|
+
return "contains";
|
|
113
|
+
}
|
|
114
|
+
function generateRuleId() {
|
|
115
|
+
return `script_rule_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
116
|
+
}
|
|
117
|
+
function isRequestInterceptHandled(request) {
|
|
118
|
+
if (typeof request.isInterceptResolutionHandled !== "function") return false;
|
|
119
|
+
try {
|
|
120
|
+
return request.isInterceptResolutionHandled();
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function continueRequest(request) {
|
|
126
|
+
if (isRequestInterceptHandled(request)) return;
|
|
127
|
+
try {
|
|
128
|
+
await request.continue();
|
|
129
|
+
} catch {}
|
|
130
|
+
}
|
|
131
|
+
function ruleMatchesUrl(rule, targetUrl) {
|
|
132
|
+
if (rule.matchType === "exact") return targetUrl === rule.url;
|
|
133
|
+
if (rule.matchType === "contains") return targetUrl.includes(rule.url);
|
|
134
|
+
try {
|
|
135
|
+
return new RegExp(rule.url).test(targetUrl);
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function findMatchingRule(rules, url) {
|
|
141
|
+
for (let index = rules.length - 1; index >= 0; index -= 1) {
|
|
142
|
+
const rule = rules[index];
|
|
143
|
+
if (rule && ruleMatchesUrl(rule, url)) return rule;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
async function handleInterceptedRequest(rules, request) {
|
|
148
|
+
if (isRequestInterceptHandled(request)) return;
|
|
149
|
+
if (request.resourceType() !== "script") {
|
|
150
|
+
await continueRequest(request);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const matchedRule = findMatchingRule(rules, request.url());
|
|
154
|
+
if (!matchedRule) {
|
|
155
|
+
await continueRequest(request);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
matchedRule.hits += 1;
|
|
159
|
+
try {
|
|
160
|
+
await request.respond({
|
|
161
|
+
status: 200,
|
|
162
|
+
contentType: "application/javascript; charset=utf-8",
|
|
163
|
+
headers: {
|
|
164
|
+
"cache-control": "no-store",
|
|
165
|
+
"x-script-replaced-by": "script_replace_persist"
|
|
166
|
+
},
|
|
167
|
+
body: matchedRule.replacement
|
|
168
|
+
});
|
|
169
|
+
} catch {
|
|
170
|
+
await continueRequest(request);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async function ensureScriptInterception(rules, installedPages, page) {
|
|
174
|
+
if (installedPages.has(page)) return;
|
|
175
|
+
await page.setRequestInterception(true);
|
|
176
|
+
const listener = (request) => {
|
|
177
|
+
handleInterceptedRequest(rules, request);
|
|
178
|
+
};
|
|
179
|
+
const eventHost = page;
|
|
180
|
+
if (typeof eventHost.prependListener === "function") eventHost.prependListener("request", listener);
|
|
181
|
+
else eventHost.on("request", listener);
|
|
182
|
+
installedPages.add(page);
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/server/domains/graphql/handlers.impl.core.runtime.shared.ts
|
|
186
|
+
const INTROSPECTION_QUERY = `
|
|
187
|
+
query IntrospectionQuery {
|
|
188
|
+
__schema {
|
|
189
|
+
queryType { name }
|
|
190
|
+
mutationType { name }
|
|
191
|
+
subscriptionType { name }
|
|
192
|
+
types { ...FullType }
|
|
193
|
+
directives {
|
|
194
|
+
name
|
|
195
|
+
description
|
|
196
|
+
locations
|
|
197
|
+
args(includeDeprecated: true) { ...InputValue }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
fragment FullType on __Type {
|
|
202
|
+
kind
|
|
203
|
+
name
|
|
204
|
+
description
|
|
205
|
+
fields(includeDeprecated: true) {
|
|
206
|
+
name
|
|
207
|
+
description
|
|
208
|
+
args(includeDeprecated: true) { ...InputValue }
|
|
209
|
+
type { ...TypeRef }
|
|
210
|
+
isDeprecated
|
|
211
|
+
deprecationReason
|
|
212
|
+
}
|
|
213
|
+
inputFields(includeDeprecated: true) { ...InputValue }
|
|
214
|
+
interfaces { ...TypeRef }
|
|
215
|
+
enumValues(includeDeprecated: true) {
|
|
216
|
+
name
|
|
217
|
+
description
|
|
218
|
+
isDeprecated
|
|
219
|
+
deprecationReason
|
|
220
|
+
}
|
|
221
|
+
possibleTypes { ...TypeRef }
|
|
222
|
+
}
|
|
223
|
+
fragment InputValue on __InputValue {
|
|
224
|
+
name
|
|
225
|
+
description
|
|
226
|
+
type { ...TypeRef }
|
|
227
|
+
defaultValue
|
|
228
|
+
isDeprecated
|
|
229
|
+
deprecationReason
|
|
230
|
+
}
|
|
231
|
+
fragment TypeRef on __Type {
|
|
232
|
+
kind
|
|
233
|
+
name
|
|
234
|
+
ofType {
|
|
235
|
+
kind
|
|
236
|
+
name
|
|
237
|
+
ofType {
|
|
238
|
+
kind
|
|
239
|
+
name
|
|
240
|
+
ofType {
|
|
241
|
+
kind
|
|
242
|
+
name
|
|
243
|
+
ofType {
|
|
244
|
+
kind
|
|
245
|
+
name
|
|
246
|
+
ofType {
|
|
247
|
+
kind
|
|
248
|
+
name
|
|
249
|
+
ofType {
|
|
250
|
+
kind
|
|
251
|
+
name
|
|
252
|
+
ofType {
|
|
253
|
+
kind
|
|
254
|
+
name
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
`.trim();
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/server/domains/graphql/handlers/callgraph.ts
|
|
266
|
+
var CallGraphHandlers = class {
|
|
267
|
+
constructor(collector) {
|
|
268
|
+
this.collector = collector;
|
|
269
|
+
}
|
|
270
|
+
async handleCallGraphAnalyze(args) {
|
|
271
|
+
try {
|
|
272
|
+
const maxDepth = parseClampedNumber(args, "maxDepth", 5, 1, 20);
|
|
273
|
+
const filterPattern = argString(args, "filterPattern")?.trim() || "";
|
|
274
|
+
if (filterPattern) try {
|
|
275
|
+
RegExp(filterPattern);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
return toError("Invalid filterPattern regex", {
|
|
278
|
+
filterPattern,
|
|
279
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
const result = await evaluateWithTimeout(await this.collector.getActivePage(), ({ maxDepth: depth, filterPattern: filter }) => {
|
|
283
|
+
const globalScope = window;
|
|
284
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
285
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
286
|
+
let scannedRecords = 0;
|
|
287
|
+
let acceptedRecords = 0;
|
|
288
|
+
const filterRegex = filter ? new RegExp(filter) : null;
|
|
289
|
+
const matchesFilter = (name) => {
|
|
290
|
+
if (!filterRegex) return true;
|
|
291
|
+
filterRegex.lastIndex = 0;
|
|
292
|
+
return filterRegex.test(name);
|
|
293
|
+
};
|
|
294
|
+
const includeEdge = (source, target) => {
|
|
295
|
+
if (!filterRegex) return true;
|
|
296
|
+
return matchesFilter(source) || matchesFilter(target);
|
|
297
|
+
};
|
|
298
|
+
const incrementNode = (name, by = 1) => {
|
|
299
|
+
const existing = nodeMap.get(name);
|
|
300
|
+
if (existing) {
|
|
301
|
+
existing.callCount += by;
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
nodeMap.set(name, {
|
|
305
|
+
id: name,
|
|
306
|
+
name,
|
|
307
|
+
callCount: by
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
const addEdge = (sourceRaw, targetRaw) => {
|
|
311
|
+
const source = typeof sourceRaw === "string" ? sourceRaw.trim() || "" : "";
|
|
312
|
+
const target = typeof targetRaw === "string" ? targetRaw.trim() || "" : "";
|
|
313
|
+
if (!source || !target || source === target) return;
|
|
314
|
+
if (!includeEdge(source, target)) return;
|
|
315
|
+
const key = `${source}__->__${target}`;
|
|
316
|
+
const existing = edgeMap.get(key);
|
|
317
|
+
if (existing) existing.count += 1;
|
|
318
|
+
else edgeMap.set(key, {
|
|
319
|
+
source,
|
|
320
|
+
target,
|
|
321
|
+
count: 1
|
|
322
|
+
});
|
|
323
|
+
incrementNode(source, 1);
|
|
324
|
+
incrementNode(target, 1);
|
|
325
|
+
};
|
|
326
|
+
const processRecord = (record, fallbackName) => {
|
|
327
|
+
scannedRecords += 1;
|
|
328
|
+
const calleeRaw = record.callee ?? record.functionName ?? record.fn ?? record.name ?? record.method ?? record.target ?? fallbackName;
|
|
329
|
+
const callee = typeof calleeRaw === "string" ? calleeRaw.trim() || fallbackName : fallbackName;
|
|
330
|
+
const callerRaw = record.caller ?? record.parent ?? record.from ?? "";
|
|
331
|
+
const caller = typeof callerRaw === "string" ? callerRaw.trim() || "" : "";
|
|
332
|
+
let used = false;
|
|
333
|
+
if (caller && callee) {
|
|
334
|
+
addEdge(caller, callee);
|
|
335
|
+
used = true;
|
|
336
|
+
}
|
|
337
|
+
const stackValue = record.stack ?? record.stackTrace ?? record.trace;
|
|
338
|
+
const frames = typeof stackValue === "string" && stackValue.trim().length > 0 ? stackValue.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
|
|
339
|
+
const atMatch = line.match(/at\s+([^(<\s]+)/);
|
|
340
|
+
if (atMatch?.[1]) return atMatch[1];
|
|
341
|
+
const atFileMatch = line.match(/^([^(<\s]+)@/);
|
|
342
|
+
if (atFileMatch?.[1]) return atFileMatch[1];
|
|
343
|
+
return "";
|
|
344
|
+
}).filter((name) => name.length > 0) : [];
|
|
345
|
+
if (frames.length > 1) {
|
|
346
|
+
const depthLimit = Math.min(depth, frames.length - 1);
|
|
347
|
+
for (let index = 0; index < depthLimit; index += 1) addEdge(frames[index + 1], frames[index]);
|
|
348
|
+
used = true;
|
|
349
|
+
} else if (frames.length === 1 && callee && frames[0] !== callee) {
|
|
350
|
+
addEdge(frames[0], callee);
|
|
351
|
+
used = true;
|
|
352
|
+
}
|
|
353
|
+
if (used) acceptedRecords += 1;
|
|
354
|
+
};
|
|
355
|
+
const aiHooks = globalScope.__aiHooks;
|
|
356
|
+
if (aiHooks && typeof aiHooks === "object") for (const [hookName, hookRecords] of Object.entries(aiHooks)) {
|
|
357
|
+
if (!Array.isArray(hookRecords)) continue;
|
|
358
|
+
for (const entry of hookRecords) if (entry && typeof entry === "object") processRecord(entry, hookName);
|
|
359
|
+
}
|
|
360
|
+
for (const key of [
|
|
361
|
+
"__functionTraceRecords",
|
|
362
|
+
"__functionTracerRecords",
|
|
363
|
+
"__functionCalls",
|
|
364
|
+
"__callTrace",
|
|
365
|
+
"__traceCalls"
|
|
366
|
+
]) {
|
|
367
|
+
const records = globalScope[key];
|
|
368
|
+
if (!Array.isArray(records)) continue;
|
|
369
|
+
for (const entry of records) if (entry && typeof entry === "object") processRecord(entry, key);
|
|
370
|
+
}
|
|
371
|
+
const functionTracer = globalScope.__functionTracer;
|
|
372
|
+
if (functionTracer && typeof functionTracer === "object") {
|
|
373
|
+
const records = functionTracer.records;
|
|
374
|
+
if (Array.isArray(records)) {
|
|
375
|
+
for (const entry of records) if (entry && typeof entry === "object") processRecord(entry, "functionTracer.records");
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const nodes = Array.from(nodeMap.values()).toSorted((left, right) => right.callCount - left.callCount);
|
|
379
|
+
const edges = Array.from(edgeMap.values()).toSorted((left, right) => right.count - left.count);
|
|
380
|
+
return {
|
|
381
|
+
nodes,
|
|
382
|
+
edges,
|
|
383
|
+
stats: {
|
|
384
|
+
scannedRecords,
|
|
385
|
+
acceptedRecords,
|
|
386
|
+
nodeCount: nodes.length,
|
|
387
|
+
edgeCount: edges.length,
|
|
388
|
+
maxDepth: depth,
|
|
389
|
+
filterPattern: filter || null
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
}, {
|
|
393
|
+
maxDepth,
|
|
394
|
+
filterPattern
|
|
395
|
+
});
|
|
396
|
+
const nodesTruncated = result.nodes.length > GRAPHQL_MAX_GRAPH_NODES;
|
|
397
|
+
const edgesTruncated = result.edges.length > GRAPHQL_MAX_GRAPH_EDGES;
|
|
398
|
+
return toResponse({
|
|
399
|
+
success: true,
|
|
400
|
+
nodes: result.nodes.slice(0, GRAPHQL_MAX_GRAPH_NODES),
|
|
401
|
+
edges: result.edges.slice(0, GRAPHQL_MAX_GRAPH_EDGES),
|
|
402
|
+
stats: {
|
|
403
|
+
...result.stats,
|
|
404
|
+
nodesReturned: Math.min(result.nodes.length, GRAPHQL_MAX_GRAPH_NODES),
|
|
405
|
+
edgesReturned: Math.min(result.edges.length, GRAPHQL_MAX_GRAPH_EDGES),
|
|
406
|
+
nodesTruncated,
|
|
407
|
+
edgesTruncated
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
} catch (error) {
|
|
411
|
+
return toError(error);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/server/domains/graphql/handlers/script-replace.ts
|
|
417
|
+
var ScriptReplaceHandlers = class {
|
|
418
|
+
scriptReplaceRules = [];
|
|
419
|
+
interceptionInstalledPages = /* @__PURE__ */ new WeakSet();
|
|
420
|
+
constructor(collector) {
|
|
421
|
+
this.collector = collector;
|
|
422
|
+
}
|
|
423
|
+
async handleScriptReplacePersist(args) {
|
|
424
|
+
try {
|
|
425
|
+
const url = argString(args, "url")?.trim();
|
|
426
|
+
const replacement = argString(args, "replacement");
|
|
427
|
+
const matchType = parseMatchType(args.matchType);
|
|
428
|
+
if (!url) return toError("Missing required argument: url");
|
|
429
|
+
if (typeof replacement !== "string" || replacement.length === 0) return toError("Missing required argument: replacement");
|
|
430
|
+
if (matchType === "regex") try {
|
|
431
|
+
RegExp(url);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
return toError("Invalid regex in url for matchType=regex", {
|
|
434
|
+
url,
|
|
435
|
+
reason: getErrorMessage(error)
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const page = await this.collector.getActivePage();
|
|
439
|
+
const rule = {
|
|
440
|
+
id: generateRuleId(),
|
|
441
|
+
url,
|
|
442
|
+
replacement,
|
|
443
|
+
matchType,
|
|
444
|
+
createdAt: Date.now(),
|
|
445
|
+
hits: 0
|
|
446
|
+
};
|
|
447
|
+
this.scriptReplaceRules.push(rule);
|
|
448
|
+
await ensureScriptInterception(this.scriptReplaceRules, this.interceptionInstalledPages, page);
|
|
449
|
+
await page.evaluateOnNewDocument((payload) => {
|
|
450
|
+
const runtimeWindow = window;
|
|
451
|
+
const key = "__scriptReplacePersistRules";
|
|
452
|
+
const filtered = (Array.isArray(runtimeWindow[key]) ? runtimeWindow[key] : []).filter((entry) => entry && entry.id !== payload.id);
|
|
453
|
+
filtered.push(payload);
|
|
454
|
+
runtimeWindow[key] = filtered;
|
|
455
|
+
}, {
|
|
456
|
+
id: rule.id,
|
|
457
|
+
url: rule.url,
|
|
458
|
+
matchType: rule.matchType,
|
|
459
|
+
createdAt: rule.createdAt
|
|
460
|
+
});
|
|
461
|
+
const replacementPreview = createPreview(replacement, GRAPHQL_MAX_PREVIEW_CHARS);
|
|
462
|
+
return toResponse({
|
|
463
|
+
success: true,
|
|
464
|
+
message: "Script replacement rule registered and interception enabled",
|
|
465
|
+
rule: {
|
|
466
|
+
id: rule.id,
|
|
467
|
+
url: rule.url,
|
|
468
|
+
matchType: rule.matchType,
|
|
469
|
+
createdAt: rule.createdAt
|
|
470
|
+
},
|
|
471
|
+
replacement: {
|
|
472
|
+
length: replacement.length,
|
|
473
|
+
preview: replacementPreview.preview,
|
|
474
|
+
truncated: replacementPreview.truncated
|
|
475
|
+
},
|
|
476
|
+
activeRuleCount: this.scriptReplaceRules.length
|
|
477
|
+
});
|
|
478
|
+
} catch (error) {
|
|
479
|
+
return toError(error);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/server/domains/graphql/handlers/introspection.ts
|
|
485
|
+
var IntrospectionHandlers = class {
|
|
486
|
+
constructor(collector) {
|
|
487
|
+
this.collector = collector;
|
|
488
|
+
}
|
|
489
|
+
async handleGraphqlIntrospect(args) {
|
|
490
|
+
try {
|
|
491
|
+
const endpoint = argString(args, "endpoint")?.trim();
|
|
492
|
+
if (!endpoint) return toError("Missing required argument: endpoint");
|
|
493
|
+
const headers = normalizeHeaders(args.headers);
|
|
494
|
+
if (argBool(args, "useBrowser", true)) {
|
|
495
|
+
const page = await this.collector.getActivePage();
|
|
496
|
+
const endpointValidationError = await validateBrowserEndpoint(endpoint, typeof page.url === "function" ? page.url() : null);
|
|
497
|
+
if (endpointValidationError) return toError(endpointValidationError);
|
|
498
|
+
return await this.introspectViaBrowser(page, endpoint, headers);
|
|
499
|
+
}
|
|
500
|
+
const endpointValidationError = await validateExternalEndpoint(endpoint);
|
|
501
|
+
if (endpointValidationError) return toError(endpointValidationError);
|
|
502
|
+
return await this.introspectViaNode(endpoint, headers);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
return toError(error);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async introspectViaNode(endpoint, headers) {
|
|
508
|
+
const requestHeaders = {
|
|
509
|
+
"content-type": "application/json",
|
|
510
|
+
...headers
|
|
511
|
+
};
|
|
512
|
+
let response;
|
|
513
|
+
let responseText;
|
|
514
|
+
try {
|
|
515
|
+
const ac = new AbortController();
|
|
516
|
+
const t = setTimeout(() => ac.abort(), 1e4);
|
|
517
|
+
try {
|
|
518
|
+
response = await fetch(endpoint, {
|
|
519
|
+
method: "POST",
|
|
520
|
+
headers: requestHeaders,
|
|
521
|
+
body: JSON.stringify({
|
|
522
|
+
query: INTROSPECTION_QUERY,
|
|
523
|
+
operationName: "IntrospectionQuery"
|
|
524
|
+
}),
|
|
525
|
+
signal: ac.signal
|
|
526
|
+
});
|
|
527
|
+
responseText = await response.text();
|
|
528
|
+
} finally {
|
|
529
|
+
clearTimeout(t);
|
|
530
|
+
}
|
|
531
|
+
} catch (error) {
|
|
532
|
+
return toResponse({
|
|
533
|
+
success: false,
|
|
534
|
+
endpoint,
|
|
535
|
+
status: 0,
|
|
536
|
+
statusText: "FETCH_ERROR",
|
|
537
|
+
error: error instanceof Error ? error.message : String(error)
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
const responseHeaders = {};
|
|
541
|
+
response.headers.forEach((value, key) => {
|
|
542
|
+
responseHeaders[key] = value;
|
|
543
|
+
});
|
|
544
|
+
let json = null;
|
|
545
|
+
try {
|
|
546
|
+
json = JSON.parse(responseText);
|
|
547
|
+
} catch {}
|
|
548
|
+
responseText = "";
|
|
549
|
+
if (!response.ok && !json) return toResponse({
|
|
550
|
+
success: false,
|
|
551
|
+
endpoint,
|
|
552
|
+
status: response.status,
|
|
553
|
+
statusText: response.statusText,
|
|
554
|
+
error: "Introspection request failed"
|
|
555
|
+
});
|
|
556
|
+
const jsonRecord = json && typeof json === "object" ? json : null;
|
|
557
|
+
const schemaPayload = jsonRecord && "data" in jsonRecord ? jsonRecord.data : json;
|
|
558
|
+
const schemaPreviewPayload = json !== null && json !== void 0 && typeof schemaPayload !== "undefined" ? serializeForPreview(schemaPayload, GRAPHQL_MAX_SCHEMA_CHARS) : {
|
|
559
|
+
preview: "",
|
|
560
|
+
truncated: false,
|
|
561
|
+
totalLength: 0
|
|
562
|
+
};
|
|
563
|
+
const payload = {
|
|
564
|
+
success: response.ok,
|
|
565
|
+
endpoint,
|
|
566
|
+
status: response.status,
|
|
567
|
+
statusText: response.statusText,
|
|
568
|
+
schemaLength: schemaPreviewPayload.totalLength,
|
|
569
|
+
schemaPreview: schemaPreviewPayload.preview,
|
|
570
|
+
schemaTruncated: schemaPreviewPayload.truncated,
|
|
571
|
+
responseHeaders
|
|
572
|
+
};
|
|
573
|
+
if (!schemaPreviewPayload.truncated) payload.schema = schemaPayload;
|
|
574
|
+
if (jsonRecord && Array.isArray(jsonRecord.errors)) payload.errors = jsonRecord.errors;
|
|
575
|
+
return toResponse(payload);
|
|
576
|
+
}
|
|
577
|
+
async introspectViaBrowser(page, endpoint, headers) {
|
|
578
|
+
const browserResult = await evaluateWithTimeout(page, async (input) => {
|
|
579
|
+
const requestHeaders = {
|
|
580
|
+
"content-type": "application/json",
|
|
581
|
+
...input.headers
|
|
582
|
+
};
|
|
583
|
+
try {
|
|
584
|
+
const ac = new AbortController();
|
|
585
|
+
const t = setTimeout(() => ac.abort(), 1e4);
|
|
586
|
+
let responseText;
|
|
587
|
+
let response;
|
|
588
|
+
try {
|
|
589
|
+
response = await fetch(input.endpoint, {
|
|
590
|
+
method: "POST",
|
|
591
|
+
headers: requestHeaders,
|
|
592
|
+
body: JSON.stringify({
|
|
593
|
+
query: input.query,
|
|
594
|
+
operationName: "IntrospectionQuery"
|
|
595
|
+
}),
|
|
596
|
+
signal: ac.signal
|
|
597
|
+
});
|
|
598
|
+
responseText = await response.text();
|
|
599
|
+
} finally {
|
|
600
|
+
clearTimeout(t);
|
|
601
|
+
}
|
|
602
|
+
const responseHeaders = {};
|
|
603
|
+
response.headers.forEach((value, key) => {
|
|
604
|
+
responseHeaders[key] = value;
|
|
605
|
+
});
|
|
606
|
+
const totalLength = responseText.length;
|
|
607
|
+
let json = null;
|
|
608
|
+
try {
|
|
609
|
+
json = JSON.parse(responseText);
|
|
610
|
+
} catch {}
|
|
611
|
+
const preview = json === null ? responseText : "";
|
|
612
|
+
responseText = "";
|
|
613
|
+
return {
|
|
614
|
+
ok: response.ok,
|
|
615
|
+
status: response.status,
|
|
616
|
+
statusText: response.statusText,
|
|
617
|
+
responseHeaders,
|
|
618
|
+
totalLength,
|
|
619
|
+
preview,
|
|
620
|
+
truncated: false,
|
|
621
|
+
json
|
|
622
|
+
};
|
|
623
|
+
} catch (error) {
|
|
624
|
+
return {
|
|
625
|
+
ok: false,
|
|
626
|
+
status: 0,
|
|
627
|
+
statusText: "FETCH_ERROR",
|
|
628
|
+
responseHeaders: {},
|
|
629
|
+
totalLength: 0,
|
|
630
|
+
preview: "",
|
|
631
|
+
truncated: false,
|
|
632
|
+
json: null,
|
|
633
|
+
error: error instanceof Error ? error.message : String(error)
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
}, {
|
|
637
|
+
endpoint,
|
|
638
|
+
headers,
|
|
639
|
+
query: INTROSPECTION_QUERY,
|
|
640
|
+
maxSchemaChars: GRAPHQL_MAX_SCHEMA_CHARS
|
|
641
|
+
});
|
|
642
|
+
if (!browserResult.ok && !browserResult.json) return toResponse({
|
|
643
|
+
success: false,
|
|
644
|
+
endpoint,
|
|
645
|
+
status: browserResult.status,
|
|
646
|
+
statusText: browserResult.statusText,
|
|
647
|
+
error: browserResult.error ?? "Introspection request failed",
|
|
648
|
+
responsePreview: createPreview(browserResult.preview || "", GRAPHQL_MAX_PREVIEW_CHARS)
|
|
649
|
+
});
|
|
650
|
+
const jsonRecord = browserResult.json && typeof browserResult.json === "object" ? browserResult.json : null;
|
|
651
|
+
const schemaPayload = jsonRecord && "data" in jsonRecord ? jsonRecord.data : browserResult.json;
|
|
652
|
+
const schemaPreviewPayload = browserResult.json !== null && browserResult.json !== void 0 && typeof schemaPayload !== "undefined" ? serializeForPreview(schemaPayload, GRAPHQL_MAX_SCHEMA_CHARS) : {
|
|
653
|
+
preview: browserResult.preview ?? "",
|
|
654
|
+
truncated: browserResult.truncated ?? false,
|
|
655
|
+
totalLength: browserResult.totalLength ?? 0
|
|
656
|
+
};
|
|
657
|
+
const payload = {
|
|
658
|
+
success: browserResult.ok,
|
|
659
|
+
endpoint,
|
|
660
|
+
status: browserResult.status,
|
|
661
|
+
statusText: browserResult.statusText,
|
|
662
|
+
schemaLength: schemaPreviewPayload.totalLength,
|
|
663
|
+
schemaPreview: schemaPreviewPayload.preview,
|
|
664
|
+
schemaTruncated: schemaPreviewPayload.truncated,
|
|
665
|
+
responseHeaders: browserResult.responseHeaders ?? {}
|
|
666
|
+
};
|
|
667
|
+
if (!schemaPreviewPayload.truncated) payload.schema = schemaPayload;
|
|
668
|
+
if (jsonRecord && Array.isArray(jsonRecord.errors)) payload.errors = jsonRecord.errors;
|
|
669
|
+
if (browserResult.error) payload.error = browserResult.error;
|
|
670
|
+
return toResponse(payload);
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
//#endregion
|
|
674
|
+
//#region src/server/domains/graphql/handlers/extract.ts
|
|
675
|
+
function isRecord(value) {
|
|
676
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
677
|
+
}
|
|
678
|
+
function appendGraphQLPayload(extracted, payload, metadata) {
|
|
679
|
+
if (!payload) return;
|
|
680
|
+
const queryRaw = payload.query;
|
|
681
|
+
if (typeof queryRaw !== "string" || queryRaw.trim().length === 0) return;
|
|
682
|
+
const operationNameRaw = payload.operationName;
|
|
683
|
+
const operationName = typeof operationNameRaw === "string" && operationNameRaw.trim().length > 0 ? operationNameRaw : queryRaw.match(/^\s*(query|mutation|subscription)\s+([A-Za-z0-9_]+)/)?.[2] ?? null;
|
|
684
|
+
extracted.push({
|
|
685
|
+
source: metadata.source,
|
|
686
|
+
url: metadata.url,
|
|
687
|
+
method: metadata.method,
|
|
688
|
+
operationName,
|
|
689
|
+
query: queryRaw,
|
|
690
|
+
variables: payload.variables ?? null,
|
|
691
|
+
timestamp: metadata.timestamp,
|
|
692
|
+
contentType: metadata.contentType
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
function parseBodyStringToPayload(rawBody) {
|
|
696
|
+
const trimmed = rawBody.trim();
|
|
697
|
+
if (!trimmed) return null;
|
|
698
|
+
try {
|
|
699
|
+
const parsed = JSON.parse(trimmed);
|
|
700
|
+
if (isRecord(parsed)) return parsed;
|
|
701
|
+
} catch {}
|
|
702
|
+
if (trimmed.includes("query=")) try {
|
|
703
|
+
const params = new URLSearchParams(trimmed);
|
|
704
|
+
const query = params.get("query");
|
|
705
|
+
if (query) {
|
|
706
|
+
const operationName = params.get("operationName");
|
|
707
|
+
const variablesRaw = params.get("variables");
|
|
708
|
+
let variables = null;
|
|
709
|
+
if (variablesRaw) try {
|
|
710
|
+
variables = JSON.parse(variablesRaw);
|
|
711
|
+
} catch {
|
|
712
|
+
variables = variablesRaw;
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
query,
|
|
716
|
+
operationName,
|
|
717
|
+
variables
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
} catch {}
|
|
721
|
+
if (trimmed.startsWith("query ") || trimmed.startsWith("mutation ") || trimmed.startsWith("subscription ")) return { query: trimmed };
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
function getRequestHeaders(record) {
|
|
725
|
+
if (isRecord(record.headers)) return record.headers;
|
|
726
|
+
if (isRecord(record.requestHeaders)) return record.requestHeaders;
|
|
727
|
+
return {};
|
|
728
|
+
}
|
|
729
|
+
function getContentType(record) {
|
|
730
|
+
const headers = getRequestHeaders(record);
|
|
731
|
+
for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === "content-type") return typeof value === "string" ? value.toLowerCase() : String(value).toLowerCase();
|
|
732
|
+
return "";
|
|
733
|
+
}
|
|
734
|
+
function getBodyCandidates(record) {
|
|
735
|
+
const candidates = [record.body, record.postData];
|
|
736
|
+
if (isRecord(record.options)) candidates.push(record.options.body);
|
|
737
|
+
if (isRecord(record.request)) candidates.push(record.request.postData);
|
|
738
|
+
return candidates;
|
|
739
|
+
}
|
|
740
|
+
function extractQueriesFromRecords(records, source) {
|
|
741
|
+
const state = {
|
|
742
|
+
scannedRecords: 0,
|
|
743
|
+
extracted: []
|
|
744
|
+
};
|
|
745
|
+
if (!Array.isArray(records)) return state;
|
|
746
|
+
for (const item of records) {
|
|
747
|
+
if (!isRecord(item)) continue;
|
|
748
|
+
state.scannedRecords += 1;
|
|
749
|
+
const url = typeof item.url === "string" ? item.url : "";
|
|
750
|
+
const method = typeof item.method === "string" ? item.method : "POST";
|
|
751
|
+
const timestamp = typeof item.timestamp === "number" ? item.timestamp : null;
|
|
752
|
+
const contentType = getContentType(item);
|
|
753
|
+
for (const bodyCandidate of getBodyCandidates(item)) {
|
|
754
|
+
let payload = null;
|
|
755
|
+
if (isRecord(bodyCandidate)) payload = bodyCandidate;
|
|
756
|
+
else if (typeof bodyCandidate === "string") payload = parseBodyStringToPayload(bodyCandidate);
|
|
757
|
+
appendGraphQLPayload(state.extracted, payload, {
|
|
758
|
+
source,
|
|
759
|
+
url,
|
|
760
|
+
method,
|
|
761
|
+
timestamp,
|
|
762
|
+
contentType
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
if (contentType.includes("application/graphql") && typeof item.body === "string") appendGraphQLPayload(state.extracted, {
|
|
766
|
+
query: item.body,
|
|
767
|
+
variables: null,
|
|
768
|
+
operationName: null
|
|
769
|
+
}, {
|
|
770
|
+
source,
|
|
771
|
+
url,
|
|
772
|
+
method,
|
|
773
|
+
timestamp,
|
|
774
|
+
contentType
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
return state;
|
|
778
|
+
}
|
|
779
|
+
function dedupeExtractedQueries(items) {
|
|
780
|
+
const sorted = items.toSorted((left, right) => (right.timestamp ?? 0) - (left.timestamp ?? 0));
|
|
781
|
+
const deduped = [];
|
|
782
|
+
const seen = /* @__PURE__ */ new Set();
|
|
783
|
+
for (const item of sorted) {
|
|
784
|
+
const key = `${item.url}|${item.operationName ?? ""}|${item.query}|${JSON.stringify(item.variables)}`;
|
|
785
|
+
if (seen.has(key)) continue;
|
|
786
|
+
seen.add(key);
|
|
787
|
+
deduped.push(item);
|
|
788
|
+
}
|
|
789
|
+
return deduped;
|
|
790
|
+
}
|
|
791
|
+
var ExtractHandlers = class {
|
|
792
|
+
deps;
|
|
793
|
+
constructor(deps) {
|
|
794
|
+
this.deps = "collector" in deps ? deps : { collector: deps };
|
|
795
|
+
}
|
|
796
|
+
async handleGraphqlExtractQueries(args) {
|
|
797
|
+
try {
|
|
798
|
+
const limit = parseClampedNumber(args, "limit", 50, 1, 200);
|
|
799
|
+
const pageExtraction = await evaluateWithTimeout(await this.deps.collector.getActivePage(), (maxItems) => {
|
|
800
|
+
const globalScope = window;
|
|
801
|
+
const extracted = [];
|
|
802
|
+
let scannedRecords = 0;
|
|
803
|
+
const pushIfGraphQL = (payload, metadata) => {
|
|
804
|
+
if (!payload) return;
|
|
805
|
+
const queryRaw = payload.query;
|
|
806
|
+
if (typeof queryRaw !== "string" || queryRaw.trim().length === 0) return;
|
|
807
|
+
const operationNameRaw = payload.operationName;
|
|
808
|
+
const operationName = typeof operationNameRaw === "string" && operationNameRaw.trim().length > 0 ? operationNameRaw : queryRaw.match(/^\s*(query|mutation|subscription)\s+([A-Za-z0-9_]+)/)?.[2] ?? null;
|
|
809
|
+
extracted.push({
|
|
810
|
+
source: metadata.source,
|
|
811
|
+
url: metadata.url,
|
|
812
|
+
method: metadata.method,
|
|
813
|
+
operationName,
|
|
814
|
+
query: queryRaw,
|
|
815
|
+
variables: payload.variables ?? null,
|
|
816
|
+
timestamp: metadata.timestamp,
|
|
817
|
+
contentType: metadata.contentType
|
|
818
|
+
});
|
|
819
|
+
};
|
|
820
|
+
const parseStringBody = (trimmed) => {
|
|
821
|
+
if (!trimmed) return null;
|
|
822
|
+
try {
|
|
823
|
+
const parsed = JSON.parse(trimmed);
|
|
824
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
825
|
+
} catch {}
|
|
826
|
+
if (trimmed.includes("query=")) try {
|
|
827
|
+
const params = new URLSearchParams(trimmed);
|
|
828
|
+
const query = params.get("query");
|
|
829
|
+
if (query) {
|
|
830
|
+
const operationName = params.get("operationName");
|
|
831
|
+
const variablesRaw = params.get("variables");
|
|
832
|
+
let variables = null;
|
|
833
|
+
if (variablesRaw) try {
|
|
834
|
+
variables = JSON.parse(variablesRaw);
|
|
835
|
+
} catch {
|
|
836
|
+
variables = variablesRaw;
|
|
837
|
+
}
|
|
838
|
+
return {
|
|
839
|
+
query,
|
|
840
|
+
operationName,
|
|
841
|
+
variables
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
} catch {}
|
|
845
|
+
if (trimmed.startsWith("query ") || trimmed.startsWith("mutation ") || trimmed.startsWith("subscription ")) return { query: trimmed };
|
|
846
|
+
return null;
|
|
847
|
+
};
|
|
848
|
+
const processRequestRecord = (record, source) => {
|
|
849
|
+
scannedRecords += 1;
|
|
850
|
+
const url = typeof record.url === "string" ? record.url : "";
|
|
851
|
+
const method = typeof record.method === "string" ? record.method : "POST";
|
|
852
|
+
const timestamp = typeof record.timestamp === "number" ? record.timestamp : null;
|
|
853
|
+
const headers = (record.headers && typeof record.headers === "object" && !Array.isArray(record.headers) ? record.headers : null) ?? (record.requestHeaders && typeof record.requestHeaders === "object" && !Array.isArray(record.requestHeaders) ? record.requestHeaders : null) ?? {};
|
|
854
|
+
let contentType = "";
|
|
855
|
+
for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === "content-type") {
|
|
856
|
+
contentType = typeof value === "string" ? value : String(value);
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
contentType = contentType.toLowerCase();
|
|
860
|
+
const bodyCandidates = [record.body, record.postData];
|
|
861
|
+
if (record.options && typeof record.options === "object" && !Array.isArray(record.options)) bodyCandidates.push(record.options.body);
|
|
862
|
+
for (const bodyCandidate of bodyCandidates) {
|
|
863
|
+
let payload = null;
|
|
864
|
+
if (bodyCandidate && typeof bodyCandidate === "object" && !Array.isArray(bodyCandidate)) payload = bodyCandidate;
|
|
865
|
+
else if (typeof bodyCandidate === "string") payload = parseStringBody(bodyCandidate);
|
|
866
|
+
pushIfGraphQL(payload, {
|
|
867
|
+
source,
|
|
868
|
+
url,
|
|
869
|
+
method,
|
|
870
|
+
timestamp,
|
|
871
|
+
contentType
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
if (contentType.includes("application/graphql") && typeof record.body === "string") pushIfGraphQL({
|
|
875
|
+
query: record.body,
|
|
876
|
+
variables: null,
|
|
877
|
+
operationName: null
|
|
878
|
+
}, {
|
|
879
|
+
source,
|
|
880
|
+
url,
|
|
881
|
+
method,
|
|
882
|
+
timestamp,
|
|
883
|
+
contentType
|
|
884
|
+
});
|
|
885
|
+
};
|
|
886
|
+
const processArray = (value, source) => {
|
|
887
|
+
if (!Array.isArray(value)) return;
|
|
888
|
+
for (const item of value) if (item && typeof item === "object") processRequestRecord(item, source);
|
|
889
|
+
};
|
|
890
|
+
const fetchRequests = Array.isArray(globalScope.__fetchRequests) ? globalScope.__fetchRequests : typeof globalScope.__getFetchRequests === "function" ? globalScope.__getFetchRequests() : void 0;
|
|
891
|
+
const xhrRequests = Array.isArray(globalScope.__xhrRequests) ? globalScope.__xhrRequests : typeof globalScope.__getXHRRequests === "function" ? globalScope.__getXHRRequests() : void 0;
|
|
892
|
+
processArray(fetchRequests, "window.__fetchRequests");
|
|
893
|
+
processArray(xhrRequests, "window.__xhrRequests");
|
|
894
|
+
processArray(globalScope.__networkRequests, "window.__networkRequests");
|
|
895
|
+
const aiHooks = globalScope.__aiHooks;
|
|
896
|
+
if (aiHooks && typeof aiHooks === "object") for (const [hookName, hookRecords] of Object.entries(aiHooks)) {
|
|
897
|
+
if (!Array.isArray(hookRecords)) continue;
|
|
898
|
+
for (const entry of hookRecords) if (entry && typeof entry === "object") processRequestRecord(entry, `window.__aiHooks.${hookName}`);
|
|
899
|
+
}
|
|
900
|
+
extracted.sort((left, right) => (right.timestamp ?? 0) - (left.timestamp ?? 0));
|
|
901
|
+
const deduped = [];
|
|
902
|
+
const seen = /* @__PURE__ */ new Set();
|
|
903
|
+
for (const item of extracted) {
|
|
904
|
+
const key = `${item.url}|${item.operationName ?? ""}|${item.query}|${JSON.stringify(item.variables)}`;
|
|
905
|
+
if (!seen.has(key)) {
|
|
906
|
+
seen.add(key);
|
|
907
|
+
deduped.push(item);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
scannedRecords,
|
|
912
|
+
totalExtracted: deduped.length,
|
|
913
|
+
extracted: deduped.slice(0, maxItems)
|
|
914
|
+
};
|
|
915
|
+
}, limit);
|
|
916
|
+
let scannedRecords = pageExtraction.scannedRecords;
|
|
917
|
+
const combinedExtracted = [...pageExtraction.extracted];
|
|
918
|
+
if (this.deps.consoleMonitor) {
|
|
919
|
+
const fetchRequestsPromise = typeof this.deps.consoleMonitor.getFetchRequests === "function" ? this.deps.consoleMonitor.getFetchRequests().catch(() => []) : Promise.resolve([]);
|
|
920
|
+
const xhrRequestsPromise = typeof this.deps.consoleMonitor.getXHRRequests === "function" ? this.deps.consoleMonitor.getXHRRequests().catch(() => []) : Promise.resolve([]);
|
|
921
|
+
const [fetchRequests, xhrRequests] = await Promise.all([fetchRequestsPromise, xhrRequestsPromise]);
|
|
922
|
+
let networkRequests = [];
|
|
923
|
+
try {
|
|
924
|
+
networkRequests = typeof this.deps.consoleMonitor.getNetworkRequests === "function" ? this.deps.consoleMonitor.getNetworkRequests({ limit: 500 }) : [];
|
|
925
|
+
} catch {
|
|
926
|
+
networkRequests = [];
|
|
927
|
+
}
|
|
928
|
+
const fallbackExtractions = [
|
|
929
|
+
extractQueriesFromRecords(fetchRequests, "consoleMonitor.fetchRequests"),
|
|
930
|
+
extractQueriesFromRecords(xhrRequests, "consoleMonitor.xhrRequests"),
|
|
931
|
+
extractQueriesFromRecords(networkRequests, "consoleMonitor.networkRequests")
|
|
932
|
+
];
|
|
933
|
+
for (const fallback of fallbackExtractions) {
|
|
934
|
+
scannedRecords += fallback.scannedRecords;
|
|
935
|
+
combinedExtracted.push(...fallback.extracted);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
const dedupedExtracted = dedupeExtractedQueries(combinedExtracted);
|
|
939
|
+
const totalExtracted = Math.max(pageExtraction.totalExtracted, dedupedExtracted.length);
|
|
940
|
+
const queries = dedupedExtracted.slice(0, limit).map((item, index) => {
|
|
941
|
+
const queryPreview = createPreview(item.query, GRAPHQL_MAX_QUERY_CHARS);
|
|
942
|
+
const variablesPreview = serializeForPreview(item.variables, GRAPHQL_MAX_PREVIEW_CHARS);
|
|
943
|
+
const normalized = {
|
|
944
|
+
index,
|
|
945
|
+
source: item.source,
|
|
946
|
+
url: item.url,
|
|
947
|
+
method: item.method,
|
|
948
|
+
operationName: item.operationName,
|
|
949
|
+
contentType: item.contentType,
|
|
950
|
+
timestamp: item.timestamp,
|
|
951
|
+
queryLength: item.query.length,
|
|
952
|
+
queryPreview: queryPreview.preview,
|
|
953
|
+
queryTruncated: queryPreview.truncated
|
|
954
|
+
};
|
|
955
|
+
if (!queryPreview.truncated) normalized.query = item.query;
|
|
956
|
+
if (!variablesPreview.truncated) normalized.variables = item.variables;
|
|
957
|
+
else {
|
|
958
|
+
normalized.variablesPreview = variablesPreview.preview;
|
|
959
|
+
normalized.variablesTruncated = true;
|
|
960
|
+
}
|
|
961
|
+
return normalized;
|
|
962
|
+
});
|
|
963
|
+
return toResponse({
|
|
964
|
+
success: true,
|
|
965
|
+
limit,
|
|
966
|
+
stats: {
|
|
967
|
+
scannedRecords,
|
|
968
|
+
totalExtracted,
|
|
969
|
+
returned: queries.length
|
|
970
|
+
},
|
|
971
|
+
queries
|
|
972
|
+
});
|
|
973
|
+
} catch (error) {
|
|
974
|
+
return toError(error);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region src/server/domains/graphql/handlers/replay.ts
|
|
980
|
+
var ReplayHandlers = class {
|
|
981
|
+
constructor(collector) {
|
|
982
|
+
this.collector = collector;
|
|
983
|
+
}
|
|
984
|
+
async handleGraphqlReplay(args) {
|
|
985
|
+
try {
|
|
986
|
+
const endpoint = argString(args, "endpoint")?.trim();
|
|
987
|
+
const query = argString(args, "query");
|
|
988
|
+
if (!endpoint) return toError("Missing required argument: endpoint");
|
|
989
|
+
if (typeof query !== "string" || query.trim().length === 0) return toError("Missing required argument: query");
|
|
990
|
+
const variables = argObject(args, "variables") ?? {};
|
|
991
|
+
const operationNameRaw = argString(args, "operationName");
|
|
992
|
+
const operationName = operationNameRaw && operationNameRaw.trim().length > 0 ? operationNameRaw.trim() : null;
|
|
993
|
+
const headers = normalizeHeaders(args.headers);
|
|
994
|
+
if (argBool(args, "useBrowser", true)) {
|
|
995
|
+
const page = await this.collector.getActivePage();
|
|
996
|
+
const endpointValidationError = await validateBrowserEndpoint(endpoint, typeof page.url === "function" ? page.url() : null);
|
|
997
|
+
if (endpointValidationError) return toError(endpointValidationError);
|
|
998
|
+
return await this.replayViaBrowser(page, endpoint, query, variables, operationName, headers);
|
|
999
|
+
}
|
|
1000
|
+
const endpointValidationError = await validateExternalEndpoint(endpoint);
|
|
1001
|
+
if (endpointValidationError) return toError(endpointValidationError);
|
|
1002
|
+
return await this.replayViaNode(endpoint, query, variables, operationName, headers);
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
return toError(error);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async replayViaNode(endpoint, query, variables, operationName, headers) {
|
|
1008
|
+
const requestHeaders = {
|
|
1009
|
+
"content-type": "application/json",
|
|
1010
|
+
...headers
|
|
1011
|
+
};
|
|
1012
|
+
let response;
|
|
1013
|
+
let responseText;
|
|
1014
|
+
try {
|
|
1015
|
+
const ac = new AbortController();
|
|
1016
|
+
const t = setTimeout(() => ac.abort(), 1e4);
|
|
1017
|
+
try {
|
|
1018
|
+
response = await fetch(endpoint, {
|
|
1019
|
+
method: "POST",
|
|
1020
|
+
headers: requestHeaders,
|
|
1021
|
+
body: JSON.stringify({
|
|
1022
|
+
query,
|
|
1023
|
+
variables,
|
|
1024
|
+
operationName
|
|
1025
|
+
}),
|
|
1026
|
+
signal: ac.signal
|
|
1027
|
+
});
|
|
1028
|
+
responseText = await response.text();
|
|
1029
|
+
} finally {
|
|
1030
|
+
clearTimeout(t);
|
|
1031
|
+
}
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
return toResponse({
|
|
1034
|
+
success: false,
|
|
1035
|
+
endpoint,
|
|
1036
|
+
status: 0,
|
|
1037
|
+
statusText: "FETCH_ERROR",
|
|
1038
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1039
|
+
operationName
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
const responseHeaders = {};
|
|
1043
|
+
response.headers.forEach((value, key) => {
|
|
1044
|
+
responseHeaders[key] = value;
|
|
1045
|
+
});
|
|
1046
|
+
let responseJson = null;
|
|
1047
|
+
try {
|
|
1048
|
+
responseJson = JSON.parse(responseText);
|
|
1049
|
+
} catch {
|
|
1050
|
+
responseJson = null;
|
|
1051
|
+
}
|
|
1052
|
+
responseText = "";
|
|
1053
|
+
return toResponse(buildReplayPayloadFromJson(responseJson, endpoint, operationName, response.ok, response.status, response.statusText, responseHeaders));
|
|
1054
|
+
}
|
|
1055
|
+
async replayViaBrowser(page, endpoint, query, variables, operationName, headers) {
|
|
1056
|
+
const browserResult = await evaluateWithTimeout(page, async (input) => {
|
|
1057
|
+
const requestHeaders = {
|
|
1058
|
+
"content-type": "application/json",
|
|
1059
|
+
...input.headers
|
|
1060
|
+
};
|
|
1061
|
+
try {
|
|
1062
|
+
const ac = new AbortController();
|
|
1063
|
+
const t = setTimeout(() => ac.abort(), 1e4);
|
|
1064
|
+
let responseText;
|
|
1065
|
+
let response;
|
|
1066
|
+
try {
|
|
1067
|
+
response = await fetch(input.endpoint, {
|
|
1068
|
+
method: "POST",
|
|
1069
|
+
headers: requestHeaders,
|
|
1070
|
+
body: JSON.stringify({
|
|
1071
|
+
query: input.query,
|
|
1072
|
+
variables: input.variables,
|
|
1073
|
+
operationName: input.operationName
|
|
1074
|
+
}),
|
|
1075
|
+
signal: ac.signal
|
|
1076
|
+
});
|
|
1077
|
+
responseText = await response.text();
|
|
1078
|
+
} finally {
|
|
1079
|
+
clearTimeout(t);
|
|
1080
|
+
}
|
|
1081
|
+
let responseJson = null;
|
|
1082
|
+
try {
|
|
1083
|
+
responseJson = JSON.parse(responseText);
|
|
1084
|
+
} catch {
|
|
1085
|
+
responseJson = null;
|
|
1086
|
+
}
|
|
1087
|
+
const rawText = responseJson === null ? responseText : "";
|
|
1088
|
+
responseText = "";
|
|
1089
|
+
const responseHeaders = {};
|
|
1090
|
+
response.headers.forEach((value, key) => {
|
|
1091
|
+
responseHeaders[key] = value;
|
|
1092
|
+
});
|
|
1093
|
+
return {
|
|
1094
|
+
ok: response.ok,
|
|
1095
|
+
status: response.status,
|
|
1096
|
+
statusText: response.statusText,
|
|
1097
|
+
responseText: rawText,
|
|
1098
|
+
responseJson,
|
|
1099
|
+
responseHeaders
|
|
1100
|
+
};
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
return {
|
|
1103
|
+
ok: false,
|
|
1104
|
+
status: 0,
|
|
1105
|
+
statusText: "FETCH_ERROR",
|
|
1106
|
+
responseText: "",
|
|
1107
|
+
responseJson: null,
|
|
1108
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
}, {
|
|
1112
|
+
endpoint,
|
|
1113
|
+
query,
|
|
1114
|
+
variables,
|
|
1115
|
+
operationName,
|
|
1116
|
+
headers
|
|
1117
|
+
});
|
|
1118
|
+
const payload = {
|
|
1119
|
+
success: browserResult.ok,
|
|
1120
|
+
endpoint,
|
|
1121
|
+
status: browserResult.status,
|
|
1122
|
+
statusText: browserResult.statusText,
|
|
1123
|
+
operationName,
|
|
1124
|
+
responseHeaders: browserResult.responseHeaders ?? {}
|
|
1125
|
+
};
|
|
1126
|
+
if (browserResult.responseJson !== null) {
|
|
1127
|
+
const responsePreview = serializeForPreview(browserResult.responseJson, GRAPHQL_MAX_SCHEMA_CHARS);
|
|
1128
|
+
payload.responseLength = responsePreview.totalLength;
|
|
1129
|
+
payload.responsePreview = responsePreview.preview;
|
|
1130
|
+
payload.responseTruncated = responsePreview.truncated;
|
|
1131
|
+
if (!responsePreview.truncated) payload.response = browserResult.responseJson;
|
|
1132
|
+
} else if (browserResult.responseText) {
|
|
1133
|
+
const text = browserResult.responseText;
|
|
1134
|
+
payload.responseFormat = "text";
|
|
1135
|
+
payload.responseLength = text.length;
|
|
1136
|
+
payload.responsePreview = text.length > GRAPHQL_MAX_SCHEMA_CHARS ? text.slice(0, GRAPHQL_MAX_SCHEMA_CHARS) : text;
|
|
1137
|
+
payload.responseTruncated = text.length > GRAPHQL_MAX_SCHEMA_CHARS;
|
|
1138
|
+
}
|
|
1139
|
+
if (browserResult.error) payload.error = browserResult.error;
|
|
1140
|
+
return toResponse(payload);
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
function buildReplayPayloadFromJson(responseJson, endpoint, operationName, ok, status, statusText, responseHeaders) {
|
|
1144
|
+
const payload = {
|
|
1145
|
+
success: ok,
|
|
1146
|
+
endpoint,
|
|
1147
|
+
status,
|
|
1148
|
+
statusText,
|
|
1149
|
+
operationName,
|
|
1150
|
+
responseHeaders
|
|
1151
|
+
};
|
|
1152
|
+
if (responseJson !== null) {
|
|
1153
|
+
const responsePreview = serializeForPreview(responseJson, GRAPHQL_MAX_SCHEMA_CHARS);
|
|
1154
|
+
payload.responseLength = responsePreview.totalLength;
|
|
1155
|
+
payload.responsePreview = responsePreview.preview;
|
|
1156
|
+
payload.responseTruncated = responsePreview.truncated;
|
|
1157
|
+
if (!responsePreview.truncated) payload.response = responseJson;
|
|
1158
|
+
}
|
|
1159
|
+
return payload;
|
|
1160
|
+
}
|
|
1161
|
+
//#endregion
|
|
1162
|
+
//#region src/server/domains/graphql/handlers.impl.ts
|
|
1163
|
+
function normalizeDependencies(deps) {
|
|
1164
|
+
return "collector" in deps ? deps : { collector: deps };
|
|
1165
|
+
}
|
|
1166
|
+
var GraphQLToolHandlers = class {
|
|
1167
|
+
callGraph;
|
|
1168
|
+
scriptReplace;
|
|
1169
|
+
introspection;
|
|
1170
|
+
extract;
|
|
1171
|
+
replay;
|
|
1172
|
+
constructor(deps) {
|
|
1173
|
+
const normalized = normalizeDependencies(deps);
|
|
1174
|
+
this.callGraph = new CallGraphHandlers(normalized.collector);
|
|
1175
|
+
this.scriptReplace = new ScriptReplaceHandlers(normalized.collector);
|
|
1176
|
+
this.introspection = new IntrospectionHandlers(normalized.collector);
|
|
1177
|
+
this.extract = new ExtractHandlers(normalized);
|
|
1178
|
+
this.replay = new ReplayHandlers(normalized.collector);
|
|
1179
|
+
}
|
|
1180
|
+
async handleCallGraphAnalyze(args) {
|
|
1181
|
+
return this.callGraph.handleCallGraphAnalyze(args);
|
|
1182
|
+
}
|
|
1183
|
+
async handleScriptReplacePersist(args) {
|
|
1184
|
+
return this.scriptReplace.handleScriptReplacePersist(args);
|
|
1185
|
+
}
|
|
1186
|
+
async handleGraphqlIntrospect(args) {
|
|
1187
|
+
return this.introspection.handleGraphqlIntrospect(args);
|
|
1188
|
+
}
|
|
1189
|
+
async handleGraphqlExtractQueries(args) {
|
|
1190
|
+
return this.extract.handleGraphqlExtractQueries(args);
|
|
1191
|
+
}
|
|
1192
|
+
async handleGraphqlReplay(args) {
|
|
1193
|
+
return this.replay.handleGraphqlReplay(args);
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
//#endregion
|
|
1197
|
+
export { GraphQLToolHandlers };
|