@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.
Files changed (162) hide show
  1. package/README.md +36 -5
  2. package/README.zh.md +36 -5
  3. package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-CqGDXmfc.mjs} +160 -54
  4. package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-BdjRfNx7.mjs} +5 -5
  5. package/dist/ConsoleMonitor-DykL3IAw.mjs +2269 -0
  6. package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-ETyy0xyo.mjs} +1 -1
  7. package/dist/DetailedDataManager-HT49OrvF.mjs +217 -0
  8. package/dist/EventBus-DFKvADm3.mjs +141 -0
  9. package/dist/EvidenceGraphBridge-318Oi0Lf.mjs +153 -0
  10. package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-BDMsY2Dz.mjs} +27 -13
  11. package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-BN4UQWnX.mjs} +1 -1
  12. package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-Cc2AFq1Y.mjs} +3 -3
  13. package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-DruMgsgj.mjs} +21 -21
  14. package/dist/HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs +566 -0
  15. package/dist/InstrumentationSession-DLH0vd-z.mjs +244 -0
  16. package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CMtviNW_.mjs} +3 -3
  17. package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-ITgb_NMi.mjs} +81 -78
  18. package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-CiL7Z3ey.mjs} +50 -21
  19. package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs} +20 -56
  20. package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-BBlAixF5.mjs} +1 -1
  21. package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-DMQ44gen.mjs} +16 -16
  22. package/dist/PageController-BPJNqqBN.mjs +431 -0
  23. package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-K7wN8Z-w.mjs} +10 -7
  24. package/dist/PrerequisiteError-TuyZIs6n.mjs +20 -0
  25. package/dist/ProcessRegistry-zGg12QbE.mjs +74 -0
  26. package/dist/ResponseBuilder-CJXWmWNw.mjs +143 -0
  27. package/dist/ReverseEvidenceGraph-C02-gXOh.mjs +269 -0
  28. package/dist/ScriptManager-ZuWD-0Jg.mjs +3003 -0
  29. package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-D-z0umeT.mjs} +2 -2
  30. package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-Cav5AVSL.mjs} +9 -6
  31. package/dist/ToolCatalog-5OJdMiF0.mjs +582 -0
  32. package/dist/ToolError-jh9whhMd.mjs +15 -0
  33. package/dist/ToolProbe-DbCFGyrg.mjs +45 -0
  34. package/dist/ToolRegistry-B9krbTtI.mjs +180 -0
  35. package/dist/ToolRouter.policy-BGDAGyeH.mjs +344 -0
  36. package/dist/TraceRecorder-B41Z5XBj.mjs +1286 -0
  37. package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-C2kjj0ze.mjs} +19 -13
  38. package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-CKrGOTpo.mjs} +3 -3
  39. package/dist/WorkflowEngine-DJ6M4opp.mjs +569 -0
  40. package/dist/analysis-BHeJW2Nb.mjs +1234 -0
  41. package/dist/antidebug-BRKeyt27.mjs +1081 -0
  42. package/dist/artifactRetention-CPXkUJXp.mjs +598 -0
  43. package/dist/artifacts-DkfosXH3.mjs +59 -0
  44. package/dist/authorization-schema-DRqyJMSk.mjs +31 -0
  45. package/dist/betterSqlite3-DLSBZodi.mjs +74 -0
  46. package/dist/binary-instrument--V3MAhJ4.mjs +971 -0
  47. package/dist/bind-helpers-ClV34xdn.mjs +42 -0
  48. package/dist/boringssl-inspector-Bo_LOLaS.mjs +180 -0
  49. package/dist/browser-Dx3_S2cG.mjs +4369 -0
  50. package/dist/capabilities-CcHlvWgK.mjs +33 -0
  51. package/dist/concurrency-Drev_Vz9.mjs +41 -0
  52. package/dist/{constants-CCvsN80K.mjs → constants-CDZLOoVv.mjs} +105 -48
  53. package/dist/coordination-DgItD9DL.mjs +259 -0
  54. package/dist/debugger-RS3RSAqs.mjs +1288 -0
  55. package/dist/definitions-BEoYofW5.mjs +47 -0
  56. package/dist/definitions-BRaefg3u.mjs +365 -0
  57. package/dist/definitions-BbkvZkiv.mjs +96 -0
  58. package/dist/definitions-BtWSHJ3o.mjs +17 -0
  59. package/dist/definitions-C1gCHO0i.mjs +43 -0
  60. package/dist/definitions-CDOg_b-l.mjs +138 -0
  61. package/dist/definitions-CVPD9hzZ.mjs +54 -0
  62. package/dist/definitions-Cea8Lgl7.mjs +94 -0
  63. package/dist/definitions-DAgIyjxM.mjs +10 -0
  64. package/dist/definitions-DJA27nsL.mjs +66 -0
  65. package/dist/definitions-DKPFU3LW.mjs +25 -0
  66. package/dist/definitions-DPRpZQ96.mjs +47 -0
  67. package/dist/definitions-DUE5gmdn.mjs +18 -0
  68. package/dist/definitions-DYVjOtxa.mjs +26 -0
  69. package/dist/definitions-DcYLVLCo.mjs +37 -0
  70. package/dist/definitions-Pp5LI2H4.mjs +27 -0
  71. package/dist/definitions-j9KdHVNR.mjs +14 -0
  72. package/dist/definitions-uzkjBwa7.mjs +258 -0
  73. package/dist/definitions-va-AnLuQ.mjs +28 -0
  74. package/dist/encoding-DJeqHmpd.mjs +1079 -0
  75. package/dist/evidence-graph-bridge-DcYizFk2.mjs +136 -0
  76. package/dist/{factory-CibqTNC8.mjs → factory-C90tBff6.mjs} +41 -56
  77. package/dist/flat-target-session-Dgax2Cy3.mjs +29 -0
  78. package/dist/graphql-CoHrhweh.mjs +1197 -0
  79. package/dist/handlers-4jmR0nMs.mjs +898 -0
  80. package/dist/handlers-BAHPxcch.mjs +789 -0
  81. package/dist/handlers-BOs9b907.mjs +2600 -0
  82. package/dist/handlers-BWXEy6ef.mjs +917 -0
  83. package/dist/handlers-Bndn6QvE.mjs +111 -0
  84. package/dist/handlers-BqC4bD4s.mjs +681 -0
  85. package/dist/handlers-BtYq60bM2.mjs +276 -0
  86. package/dist/handlers-BzgcB4iv.mjs +799 -0
  87. package/dist/handlers-CRyRWj2b.mjs +859 -0
  88. package/dist/handlers-CVv2H1uq.mjs +592 -0
  89. package/dist/handlers-Dl5a7JS4.mjs +572 -0
  90. package/dist/handlers-Dx2d7jt7.mjs +2537 -0
  91. package/dist/handlers-Dz9PYsCa.mjs +2805 -0
  92. package/dist/handlers-HujRKC3b.mjs +661 -0
  93. package/dist/handlers.impl-XWXkQfyi.mjs +807 -0
  94. package/dist/hooks-B1B8NRHL.mjs +898 -0
  95. package/dist/index.mjs +491 -259
  96. package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
  97. package/dist/maintenance-PRMkLVRW.mjs +835 -0
  98. package/dist/manifest-67Bok-Si.mjs +58 -0
  99. package/dist/manifest-6lNTMZAB2.mjs +87 -0
  100. package/dist/manifest-B2duEHiH.mjs +90 -0
  101. package/dist/manifest-B6EY9Vm8.mjs +57 -0
  102. package/dist/manifest-B6nKSbyY.mjs +95 -0
  103. package/dist/manifest-BL8AQNPF.mjs +106 -0
  104. package/dist/manifest-BSZvJJmV.mjs +47 -0
  105. package/dist/manifest-BU7qzUyX.mjs +418 -0
  106. package/dist/manifest-Bl62e8WK.mjs +49 -0
  107. package/dist/manifest-Bo5cXjdt.mjs +82 -0
  108. package/dist/manifest-BpS4gtUK.mjs +1347 -0
  109. package/dist/manifest-Bv65_e2W.mjs +101 -0
  110. package/dist/manifest-BytNIF4Z.mjs +117 -0
  111. package/dist/manifest-C-xtsjS3.mjs +81 -0
  112. package/dist/manifest-CDYl7OhA.mjs +66 -0
  113. package/dist/manifest-CRZ3xmkD.mjs +61 -0
  114. package/dist/manifest-CoW6u4Tp.mjs +132 -0
  115. package/dist/manifest-Cq5zN_8A.mjs +50 -0
  116. package/dist/manifest-D7YZM_2e.mjs +194 -0
  117. package/dist/manifest-DE_VrAeQ.mjs +314 -0
  118. package/dist/manifest-DGsXSCpT.mjs +39 -0
  119. package/dist/manifest-DJ2vfEuW.mjs +156 -0
  120. package/dist/manifest-DPXDYhEu.mjs +80 -0
  121. package/dist/manifest-Dd4fQb0a.mjs +322 -0
  122. package/dist/manifest-Deq6opGg.mjs +223 -0
  123. package/dist/manifest-DfJTafJK.mjs +37 -0
  124. package/dist/manifest-DgOdgN_j.mjs +50 -0
  125. package/dist/manifest-DlbMW4v4.mjs +47 -0
  126. package/dist/manifest-DmVfbH0w.mjs +374 -0
  127. package/dist/manifest-Dog6Ddjr.mjs +109 -0
  128. package/dist/manifest-DvgU5FWb.mjs +58 -0
  129. package/dist/manifest-HsfDBs7j.mjs +50 -0
  130. package/dist/manifest-I8oQHvCG.mjs +186 -0
  131. package/dist/manifest-NvH_a-av.mjs +786 -0
  132. package/dist/manifest-cEJU1v0Z.mjs +129 -0
  133. package/dist/manifest-wOl5XLB12.mjs +112 -0
  134. package/dist/modules-tZozf0LQ.mjs +10635 -0
  135. package/dist/mojo-ipc-DXNEXEqb.mjs +640 -0
  136. package/dist/network-CPVvwvFg.mjs +3852 -0
  137. package/dist/{artifacts-BbdOMET5.mjs → outputPaths-um7lCRY3.mjs} +219 -216
  138. package/dist/parse-args-B4cY5Vx5.mjs +39 -0
  139. package/dist/platform-CYeFoTWp.mjs +2161 -0
  140. package/dist/process-BTbgcVc6.mjs +1306 -0
  141. package/dist/proxy-r8YN6nP1.mjs +192 -0
  142. package/dist/registry-Bl8ZQW61.mjs +34 -0
  143. package/dist/response-CWhh2aLo.mjs +34 -0
  144. package/dist/server/plugin-api.mjs +2 -2
  145. package/dist/shared-state-board-BoZnSoj-.mjs +586 -0
  146. package/dist/sourcemap-BIDHUVXy.mjs +934 -0
  147. package/dist/ssrf-policy-Dsqd-DTX.mjs +166 -0
  148. package/dist/streaming-Dal6utPp.mjs +725 -0
  149. package/dist/tool-builder-BHJp32mV.mjs +186 -0
  150. package/dist/transform-DRVgGG90.mjs +1011 -0
  151. package/dist/types-Bx92KJfT.mjs +4 -0
  152. package/dist/wasm-BYx5UOeG.mjs +1044 -0
  153. package/dist/webcrack-Be0_FccV.mjs +747 -0
  154. package/dist/workflow-BpuKEtvn.mjs +725 -0
  155. package/package.json +82 -49
  156. package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
  157. package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
  158. package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-jJFy20G-.mjs} +0 -0
  159. package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-BWmPgQsv.mjs} +0 -0
  160. package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-K3V4vGsw.mjs} +0 -0
  161. package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-nnMvEohD.mjs} +0 -0
  162. 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 };