mock-mcp 0.3.1 → 0.5.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 (44) hide show
  1. package/README.md +212 -124
  2. package/dist/adapter/index.cjs +712 -0
  3. package/dist/adapter/index.d.cts +55 -0
  4. package/dist/adapter/index.d.ts +55 -0
  5. package/dist/adapter/index.js +672 -0
  6. package/dist/client/connect.cjs +913 -0
  7. package/dist/client/connect.d.cts +211 -0
  8. package/dist/client/connect.d.ts +204 -7
  9. package/dist/client/connect.js +863 -20
  10. package/dist/client/index.cjs +914 -0
  11. package/dist/client/index.d.cts +4 -0
  12. package/dist/client/index.d.ts +4 -2
  13. package/dist/client/index.js +873 -2
  14. package/dist/daemon/index.cjs +667 -0
  15. package/dist/daemon/index.d.cts +62 -0
  16. package/dist/daemon/index.d.ts +62 -0
  17. package/dist/daemon/index.js +628 -0
  18. package/dist/discovery-Dc2LdF8q.d.cts +105 -0
  19. package/dist/discovery-Dc2LdF8q.d.ts +105 -0
  20. package/dist/index.cjs +2238 -0
  21. package/dist/index.d.cts +472 -0
  22. package/dist/index.d.ts +472 -11
  23. package/dist/index.js +2185 -53
  24. package/dist/protocol-CiwaQFOt.d.ts +239 -0
  25. package/dist/protocol-xZu-wb0n.d.cts +239 -0
  26. package/dist/shared/index.cjs +386 -0
  27. package/dist/shared/index.d.cts +4 -0
  28. package/dist/shared/index.d.ts +4 -0
  29. package/dist/shared/index.js +310 -0
  30. package/dist/types-BKREdsyr.d.cts +32 -0
  31. package/dist/types-BKREdsyr.d.ts +32 -0
  32. package/package.json +44 -4
  33. package/dist/client/batch-mock-collector.d.ts +0 -111
  34. package/dist/client/batch-mock-collector.js +0 -308
  35. package/dist/client/util.d.ts +0 -1
  36. package/dist/client/util.js +0 -3
  37. package/dist/connect.cjs +0 -400
  38. package/dist/connect.d.cts +0 -82
  39. package/dist/server/index.d.ts +0 -1
  40. package/dist/server/index.js +0 -1
  41. package/dist/server/test-mock-mcp-server.d.ts +0 -73
  42. package/dist/server/test-mock-mcp-server.js +0 -419
  43. package/dist/types.d.ts +0 -45
  44. package/dist/types.js +0 -2
@@ -1,308 +0,0 @@
1
- import WebSocket from "ws";
2
- import { BATCH_MOCK_REQUEST, BATCH_MOCK_RESPONSE, } from "../types.js";
3
- import { isEnabled } from "./util.js";
4
- const DEFAULT_TIMEOUT = 60_000;
5
- const DEFAULT_BATCH_DEBOUNCE_MS = 0;
6
- const DEFAULT_MAX_BATCH_SIZE = 50;
7
- const DEFAULT_PORT = 3002;
8
- const DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;
9
- /**
10
- * Collects HTTP requests issued during a single macrotask and forwards them to
11
- * the MCP server as a batch for AI-assisted mock generation.
12
- */
13
- export class BatchMockCollector {
14
- ws;
15
- pendingRequests = new Map();
16
- queuedRequestIds = new Set();
17
- timeout;
18
- batchDebounceMs;
19
- maxBatchSize;
20
- logger;
21
- heartbeatIntervalMs;
22
- enableReconnect;
23
- port;
24
- batchTimer = null;
25
- heartbeatTimer = null;
26
- reconnectTimer = null;
27
- requestIdCounter = 0;
28
- closed = false;
29
- readyResolve;
30
- readyReject;
31
- readyPromise = Promise.resolve();
32
- constructor(options = {}) {
33
- this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
34
- this.batchDebounceMs = options.batchDebounceMs ?? DEFAULT_BATCH_DEBOUNCE_MS;
35
- this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
36
- this.logger = options.logger ?? console;
37
- this.heartbeatIntervalMs =
38
- options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
39
- this.enableReconnect = options.enableReconnect ?? true;
40
- this.port = options.port ?? DEFAULT_PORT;
41
- this.resetReadyPromise();
42
- this.ws = this.createWebSocket();
43
- this.setupWebSocket();
44
- }
45
- /**
46
- * Ensures the underlying WebSocket connection is ready for use.
47
- */
48
- async waitUntilReady() {
49
- return this.readyPromise;
50
- }
51
- /**
52
- * Request mock data for a specific endpoint/method pair.
53
- */
54
- async requestMock(endpoint, method, options = {}) {
55
- if (this.closed) {
56
- throw new Error("BatchMockCollector has been closed");
57
- }
58
- await this.waitUntilReady();
59
- const requestId = `req-${++this.requestIdCounter}`;
60
- const request = {
61
- requestId,
62
- endpoint,
63
- method,
64
- body: options.body,
65
- headers: options.headers,
66
- metadata: options.metadata,
67
- };
68
- let settleCompletion;
69
- const completion = new Promise((resolve) => {
70
- settleCompletion = resolve;
71
- });
72
- return new Promise((resolve, reject) => {
73
- const timeoutId = setTimeout(() => {
74
- this.rejectRequest(requestId, new Error(`Mock request timed out after ${this.timeout}ms: ${method} ${endpoint}`));
75
- }, this.timeout);
76
- this.pendingRequests.set(requestId, {
77
- request,
78
- resolve: (mock) => {
79
- settleCompletion({ status: "fulfilled", value: undefined });
80
- resolve(this.buildResolvedMock(mock));
81
- },
82
- reject: (error) => {
83
- settleCompletion({ status: "rejected", reason: error });
84
- reject(error);
85
- },
86
- timeoutId,
87
- completion,
88
- });
89
- this.enqueueRequest(requestId);
90
- });
91
- }
92
- /**
93
- * Wait for all requests that are currently pending to settle. Requests
94
- * created after this method is called are not included.
95
- */
96
- async waitForPendingRequests() {
97
- if (!isEnabled()) {
98
- return;
99
- }
100
- const pendingCompletions = Array.from(this.pendingRequests.values()).map((pending) => pending.completion);
101
- const results = await Promise.all(pendingCompletions);
102
- const rejected = results.find((result) => result.status === "rejected");
103
- if (rejected) {
104
- throw rejected.reason;
105
- }
106
- }
107
- /**
108
- * Close the underlying connection and fail all pending requests.
109
- */
110
- async close(code) {
111
- if (this.closed) {
112
- return;
113
- }
114
- this.closed = true;
115
- if (this.batchTimer) {
116
- clearTimeout(this.batchTimer);
117
- this.batchTimer = null;
118
- }
119
- if (this.heartbeatTimer) {
120
- clearInterval(this.heartbeatTimer);
121
- this.heartbeatTimer = null;
122
- }
123
- if (this.reconnectTimer) {
124
- clearTimeout(this.reconnectTimer);
125
- this.reconnectTimer = null;
126
- }
127
- this.queuedRequestIds.clear();
128
- const closePromise = new Promise((resolve) => {
129
- this.ws.once("close", () => resolve());
130
- });
131
- this.ws.close(code);
132
- this.failAllPending(new Error("BatchMockCollector has been closed"));
133
- await closePromise;
134
- }
135
- setupWebSocket() {
136
- this.ws.on("open", () => {
137
- this.logger.log("🔌 Connected to mock MCP WebSocket endpoint");
138
- this.readyResolve?.();
139
- this.startHeartbeat();
140
- });
141
- this.ws.on("message", (data) => this.handleMessage(data));
142
- this.ws.on("error", (error) => {
143
- this.logger.error("❌ WebSocket error:", error);
144
- this.readyReject?.(error instanceof Error ? error : new Error(String(error)));
145
- this.failAllPending(error instanceof Error ? error : new Error(String(error)));
146
- });
147
- this.ws.on("close", () => {
148
- this.logger.warn("🔌 WebSocket connection closed");
149
- this.stopHeartbeat();
150
- this.failAllPending(new Error("WebSocket connection closed"));
151
- if (!this.closed && this.enableReconnect) {
152
- this.scheduleReconnect();
153
- }
154
- });
155
- }
156
- createWebSocket() {
157
- const wsUrl = `ws://localhost:${this.port}`;
158
- return new WebSocket(wsUrl);
159
- }
160
- resetReadyPromise() {
161
- this.readyPromise = new Promise((resolve, reject) => {
162
- this.readyResolve = resolve;
163
- this.readyReject = reject;
164
- });
165
- }
166
- startHeartbeat() {
167
- if (this.heartbeatIntervalMs <= 0 || this.heartbeatTimer) {
168
- return;
169
- }
170
- let lastPong = Date.now();
171
- this.ws.on("pong", () => {
172
- lastPong = Date.now();
173
- });
174
- this.heartbeatTimer = setInterval(() => {
175
- if (this.ws.readyState !== WebSocket.OPEN) {
176
- return;
177
- }
178
- const now = Date.now();
179
- if (now - lastPong > this.heartbeatIntervalMs * 2) {
180
- this.logger.warn("Heartbeat missed; closing socket to trigger reconnect...");
181
- this.ws.close();
182
- return;
183
- }
184
- this.ws.ping();
185
- }, this.heartbeatIntervalMs);
186
- this.heartbeatTimer.unref?.();
187
- }
188
- stopHeartbeat() {
189
- if (this.heartbeatTimer) {
190
- clearInterval(this.heartbeatTimer);
191
- this.heartbeatTimer = null;
192
- }
193
- }
194
- scheduleReconnect() {
195
- if (this.reconnectTimer || this.closed) {
196
- return;
197
- }
198
- this.reconnectTimer = setTimeout(() => {
199
- this.reconnectTimer = null;
200
- this.logger.warn("🔄 Reconnecting to mock MCP WebSocket endpoint...");
201
- this.stopHeartbeat();
202
- this.resetReadyPromise();
203
- this.ws = this.createWebSocket();
204
- this.setupWebSocket();
205
- }, 1_000);
206
- this.reconnectTimer.unref?.();
207
- }
208
- handleMessage(data) {
209
- let parsed;
210
- try {
211
- parsed = JSON.parse(data.toString());
212
- }
213
- catch (error) {
214
- this.logger.error("Failed to parse server message:", error);
215
- return;
216
- }
217
- if (parsed.type !== BATCH_MOCK_RESPONSE) {
218
- this.logger.warn("Received unsupported message type", parsed.type);
219
- return;
220
- }
221
- this.logger.debug?.(`📦 Received mock data for ${parsed.mocks.length} requests (batch ${parsed.batchId})`);
222
- for (const mock of parsed.mocks) {
223
- this.resolveRequest(mock);
224
- }
225
- }
226
- resolveRequest(mock) {
227
- const pending = this.pendingRequests.get(mock.requestId);
228
- if (!pending) {
229
- this.logger.warn(`Received mock for unknown request: ${mock.requestId}`);
230
- return;
231
- }
232
- clearTimeout(pending.timeoutId);
233
- this.pendingRequests.delete(mock.requestId);
234
- const resolve = () => pending.resolve(mock);
235
- if (mock.delayMs && mock.delayMs > 0) {
236
- setTimeout(resolve, mock.delayMs);
237
- }
238
- else {
239
- resolve();
240
- }
241
- }
242
- enqueueRequest(requestId) {
243
- this.queuedRequestIds.add(requestId);
244
- if (this.batchTimer) {
245
- return;
246
- }
247
- this.batchTimer = setTimeout(() => {
248
- this.batchTimer = null;
249
- this.flushQueue();
250
- }, this.batchDebounceMs);
251
- }
252
- flushQueue() {
253
- const queuedIds = Array.from(this.queuedRequestIds);
254
- this.queuedRequestIds.clear();
255
- if (queuedIds.length === 0) {
256
- return;
257
- }
258
- for (let i = 0; i < queuedIds.length; i += this.maxBatchSize) {
259
- const chunkIds = queuedIds.slice(i, i + this.maxBatchSize);
260
- const requests = [];
261
- for (const id of chunkIds) {
262
- const pending = this.pendingRequests.get(id);
263
- if (pending) {
264
- requests.push(pending.request);
265
- }
266
- }
267
- if (requests.length > 0) {
268
- this.sendBatch(requests);
269
- }
270
- }
271
- }
272
- sendBatch(requests) {
273
- if (this.ws.readyState !== WebSocket.OPEN) {
274
- const error = new Error("WebSocket is not open");
275
- requests.forEach((request) => this.rejectRequest(request.requestId, error));
276
- return;
277
- }
278
- const payload = {
279
- type: BATCH_MOCK_REQUEST,
280
- requests,
281
- };
282
- this.logger.debug?.(`📤 Sending batch with ${requests.length} request(s) to MCP server`);
283
- this.ws.send(JSON.stringify(payload));
284
- }
285
- buildResolvedMock(mock) {
286
- return {
287
- requestId: mock.requestId,
288
- data: mock.data,
289
- status: mock.status,
290
- headers: mock.headers,
291
- delayMs: mock.delayMs,
292
- };
293
- }
294
- rejectRequest(requestId, error) {
295
- const pending = this.pendingRequests.get(requestId);
296
- if (!pending) {
297
- return;
298
- }
299
- clearTimeout(pending.timeoutId);
300
- this.pendingRequests.delete(requestId);
301
- pending.reject(error);
302
- }
303
- failAllPending(error) {
304
- for (const requestId of Array.from(this.pendingRequests.keys())) {
305
- this.rejectRequest(requestId, error);
306
- }
307
- }
308
- }
@@ -1 +0,0 @@
1
- export declare const isEnabled: () => boolean;
@@ -1,3 +0,0 @@
1
- export const isEnabled = () => {
2
- return process.env.MOCK_MCP !== undefined && process.env.MOCK_MCP !== "0";
3
- };
package/dist/connect.cjs DELETED
@@ -1,400 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/client/connect.ts
31
- var connect_exports = {};
32
- __export(connect_exports, {
33
- connect: () => connect
34
- });
35
- module.exports = __toCommonJS(connect_exports);
36
-
37
- // src/client/batch-mock-collector.ts
38
- var import_ws = __toESM(require("ws"), 1);
39
-
40
- // src/types.ts
41
- var BATCH_MOCK_REQUEST = "BATCH_MOCK_REQUEST";
42
- var BATCH_MOCK_RESPONSE = "BATCH_MOCK_RESPONSE";
43
-
44
- // src/client/util.ts
45
- var isEnabled = () => {
46
- return process.env.MOCK_MCP !== void 0 && process.env.MOCK_MCP !== "0";
47
- };
48
-
49
- // src/client/batch-mock-collector.ts
50
- var DEFAULT_TIMEOUT = 6e4;
51
- var DEFAULT_BATCH_DEBOUNCE_MS = 0;
52
- var DEFAULT_MAX_BATCH_SIZE = 50;
53
- var DEFAULT_PORT = 3002;
54
- var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
55
- var BatchMockCollector = class {
56
- ws;
57
- pendingRequests = /* @__PURE__ */ new Map();
58
- queuedRequestIds = /* @__PURE__ */ new Set();
59
- timeout;
60
- batchDebounceMs;
61
- maxBatchSize;
62
- logger;
63
- heartbeatIntervalMs;
64
- enableReconnect;
65
- port;
66
- batchTimer = null;
67
- heartbeatTimer = null;
68
- reconnectTimer = null;
69
- requestIdCounter = 0;
70
- closed = false;
71
- readyResolve;
72
- readyReject;
73
- readyPromise = Promise.resolve();
74
- constructor(options = {}) {
75
- this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
76
- this.batchDebounceMs = options.batchDebounceMs ?? DEFAULT_BATCH_DEBOUNCE_MS;
77
- this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
78
- this.logger = options.logger ?? console;
79
- this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
80
- this.enableReconnect = options.enableReconnect ?? true;
81
- this.port = options.port ?? DEFAULT_PORT;
82
- this.resetReadyPromise();
83
- this.ws = this.createWebSocket();
84
- this.setupWebSocket();
85
- }
86
- /**
87
- * Ensures the underlying WebSocket connection is ready for use.
88
- */
89
- async waitUntilReady() {
90
- return this.readyPromise;
91
- }
92
- /**
93
- * Request mock data for a specific endpoint/method pair.
94
- */
95
- async requestMock(endpoint, method, options = {}) {
96
- if (this.closed) {
97
- throw new Error("BatchMockCollector has been closed");
98
- }
99
- await this.waitUntilReady();
100
- const requestId = `req-${++this.requestIdCounter}`;
101
- const request = {
102
- requestId,
103
- endpoint,
104
- method,
105
- body: options.body,
106
- headers: options.headers,
107
- metadata: options.metadata
108
- };
109
- let settleCompletion;
110
- const completion = new Promise((resolve) => {
111
- settleCompletion = resolve;
112
- });
113
- return new Promise((resolve, reject) => {
114
- const timeoutId = setTimeout(() => {
115
- this.rejectRequest(
116
- requestId,
117
- new Error(
118
- `Mock request timed out after ${this.timeout}ms: ${method} ${endpoint}`
119
- )
120
- );
121
- }, this.timeout);
122
- this.pendingRequests.set(requestId, {
123
- request,
124
- resolve: (mock) => {
125
- settleCompletion({ status: "fulfilled", value: void 0 });
126
- resolve(this.buildResolvedMock(mock));
127
- },
128
- reject: (error) => {
129
- settleCompletion({ status: "rejected", reason: error });
130
- reject(error);
131
- },
132
- timeoutId,
133
- completion
134
- });
135
- this.enqueueRequest(requestId);
136
- });
137
- }
138
- /**
139
- * Wait for all requests that are currently pending to settle. Requests
140
- * created after this method is called are not included.
141
- */
142
- async waitForPendingRequests() {
143
- if (!isEnabled()) {
144
- return;
145
- }
146
- const pendingCompletions = Array.from(this.pendingRequests.values()).map(
147
- (pending) => pending.completion
148
- );
149
- const results = await Promise.all(pendingCompletions);
150
- const rejected = results.find(
151
- (result) => result.status === "rejected"
152
- );
153
- if (rejected) {
154
- throw rejected.reason;
155
- }
156
- }
157
- /**
158
- * Close the underlying connection and fail all pending requests.
159
- */
160
- async close(code) {
161
- if (this.closed) {
162
- return;
163
- }
164
- this.closed = true;
165
- if (this.batchTimer) {
166
- clearTimeout(this.batchTimer);
167
- this.batchTimer = null;
168
- }
169
- if (this.heartbeatTimer) {
170
- clearInterval(this.heartbeatTimer);
171
- this.heartbeatTimer = null;
172
- }
173
- if (this.reconnectTimer) {
174
- clearTimeout(this.reconnectTimer);
175
- this.reconnectTimer = null;
176
- }
177
- this.queuedRequestIds.clear();
178
- const closePromise = new Promise((resolve) => {
179
- this.ws.once("close", () => resolve());
180
- });
181
- this.ws.close(code);
182
- this.failAllPending(new Error("BatchMockCollector has been closed"));
183
- await closePromise;
184
- }
185
- setupWebSocket() {
186
- this.ws.on("open", () => {
187
- this.logger.log("\u{1F50C} Connected to mock MCP WebSocket endpoint");
188
- this.readyResolve?.();
189
- this.startHeartbeat();
190
- });
191
- this.ws.on("message", (data) => this.handleMessage(data));
192
- this.ws.on("error", (error) => {
193
- this.logger.error("\u274C WebSocket error:", error);
194
- this.readyReject?.(
195
- error instanceof Error ? error : new Error(String(error))
196
- );
197
- this.failAllPending(
198
- error instanceof Error ? error : new Error(String(error))
199
- );
200
- });
201
- this.ws.on("close", () => {
202
- this.logger.warn("\u{1F50C} WebSocket connection closed");
203
- this.stopHeartbeat();
204
- this.failAllPending(new Error("WebSocket connection closed"));
205
- if (!this.closed && this.enableReconnect) {
206
- this.scheduleReconnect();
207
- }
208
- });
209
- }
210
- createWebSocket() {
211
- const wsUrl = `ws://localhost:${this.port}`;
212
- return new import_ws.default(wsUrl);
213
- }
214
- resetReadyPromise() {
215
- this.readyPromise = new Promise((resolve, reject) => {
216
- this.readyResolve = resolve;
217
- this.readyReject = reject;
218
- });
219
- }
220
- startHeartbeat() {
221
- if (this.heartbeatIntervalMs <= 0 || this.heartbeatTimer) {
222
- return;
223
- }
224
- let lastPong = Date.now();
225
- this.ws.on("pong", () => {
226
- lastPong = Date.now();
227
- });
228
- this.heartbeatTimer = setInterval(() => {
229
- if (this.ws.readyState !== import_ws.default.OPEN) {
230
- return;
231
- }
232
- const now = Date.now();
233
- if (now - lastPong > this.heartbeatIntervalMs * 2) {
234
- this.logger.warn(
235
- "Heartbeat missed; closing socket to trigger reconnect..."
236
- );
237
- this.ws.close();
238
- return;
239
- }
240
- this.ws.ping();
241
- }, this.heartbeatIntervalMs);
242
- this.heartbeatTimer.unref?.();
243
- }
244
- stopHeartbeat() {
245
- if (this.heartbeatTimer) {
246
- clearInterval(this.heartbeatTimer);
247
- this.heartbeatTimer = null;
248
- }
249
- }
250
- scheduleReconnect() {
251
- if (this.reconnectTimer || this.closed) {
252
- return;
253
- }
254
- this.reconnectTimer = setTimeout(() => {
255
- this.reconnectTimer = null;
256
- this.logger.warn("\u{1F504} Reconnecting to mock MCP WebSocket endpoint...");
257
- this.stopHeartbeat();
258
- this.resetReadyPromise();
259
- this.ws = this.createWebSocket();
260
- this.setupWebSocket();
261
- }, 1e3);
262
- this.reconnectTimer.unref?.();
263
- }
264
- handleMessage(data) {
265
- let parsed;
266
- try {
267
- parsed = JSON.parse(data.toString());
268
- } catch (error) {
269
- this.logger.error("Failed to parse server message:", error);
270
- return;
271
- }
272
- if (parsed.type !== BATCH_MOCK_RESPONSE) {
273
- this.logger.warn("Received unsupported message type", parsed.type);
274
- return;
275
- }
276
- this.logger.debug?.(
277
- `\u{1F4E6} Received mock data for ${parsed.mocks.length} requests (batch ${parsed.batchId})`
278
- );
279
- for (const mock of parsed.mocks) {
280
- this.resolveRequest(mock);
281
- }
282
- }
283
- resolveRequest(mock) {
284
- const pending = this.pendingRequests.get(mock.requestId);
285
- if (!pending) {
286
- this.logger.warn(`Received mock for unknown request: ${mock.requestId}`);
287
- return;
288
- }
289
- clearTimeout(pending.timeoutId);
290
- this.pendingRequests.delete(mock.requestId);
291
- const resolve = () => pending.resolve(mock);
292
- if (mock.delayMs && mock.delayMs > 0) {
293
- setTimeout(resolve, mock.delayMs);
294
- } else {
295
- resolve();
296
- }
297
- }
298
- enqueueRequest(requestId) {
299
- this.queuedRequestIds.add(requestId);
300
- if (this.batchTimer) {
301
- return;
302
- }
303
- this.batchTimer = setTimeout(() => {
304
- this.batchTimer = null;
305
- this.flushQueue();
306
- }, this.batchDebounceMs);
307
- }
308
- flushQueue() {
309
- const queuedIds = Array.from(this.queuedRequestIds);
310
- this.queuedRequestIds.clear();
311
- if (queuedIds.length === 0) {
312
- return;
313
- }
314
- for (let i = 0; i < queuedIds.length; i += this.maxBatchSize) {
315
- const chunkIds = queuedIds.slice(i, i + this.maxBatchSize);
316
- const requests = [];
317
- for (const id of chunkIds) {
318
- const pending = this.pendingRequests.get(id);
319
- if (pending) {
320
- requests.push(pending.request);
321
- }
322
- }
323
- if (requests.length > 0) {
324
- this.sendBatch(requests);
325
- }
326
- }
327
- }
328
- sendBatch(requests) {
329
- if (this.ws.readyState !== import_ws.default.OPEN) {
330
- const error = new Error("WebSocket is not open");
331
- requests.forEach(
332
- (request) => this.rejectRequest(request.requestId, error)
333
- );
334
- return;
335
- }
336
- const payload = {
337
- type: BATCH_MOCK_REQUEST,
338
- requests
339
- };
340
- this.logger.debug?.(
341
- `\u{1F4E4} Sending batch with ${requests.length} request(s) to MCP server`
342
- );
343
- this.ws.send(JSON.stringify(payload));
344
- }
345
- buildResolvedMock(mock) {
346
- return {
347
- requestId: mock.requestId,
348
- data: mock.data,
349
- status: mock.status,
350
- headers: mock.headers,
351
- delayMs: mock.delayMs
352
- };
353
- }
354
- rejectRequest(requestId, error) {
355
- const pending = this.pendingRequests.get(requestId);
356
- if (!pending) {
357
- return;
358
- }
359
- clearTimeout(pending.timeoutId);
360
- this.pendingRequests.delete(requestId);
361
- pending.reject(error);
362
- }
363
- failAllPending(error) {
364
- for (const requestId of Array.from(this.pendingRequests.keys())) {
365
- this.rejectRequest(requestId, error);
366
- }
367
- }
368
- };
369
-
370
- // src/client/connect.ts
371
- var DisabledMockClient = class {
372
- async waitUntilReady() {
373
- return;
374
- }
375
- async requestMock() {
376
- throw new Error(
377
- "[mock-mcp] MOCK_MCP is not enabled. Set MOCK_MCP=1 to enable mock generation."
378
- );
379
- }
380
- async waitForPendingRequests() {
381
- return;
382
- }
383
- async close() {
384
- return;
385
- }
386
- };
387
- var connect = async (options) => {
388
- const resolvedOptions = typeof options === "number" ? { port: options } : options ?? {};
389
- if (!isEnabled()) {
390
- console.log("[mock-mcp] Skipping (set MOCK_MCP=1 to enable)");
391
- return new DisabledMockClient();
392
- }
393
- const collector = new BatchMockCollector(resolvedOptions);
394
- await collector.waitUntilReady();
395
- return collector;
396
- };
397
- // Annotate the CommonJS export names for ESM import in node:
398
- 0 && (module.exports = {
399
- connect
400
- });