@frontmcp/testing 0.6.0 → 0.6.2

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 (142) hide show
  1. package/esm/fixtures/index.mjs +2377 -0
  2. package/esm/index.mjs +4768 -0
  3. package/esm/matchers/index.mjs +646 -0
  4. package/esm/package.json +127 -0
  5. package/esm/playwright/index.mjs +19 -0
  6. package/esm/setup.mjs +680 -0
  7. package/fixtures/index.js +2418 -0
  8. package/index.js +4866 -0
  9. package/jest-preset.js +3 -3
  10. package/matchers/index.js +673 -0
  11. package/package.json +52 -24
  12. package/playwright/index.js +46 -0
  13. package/setup.js +651 -0
  14. package/src/assertions/index.js +0 -18
  15. package/src/assertions/index.js.map +0 -1
  16. package/src/assertions/mcp-assertions.js +0 -220
  17. package/src/assertions/mcp-assertions.js.map +0 -1
  18. package/src/auth/auth-headers.js +0 -62
  19. package/src/auth/auth-headers.js.map +0 -1
  20. package/src/auth/index.js +0 -15
  21. package/src/auth/index.js.map +0 -1
  22. package/src/auth/mock-api-server.js +0 -200
  23. package/src/auth/mock-api-server.js.map +0 -1
  24. package/src/auth/mock-oauth-server.js +0 -253
  25. package/src/auth/mock-oauth-server.js.map +0 -1
  26. package/src/auth/token-factory.js +0 -181
  27. package/src/auth/token-factory.js.map +0 -1
  28. package/src/auth/user-fixtures.js +0 -92
  29. package/src/auth/user-fixtures.js.map +0 -1
  30. package/src/client/index.js +0 -12
  31. package/src/client/index.js.map +0 -1
  32. package/src/client/mcp-test-client.builder.js +0 -163
  33. package/src/client/mcp-test-client.builder.js.map +0 -1
  34. package/src/client/mcp-test-client.js +0 -937
  35. package/src/client/mcp-test-client.js.map +0 -1
  36. package/src/client/mcp-test-client.types.js +0 -16
  37. package/src/client/mcp-test-client.types.js.map +0 -1
  38. package/src/errors/index.js +0 -85
  39. package/src/errors/index.js.map +0 -1
  40. package/src/example-tools/index.js +0 -40
  41. package/src/example-tools/index.js.map +0 -1
  42. package/src/example-tools/tool-configs.js +0 -222
  43. package/src/example-tools/tool-configs.js.map +0 -1
  44. package/src/expect.js +0 -31
  45. package/src/expect.js.map +0 -1
  46. package/src/fixtures/fixture-types.js +0 -7
  47. package/src/fixtures/fixture-types.js.map +0 -1
  48. package/src/fixtures/index.js +0 -16
  49. package/src/fixtures/index.js.map +0 -1
  50. package/src/fixtures/test-fixture.js +0 -311
  51. package/src/fixtures/test-fixture.js.map +0 -1
  52. package/src/http-mock/http-mock.js +0 -544
  53. package/src/http-mock/http-mock.js.map +0 -1
  54. package/src/http-mock/http-mock.types.js +0 -10
  55. package/src/http-mock/http-mock.types.js.map +0 -1
  56. package/src/http-mock/index.js +0 -11
  57. package/src/http-mock/index.js.map +0 -1
  58. package/src/index.js +0 -167
  59. package/src/index.js.map +0 -1
  60. package/src/interceptor/index.js +0 -15
  61. package/src/interceptor/index.js.map +0 -1
  62. package/src/interceptor/interceptor-chain.js +0 -207
  63. package/src/interceptor/interceptor-chain.js.map +0 -1
  64. package/src/interceptor/interceptor.types.js +0 -7
  65. package/src/interceptor/interceptor.types.js.map +0 -1
  66. package/src/interceptor/mock-registry.js +0 -189
  67. package/src/interceptor/mock-registry.js.map +0 -1
  68. package/src/matchers/index.js +0 -12
  69. package/src/matchers/index.js.map +0 -1
  70. package/src/matchers/matcher-types.js +0 -10
  71. package/src/matchers/matcher-types.js.map +0 -1
  72. package/src/matchers/mcp-matchers.js +0 -395
  73. package/src/matchers/mcp-matchers.js.map +0 -1
  74. package/src/platform/index.js +0 -47
  75. package/src/platform/index.js.map +0 -1
  76. package/src/platform/platform-client-info.js +0 -155
  77. package/src/platform/platform-client-info.js.map +0 -1
  78. package/src/platform/platform-types.js +0 -110
  79. package/src/platform/platform-types.js.map +0 -1
  80. package/src/playwright/index.js +0 -49
  81. package/src/playwright/index.js.map +0 -1
  82. package/src/server/index.js +0 -10
  83. package/src/server/index.js.map +0 -1
  84. package/src/server/test-server.js +0 -341
  85. package/src/server/test-server.js.map +0 -1
  86. package/src/setup.js +0 -30
  87. package/src/setup.js.map +0 -1
  88. package/src/transport/index.js +0 -10
  89. package/src/transport/index.js.map +0 -1
  90. package/src/transport/streamable-http.transport.js +0 -438
  91. package/src/transport/streamable-http.transport.js.map +0 -1
  92. package/src/transport/transport.interface.js +0 -7
  93. package/src/transport/transport.interface.js.map +0 -1
  94. package/src/ui/index.js +0 -23
  95. package/src/ui/index.js.map +0 -1
  96. package/src/ui/ui-assertions.js +0 -367
  97. package/src/ui/ui-assertions.js.map +0 -1
  98. package/src/ui/ui-matchers.js +0 -493
  99. package/src/ui/ui-matchers.js.map +0 -1
  100. /package/{src/assertions → assertions}/index.d.ts +0 -0
  101. /package/{src/assertions → assertions}/mcp-assertions.d.ts +0 -0
  102. /package/{src/auth → auth}/auth-headers.d.ts +0 -0
  103. /package/{src/auth → auth}/index.d.ts +0 -0
  104. /package/{src/auth → auth}/mock-api-server.d.ts +0 -0
  105. /package/{src/auth → auth}/mock-oauth-server.d.ts +0 -0
  106. /package/{src/auth → auth}/token-factory.d.ts +0 -0
  107. /package/{src/auth → auth}/user-fixtures.d.ts +0 -0
  108. /package/{src/client → client}/index.d.ts +0 -0
  109. /package/{src/client → client}/mcp-test-client.builder.d.ts +0 -0
  110. /package/{src/client → client}/mcp-test-client.d.ts +0 -0
  111. /package/{src/client → client}/mcp-test-client.types.d.ts +0 -0
  112. /package/{src/errors → errors}/index.d.ts +0 -0
  113. /package/{src/example-tools → example-tools}/index.d.ts +0 -0
  114. /package/{src/example-tools → example-tools}/tool-configs.d.ts +0 -0
  115. /package/{src/expect.d.ts → expect.d.ts} +0 -0
  116. /package/{src/fixtures → fixtures}/fixture-types.d.ts +0 -0
  117. /package/{src/fixtures → fixtures}/index.d.ts +0 -0
  118. /package/{src/fixtures → fixtures}/test-fixture.d.ts +0 -0
  119. /package/{src/http-mock → http-mock}/http-mock.d.ts +0 -0
  120. /package/{src/http-mock → http-mock}/http-mock.types.d.ts +0 -0
  121. /package/{src/http-mock → http-mock}/index.d.ts +0 -0
  122. /package/{src/index.d.ts → index.d.ts} +0 -0
  123. /package/{src/interceptor → interceptor}/index.d.ts +0 -0
  124. /package/{src/interceptor → interceptor}/interceptor-chain.d.ts +0 -0
  125. /package/{src/interceptor → interceptor}/interceptor.types.d.ts +0 -0
  126. /package/{src/interceptor → interceptor}/mock-registry.d.ts +0 -0
  127. /package/{src/matchers → matchers}/index.d.ts +0 -0
  128. /package/{src/matchers → matchers}/matcher-types.d.ts +0 -0
  129. /package/{src/matchers → matchers}/mcp-matchers.d.ts +0 -0
  130. /package/{src/platform → platform}/index.d.ts +0 -0
  131. /package/{src/platform → platform}/platform-client-info.d.ts +0 -0
  132. /package/{src/platform → platform}/platform-types.d.ts +0 -0
  133. /package/{src/playwright → playwright}/index.d.ts +0 -0
  134. /package/{src/server → server}/index.d.ts +0 -0
  135. /package/{src/server → server}/test-server.d.ts +0 -0
  136. /package/{src/setup.d.ts → setup.d.ts} +0 -0
  137. /package/{src/transport → transport}/index.d.ts +0 -0
  138. /package/{src/transport → transport}/streamable-http.transport.d.ts +0 -0
  139. /package/{src/transport → transport}/transport.interface.d.ts +0 -0
  140. /package/{src/ui → ui}/index.d.ts +0 -0
  141. /package/{src/ui → ui}/ui-assertions.d.ts +0 -0
  142. /package/{src/ui → ui}/ui-matchers.d.ts +0 -0
@@ -1,544 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file http-mock.ts
4
- * @description HTTP request mocking implementation for offline MCP server testing
5
- *
6
- * This module intercepts fetch() and XMLHttpRequest calls, allowing tools to be
7
- * tested without making real HTTP requests.
8
- *
9
- * @example
10
- * ```typescript
11
- * import { httpMock } from '@frontmcp/testing';
12
- *
13
- * // Enable HTTP mocking
14
- * const interceptor = httpMock.interceptor();
15
- *
16
- * // Mock a GET request
17
- * interceptor.get('https://api.example.com/users', {
18
- * body: [{ id: 1, name: 'John' }],
19
- * });
20
- *
21
- * // Mock a POST request with pattern matching
22
- * interceptor.post(/api\.example\.com\/users/, {
23
- * status: 201,
24
- * body: { id: 2, name: 'Jane' },
25
- * });
26
- *
27
- * // Now run your MCP server test - HTTP calls will be intercepted
28
- * const result = await mcp.tools.call('fetch-users', {});
29
- *
30
- * // Verify all mocks were used
31
- * interceptor.assertDone();
32
- *
33
- * // Restore original fetch
34
- * interceptor.restore();
35
- * ```
36
- */
37
- Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.httpResponse = exports.httpMock = void 0;
39
- // ═══════════════════════════════════════════════════════════════════
40
- // GLOBAL STATE
41
- // ═══════════════════════════════════════════════════════════════════
42
- /** Original fetch function */
43
- let originalFetch = null;
44
- /** Whether mocking is enabled */
45
- let mockingEnabled = false;
46
- /** All active interceptors */
47
- const activeInterceptors = [];
48
- class HttpInterceptorImpl {
49
- mocks = [];
50
- _allowPassthrough = false;
51
- _isActive = true;
52
- mock(definition) {
53
- const entry = {
54
- definition,
55
- callCount: 0,
56
- calls: [],
57
- remainingUses: definition.times ?? Infinity,
58
- };
59
- this.mocks.push(entry);
60
- return {
61
- remove: () => {
62
- const index = this.mocks.indexOf(entry);
63
- if (index !== -1) {
64
- this.mocks.splice(index, 1);
65
- }
66
- },
67
- callCount: () => entry.callCount,
68
- calls: () => [...entry.calls],
69
- waitForCalls: async (count, timeoutMs = 5000) => {
70
- const deadline = Date.now() + timeoutMs;
71
- while (Date.now() < deadline) {
72
- if (entry.callCount >= count) {
73
- return entry.calls.slice(0, count);
74
- }
75
- await sleep(50);
76
- }
77
- throw new Error(`Timeout waiting for ${count} calls, got ${entry.callCount}`);
78
- },
79
- };
80
- }
81
- get(url, response) {
82
- return this.mock({
83
- match: { url, method: 'GET' },
84
- response: normalizeResponse(response),
85
- });
86
- }
87
- post(url, response) {
88
- return this.mock({
89
- match: { url, method: 'POST' },
90
- response: normalizeResponse(response),
91
- });
92
- }
93
- put(url, response) {
94
- return this.mock({
95
- match: { url, method: 'PUT' },
96
- response: normalizeResponse(response),
97
- });
98
- }
99
- delete(url, response) {
100
- return this.mock({
101
- match: { url, method: 'DELETE' },
102
- response: normalizeResponse(response),
103
- });
104
- }
105
- any(url, response) {
106
- return this.mock({
107
- match: { url },
108
- response: normalizeResponse(response),
109
- });
110
- }
111
- clear() {
112
- this.mocks = [];
113
- }
114
- pending() {
115
- return this.mocks.filter((m) => m.remainingUses > 0).map((m) => m.definition);
116
- }
117
- isDone() {
118
- return this.mocks.every((m) => m.remainingUses <= 0 || m.definition.times === undefined);
119
- }
120
- assertDone() {
121
- const pending = this.pending().filter((m) => m.times !== undefined);
122
- if (pending.length > 0) {
123
- const descriptions = pending.map((m) => {
124
- const url = m.match.url instanceof RegExp ? m.match.url.toString() : m.match.url;
125
- return ` - ${m.match.method ?? 'ANY'} ${url}`;
126
- });
127
- throw new Error(`Unused HTTP mocks:\n${descriptions.join('\n')}`);
128
- }
129
- }
130
- allowPassthrough(allow) {
131
- this._allowPassthrough = allow;
132
- }
133
- restore() {
134
- this._isActive = false;
135
- const index = activeInterceptors.indexOf(this);
136
- if (index !== -1) {
137
- activeInterceptors.splice(index, 1);
138
- }
139
- }
140
- // Internal methods
141
- isActive() {
142
- return this._isActive;
143
- }
144
- canPassthrough() {
145
- return this._allowPassthrough;
146
- }
147
- /**
148
- * Try to match a request against mocks in this scope
149
- */
150
- async matchRequest(info) {
151
- if (!this._isActive)
152
- return null;
153
- for (const entry of this.mocks) {
154
- if (entry.remainingUses <= 0)
155
- continue;
156
- if (this.requestMatches(info, entry.definition.match)) {
157
- // Found a match
158
- entry.callCount++;
159
- entry.calls.push(info);
160
- entry.remainingUses--;
161
- // Get response
162
- let mockResponse;
163
- if (typeof entry.definition.response === 'function') {
164
- mockResponse = await entry.definition.response(info);
165
- }
166
- else {
167
- mockResponse = entry.definition.response;
168
- }
169
- // Apply delay
170
- if (mockResponse.delay && mockResponse.delay > 0) {
171
- await sleep(mockResponse.delay);
172
- }
173
- return createMockResponse(mockResponse);
174
- }
175
- }
176
- return null;
177
- }
178
- requestMatches(info, matcher) {
179
- // Check URL
180
- if (!this.urlMatches(info.url, matcher.url)) {
181
- return false;
182
- }
183
- // Check method
184
- if (matcher.method) {
185
- const methods = Array.isArray(matcher.method) ? matcher.method : [matcher.method];
186
- if (!methods.includes(info.method)) {
187
- return false;
188
- }
189
- }
190
- // Check headers
191
- if (matcher.headers) {
192
- for (const [key, expected] of Object.entries(matcher.headers)) {
193
- const actual = info.headers[key.toLowerCase()];
194
- if (!actual)
195
- return false;
196
- if (expected instanceof RegExp) {
197
- if (!expected.test(actual))
198
- return false;
199
- }
200
- else if (actual !== expected) {
201
- return false;
202
- }
203
- }
204
- }
205
- // Check body
206
- if (matcher.body !== undefined) {
207
- if (!this.bodyMatches(info.body, matcher.body)) {
208
- return false;
209
- }
210
- }
211
- return true;
212
- }
213
- urlMatches(url, matcher) {
214
- if (typeof matcher === 'string') {
215
- // Exact match or contains
216
- return url === matcher || url.includes(matcher);
217
- }
218
- else if (matcher instanceof RegExp) {
219
- return matcher.test(url);
220
- }
221
- else {
222
- return matcher(url);
223
- }
224
- }
225
- bodyMatches(actual, expected) {
226
- if (typeof expected === 'function') {
227
- return expected(actual);
228
- }
229
- if (typeof expected === 'string') {
230
- const actualStr = typeof actual === 'string' ? actual : JSON.stringify(actual);
231
- return actualStr === expected || actualStr.includes(expected);
232
- }
233
- if (expected instanceof RegExp) {
234
- const actualStr = typeof actual === 'string' ? actual : JSON.stringify(actual);
235
- return expected.test(actualStr);
236
- }
237
- // Object comparison - check that all expected keys match
238
- if (typeof actual !== 'object' || actual === null) {
239
- return false;
240
- }
241
- for (const [key, value] of Object.entries(expected)) {
242
- const actualValue = actual[key];
243
- if (typeof value === 'object' && value !== null) {
244
- if (!this.bodyMatches(actualValue, value)) {
245
- return false;
246
- }
247
- }
248
- else if (actualValue !== value) {
249
- return false;
250
- }
251
- }
252
- return true;
253
- }
254
- }
255
- // ═══════════════════════════════════════════════════════════════════
256
- // FETCH INTERCEPTOR
257
- // ═══════════════════════════════════════════════════════════════════
258
- async function interceptedFetch(input, init) {
259
- // Build request info
260
- const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
261
- const method = (init?.method ?? 'GET').toUpperCase();
262
- // Parse headers
263
- const headers = {};
264
- if (init?.headers) {
265
- if (init.headers instanceof Headers) {
266
- init.headers.forEach((value, key) => {
267
- headers[key.toLowerCase()] = value;
268
- });
269
- }
270
- else if (Array.isArray(init.headers)) {
271
- for (const [key, value] of init.headers) {
272
- headers[key.toLowerCase()] = value;
273
- }
274
- }
275
- else {
276
- for (const [key, value] of Object.entries(init.headers)) {
277
- headers[key.toLowerCase()] = value;
278
- }
279
- }
280
- }
281
- // Parse body
282
- let body;
283
- let rawBody;
284
- if (init?.body) {
285
- if (typeof init.body === 'string') {
286
- rawBody = init.body;
287
- try {
288
- body = JSON.parse(init.body);
289
- }
290
- catch {
291
- body = init.body;
292
- }
293
- }
294
- else if (init.body instanceof ArrayBuffer || init.body instanceof Uint8Array) {
295
- rawBody = new TextDecoder().decode(init.body);
296
- try {
297
- body = JSON.parse(rawBody);
298
- }
299
- catch {
300
- body = rawBody;
301
- }
302
- }
303
- else {
304
- body = init.body;
305
- }
306
- }
307
- const requestInfo = {
308
- url,
309
- method,
310
- headers,
311
- body,
312
- rawBody,
313
- };
314
- // Try to match against active interceptors (in reverse order - last added first)
315
- for (let i = activeInterceptors.length - 1; i >= 0; i--) {
316
- const interceptor = activeInterceptors[i];
317
- if (!interceptor.isActive())
318
- continue;
319
- const mockResponse = await interceptor.matchRequest(requestInfo);
320
- if (mockResponse) {
321
- return mockResponse;
322
- }
323
- }
324
- // No match found - check if passthrough is allowed
325
- for (const interceptor of activeInterceptors) {
326
- if (interceptor.isActive() && interceptor.canPassthrough()) {
327
- // Passthrough to real fetch
328
- if (originalFetch) {
329
- return originalFetch(input, init);
330
- }
331
- }
332
- }
333
- // No match and no passthrough - throw error
334
- throw new Error(`No HTTP mock found for ${method} ${url}\n` +
335
- `Add a mock using httpMock.interceptor().${method.toLowerCase()}('${url}', { body: ... })`);
336
- }
337
- // ═══════════════════════════════════════════════════════════════════
338
- // HELPERS
339
- // ═══════════════════════════════════════════════════════════════════
340
- function normalizeResponse(response) {
341
- // If it looks like a plain object (response body), wrap it
342
- if (!('status' in response) && !('body' in response) && !('headers' in response)) {
343
- return { body: response };
344
- }
345
- return response;
346
- }
347
- function createMockResponse(mock) {
348
- // Check for network error - throw instead of returning Response
349
- // This simulates real network failures where fetch rejects
350
- const mockAny = mock;
351
- if (mockAny._throwError) {
352
- const body = mock.body;
353
- const message = body && typeof body === 'object' && 'message' in body ? String(body['message']) : 'Network error';
354
- throw new TypeError(`fetch failed: ${message}`);
355
- }
356
- const status = mock.status ?? 200;
357
- const statusText = mock.statusText ?? 'OK';
358
- // Build headers
359
- const headers = new Headers(mock.headers ?? {});
360
- // Build body
361
- let body = null;
362
- if (mock.body !== undefined) {
363
- if (typeof mock.body === 'string') {
364
- body = mock.body;
365
- if (!headers.has('content-type')) {
366
- headers.set('content-type', 'text/plain');
367
- }
368
- }
369
- else if (mock.body instanceof ArrayBuffer) {
370
- body = mock.body;
371
- }
372
- else if (Buffer.isBuffer(mock.body)) {
373
- // Convert Buffer to ArrayBuffer for Response compatibility
374
- body = new Uint8Array(mock.body).buffer;
375
- }
376
- else {
377
- body = JSON.stringify(mock.body);
378
- if (!headers.has('content-type')) {
379
- headers.set('content-type', 'application/json');
380
- }
381
- }
382
- }
383
- return new Response(body, { status, statusText, headers });
384
- }
385
- function sleep(ms) {
386
- return new Promise((resolve) => setTimeout(resolve, ms));
387
- }
388
- // ═══════════════════════════════════════════════════════════════════
389
- // MANAGER IMPLEMENTATION
390
- // ═══════════════════════════════════════════════════════════════════
391
- class HttpMockManagerImpl {
392
- interceptor() {
393
- // Auto-enable mocking when creating an interceptor
394
- if (!mockingEnabled) {
395
- this.enable();
396
- }
397
- const interceptor = new HttpInterceptorImpl();
398
- activeInterceptors.push(interceptor);
399
- return interceptor;
400
- }
401
- enable() {
402
- if (mockingEnabled)
403
- return;
404
- // Store original fetch
405
- if (typeof globalThis.fetch === 'function') {
406
- originalFetch = globalThis.fetch;
407
- globalThis.fetch = interceptedFetch;
408
- }
409
- mockingEnabled = true;
410
- }
411
- disable() {
412
- if (!mockingEnabled)
413
- return;
414
- // Restore original fetch
415
- if (originalFetch) {
416
- globalThis.fetch = originalFetch;
417
- originalFetch = null;
418
- }
419
- // Clear all interceptors
420
- activeInterceptors.length = 0;
421
- mockingEnabled = false;
422
- }
423
- isEnabled() {
424
- return mockingEnabled;
425
- }
426
- clearAll() {
427
- for (const interceptor of activeInterceptors) {
428
- interceptor.clear();
429
- }
430
- activeInterceptors.length = 0;
431
- }
432
- }
433
- // ═══════════════════════════════════════════════════════════════════
434
- // EXPORTS
435
- // ═══════════════════════════════════════════════════════════════════
436
- /**
437
- * Global HTTP mock manager
438
- *
439
- * @example
440
- * ```typescript
441
- * import { httpMock } from '@frontmcp/testing';
442
- *
443
- * // Create an HTTP interceptor
444
- * const interceptor = httpMock.interceptor();
445
- *
446
- * // Mock requests
447
- * interceptor.get('https://api.example.com/data', { id: 1, name: 'Test' });
448
- *
449
- * // Run tests...
450
- *
451
- * // Clean up
452
- * interceptor.restore();
453
- * ```
454
- */
455
- exports.httpMock = new HttpMockManagerImpl();
456
- /**
457
- * Helper to create mock responses
458
- */
459
- exports.httpResponse = {
460
- /** Create a JSON response */
461
- json(data, status = 200) {
462
- return {
463
- status,
464
- headers: { 'content-type': 'application/json' },
465
- body: data,
466
- };
467
- },
468
- /** Create a text response */
469
- text(data, status = 200) {
470
- return {
471
- status,
472
- headers: { 'content-type': 'text/plain' },
473
- body: data,
474
- };
475
- },
476
- /** Create an HTML response */
477
- html(data, status = 200) {
478
- return {
479
- status,
480
- headers: { 'content-type': 'text/html' },
481
- body: data,
482
- };
483
- },
484
- /** Create an error response */
485
- error(status, message) {
486
- return {
487
- status,
488
- statusText: message ?? getStatusText(status),
489
- body: message ? { error: message } : undefined,
490
- };
491
- },
492
- /** Create a 404 Not Found response */
493
- notFound(message = 'Not Found') {
494
- return exports.httpResponse.error(404, message);
495
- },
496
- /** Create a 500 Internal Server Error response */
497
- serverError(message = 'Internal Server Error') {
498
- return exports.httpResponse.error(500, message);
499
- },
500
- /** Create a 401 Unauthorized response */
501
- unauthorized(message = 'Unauthorized') {
502
- return exports.httpResponse.error(401, message);
503
- },
504
- /** Create a 403 Forbidden response */
505
- forbidden(message = 'Forbidden') {
506
- return exports.httpResponse.error(403, message);
507
- },
508
- /**
509
- * Create a network error that causes fetch to reject
510
- * This simulates real network failures where fetch throws instead of returning a Response
511
- */
512
- networkError(message = 'Network error') {
513
- return {
514
- status: 0,
515
- body: { _networkError: true, message },
516
- // Mark this as a network error for the interceptor to throw
517
- _throwError: true,
518
- };
519
- },
520
- /** Create a delayed response */
521
- delayed(data, delayMs, status = 200) {
522
- return {
523
- status,
524
- body: data,
525
- delay: delayMs,
526
- };
527
- },
528
- };
529
- function getStatusText(status) {
530
- const texts = {
531
- 200: 'OK',
532
- 201: 'Created',
533
- 204: 'No Content',
534
- 400: 'Bad Request',
535
- 401: 'Unauthorized',
536
- 403: 'Forbidden',
537
- 404: 'Not Found',
538
- 500: 'Internal Server Error',
539
- 502: 'Bad Gateway',
540
- 503: 'Service Unavailable',
541
- };
542
- return texts[status] ?? 'Unknown';
543
- }
544
- //# sourceMappingURL=http-mock.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"http-mock.js","sourceRoot":"","sources":["../../../src/http-mock/http-mock.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file http-mock.ts\n * @description HTTP request mocking implementation for offline MCP server testing\n *\n * This module intercepts fetch() and XMLHttpRequest calls, allowing tools to be\n * tested without making real HTTP requests.\n *\n * @example\n * ```typescript\n * import { httpMock } from '@frontmcp/testing';\n *\n * // Enable HTTP mocking\n * const interceptor = httpMock.interceptor();\n *\n * // Mock a GET request\n * interceptor.get('https://api.example.com/users', {\n * body: [{ id: 1, name: 'John' }],\n * });\n *\n * // Mock a POST request with pattern matching\n * interceptor.post(/api\\.example\\.com\\/users/, {\n * status: 201,\n * body: { id: 2, name: 'Jane' },\n * });\n *\n * // Now run your MCP server test - HTTP calls will be intercepted\n * const result = await mcp.tools.call('fetch-users', {});\n *\n * // Verify all mocks were used\n * interceptor.assertDone();\n *\n * // Restore original fetch\n * interceptor.restore();\n * ```\n */\n\nimport type {\n HttpMethod,\n HttpRequestMatcher,\n HttpMockResponse,\n HttpMockDefinition,\n HttpRequestInfo,\n HttpMockHandle,\n HttpInterceptor,\n HttpMockManager,\n} from './http-mock.types';\n\n// ═══════════════════════════════════════════════════════════════════\n// GLOBAL STATE\n// ═══════════════════════════════════════════════════════════════════\n\n/** Original fetch function */\nlet originalFetch: typeof globalThis.fetch | null = null;\n\n/** Whether mocking is enabled */\nlet mockingEnabled = false;\n\n/** All active interceptors */\nconst activeInterceptors: HttpInterceptorImpl[] = [];\n\n// ═══════════════════════════════════════════════════════════════════\n// HTTP INTERCEPTOR IMPLEMENTATION\n// ═══════════════════════════════════════════════════════════════════\n\ninterface MockEntry {\n definition: HttpMockDefinition;\n callCount: number;\n calls: HttpRequestInfo[];\n remainingUses: number;\n}\n\nclass HttpInterceptorImpl implements HttpInterceptor {\n private mocks: MockEntry[] = [];\n private _allowPassthrough = false;\n private _isActive = true;\n\n mock(definition: HttpMockDefinition): HttpMockHandle {\n const entry: MockEntry = {\n definition,\n callCount: 0,\n calls: [],\n remainingUses: definition.times ?? Infinity,\n };\n\n this.mocks.push(entry);\n\n return {\n remove: () => {\n const index = this.mocks.indexOf(entry);\n if (index !== -1) {\n this.mocks.splice(index, 1);\n }\n },\n callCount: () => entry.callCount,\n calls: () => [...entry.calls],\n waitForCalls: async (count: number, timeoutMs = 5000) => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (entry.callCount >= count) {\n return entry.calls.slice(0, count);\n }\n await sleep(50);\n }\n throw new Error(`Timeout waiting for ${count} calls, got ${entry.callCount}`);\n },\n };\n }\n\n get(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle {\n return this.mock({\n match: { url, method: 'GET' },\n response: normalizeResponse(response),\n });\n }\n\n post(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle {\n return this.mock({\n match: { url, method: 'POST' },\n response: normalizeResponse(response),\n });\n }\n\n put(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle {\n return this.mock({\n match: { url, method: 'PUT' },\n response: normalizeResponse(response),\n });\n }\n\n delete(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle {\n return this.mock({\n match: { url, method: 'DELETE' },\n response: normalizeResponse(response),\n });\n }\n\n any(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle {\n return this.mock({\n match: { url },\n response: normalizeResponse(response),\n });\n }\n\n clear(): void {\n this.mocks = [];\n }\n\n pending(): HttpMockDefinition[] {\n return this.mocks.filter((m) => m.remainingUses > 0).map((m) => m.definition);\n }\n\n isDone(): boolean {\n return this.mocks.every((m) => m.remainingUses <= 0 || m.definition.times === undefined);\n }\n\n assertDone(): void {\n const pending = this.pending().filter((m) => m.times !== undefined);\n if (pending.length > 0) {\n const descriptions = pending.map((m) => {\n const url = m.match.url instanceof RegExp ? m.match.url.toString() : m.match.url;\n return ` - ${m.match.method ?? 'ANY'} ${url}`;\n });\n throw new Error(`Unused HTTP mocks:\\n${descriptions.join('\\n')}`);\n }\n }\n\n allowPassthrough(allow: boolean): void {\n this._allowPassthrough = allow;\n }\n\n restore(): void {\n this._isActive = false;\n const index = activeInterceptors.indexOf(this);\n if (index !== -1) {\n activeInterceptors.splice(index, 1);\n }\n }\n\n // Internal methods\n\n isActive(): boolean {\n return this._isActive;\n }\n\n canPassthrough(): boolean {\n return this._allowPassthrough;\n }\n\n /**\n * Try to match a request against mocks in this scope\n */\n async matchRequest(info: HttpRequestInfo): Promise<Response | null> {\n if (!this._isActive) return null;\n\n for (const entry of this.mocks) {\n if (entry.remainingUses <= 0) continue;\n\n if (this.requestMatches(info, entry.definition.match)) {\n // Found a match\n entry.callCount++;\n entry.calls.push(info);\n entry.remainingUses--;\n\n // Get response\n let mockResponse: HttpMockResponse;\n if (typeof entry.definition.response === 'function') {\n mockResponse = await entry.definition.response(info);\n } else {\n mockResponse = entry.definition.response;\n }\n\n // Apply delay\n if (mockResponse.delay && mockResponse.delay > 0) {\n await sleep(mockResponse.delay);\n }\n\n return createMockResponse(mockResponse);\n }\n }\n\n return null;\n }\n\n private requestMatches(info: HttpRequestInfo, matcher: HttpRequestMatcher): boolean {\n // Check URL\n if (!this.urlMatches(info.url, matcher.url)) {\n return false;\n }\n\n // Check method\n if (matcher.method) {\n const methods = Array.isArray(matcher.method) ? matcher.method : [matcher.method];\n if (!methods.includes(info.method)) {\n return false;\n }\n }\n\n // Check headers\n if (matcher.headers) {\n for (const [key, expected] of Object.entries(matcher.headers)) {\n const actual = info.headers[key.toLowerCase()];\n if (!actual) return false;\n if (expected instanceof RegExp) {\n if (!expected.test(actual)) return false;\n } else if (actual !== expected) {\n return false;\n }\n }\n }\n\n // Check body\n if (matcher.body !== undefined) {\n if (!this.bodyMatches(info.body, matcher.body)) {\n return false;\n }\n }\n\n return true;\n }\n\n private urlMatches(url: string, matcher: string | RegExp | ((url: string) => boolean)): boolean {\n if (typeof matcher === 'string') {\n // Exact match or contains\n return url === matcher || url.includes(matcher);\n } else if (matcher instanceof RegExp) {\n return matcher.test(url);\n } else {\n return matcher(url);\n }\n }\n\n private bodyMatches(\n actual: unknown,\n expected: string | RegExp | Record<string, unknown> | ((body: unknown) => boolean),\n ): boolean {\n if (typeof expected === 'function') {\n return expected(actual);\n }\n\n if (typeof expected === 'string') {\n const actualStr = typeof actual === 'string' ? actual : JSON.stringify(actual);\n return actualStr === expected || actualStr.includes(expected);\n }\n\n if (expected instanceof RegExp) {\n const actualStr = typeof actual === 'string' ? actual : JSON.stringify(actual);\n return expected.test(actualStr);\n }\n\n // Object comparison - check that all expected keys match\n if (typeof actual !== 'object' || actual === null) {\n return false;\n }\n\n for (const [key, value] of Object.entries(expected)) {\n const actualValue = (actual as Record<string, unknown>)[key];\n if (typeof value === 'object' && value !== null) {\n if (!this.bodyMatches(actualValue, value as Record<string, unknown>)) {\n return false;\n }\n } else if (actualValue !== value) {\n return false;\n }\n }\n\n return true;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// FETCH INTERCEPTOR\n// ═══════════════════════════════════════════════════════════════════\n\nasync function interceptedFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n // Build request info\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = ((init?.method ?? 'GET') as HttpMethod).toUpperCase() as HttpMethod;\n\n // Parse headers\n const headers: Record<string, string> = {};\n if (init?.headers) {\n if (init.headers instanceof Headers) {\n init.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n } else if (Array.isArray(init.headers)) {\n for (const [key, value] of init.headers) {\n headers[key.toLowerCase()] = value;\n }\n } else {\n for (const [key, value] of Object.entries(init.headers)) {\n headers[key.toLowerCase()] = value;\n }\n }\n }\n\n // Parse body\n let body: unknown;\n let rawBody: string | undefined;\n if (init?.body) {\n if (typeof init.body === 'string') {\n rawBody = init.body;\n try {\n body = JSON.parse(init.body);\n } catch {\n body = init.body;\n }\n } else if (init.body instanceof ArrayBuffer || init.body instanceof Uint8Array) {\n rawBody = new TextDecoder().decode(init.body);\n try {\n body = JSON.parse(rawBody);\n } catch {\n body = rawBody;\n }\n } else {\n body = init.body;\n }\n }\n\n const requestInfo: HttpRequestInfo = {\n url,\n method,\n headers,\n body,\n rawBody,\n };\n\n // Try to match against active interceptors (in reverse order - last added first)\n for (let i = activeInterceptors.length - 1; i >= 0; i--) {\n const interceptor = activeInterceptors[i];\n if (!interceptor.isActive()) continue;\n\n const mockResponse = await interceptor.matchRequest(requestInfo);\n if (mockResponse) {\n return mockResponse;\n }\n }\n\n // No match found - check if passthrough is allowed\n for (const interceptor of activeInterceptors) {\n if (interceptor.isActive() && interceptor.canPassthrough()) {\n // Passthrough to real fetch\n if (originalFetch) {\n return originalFetch(input, init);\n }\n }\n }\n\n // No match and no passthrough - throw error\n throw new Error(\n `No HTTP mock found for ${method} ${url}\\n` +\n `Add a mock using httpMock.interceptor().${method.toLowerCase()}('${url}', { body: ... })`,\n );\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// HELPERS\n// ═══════════════════════════════════════════════════════════════════\n\nfunction normalizeResponse(response: HttpMockResponse | Record<string, unknown>): HttpMockResponse {\n // If it looks like a plain object (response body), wrap it\n if (!('status' in response) && !('body' in response) && !('headers' in response)) {\n return { body: response as Record<string, unknown> };\n }\n return response as HttpMockResponse;\n}\n\nfunction createMockResponse(mock: HttpMockResponse): Response {\n // Check for network error - throw instead of returning Response\n // This simulates real network failures where fetch rejects\n const mockAny = mock as HttpMockResponse & { _throwError?: boolean };\n if (mockAny._throwError) {\n const body = mock.body as Record<string, unknown> | undefined;\n const message = body && typeof body === 'object' && 'message' in body ? String(body['message']) : 'Network error';\n throw new TypeError(`fetch failed: ${message}`);\n }\n\n const status = mock.status ?? 200;\n const statusText = mock.statusText ?? 'OK';\n\n // Build headers\n const headers = new Headers(mock.headers ?? {});\n\n // Build body\n let body: BodyInit | null = null;\n if (mock.body !== undefined) {\n if (typeof mock.body === 'string') {\n body = mock.body;\n if (!headers.has('content-type')) {\n headers.set('content-type', 'text/plain');\n }\n } else if (mock.body instanceof ArrayBuffer) {\n body = mock.body;\n } else if (Buffer.isBuffer(mock.body)) {\n // Convert Buffer to ArrayBuffer for Response compatibility\n body = new Uint8Array(mock.body).buffer;\n } else {\n body = JSON.stringify(mock.body);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n }\n }\n\n return new Response(body, { status, statusText, headers });\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// MANAGER IMPLEMENTATION\n// ═══════════════════════════════════════════════════════════════════\n\nclass HttpMockManagerImpl implements HttpMockManager {\n interceptor(): HttpInterceptor {\n // Auto-enable mocking when creating an interceptor\n if (!mockingEnabled) {\n this.enable();\n }\n\n const interceptor = new HttpInterceptorImpl();\n activeInterceptors.push(interceptor);\n return interceptor;\n }\n\n enable(): void {\n if (mockingEnabled) return;\n\n // Store original fetch\n if (typeof globalThis.fetch === 'function') {\n originalFetch = globalThis.fetch;\n globalThis.fetch = interceptedFetch;\n }\n\n mockingEnabled = true;\n }\n\n disable(): void {\n if (!mockingEnabled) return;\n\n // Restore original fetch\n if (originalFetch) {\n globalThis.fetch = originalFetch;\n originalFetch = null;\n }\n\n // Clear all interceptors\n activeInterceptors.length = 0;\n mockingEnabled = false;\n }\n\n isEnabled(): boolean {\n return mockingEnabled;\n }\n\n clearAll(): void {\n for (const interceptor of activeInterceptors) {\n interceptor.clear();\n }\n activeInterceptors.length = 0;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Global HTTP mock manager\n *\n * @example\n * ```typescript\n * import { httpMock } from '@frontmcp/testing';\n *\n * // Create an HTTP interceptor\n * const interceptor = httpMock.interceptor();\n *\n * // Mock requests\n * interceptor.get('https://api.example.com/data', { id: 1, name: 'Test' });\n *\n * // Run tests...\n *\n * // Clean up\n * interceptor.restore();\n * ```\n */\nexport const httpMock: HttpMockManager = new HttpMockManagerImpl();\n\n/**\n * Helper to create mock responses\n */\nexport const httpResponse = {\n /** Create a JSON response */\n json<T>(data: T, status = 200): HttpMockResponse {\n return {\n status,\n headers: { 'content-type': 'application/json' },\n body: data as Record<string, unknown>,\n };\n },\n\n /** Create a text response */\n text(data: string, status = 200): HttpMockResponse {\n return {\n status,\n headers: { 'content-type': 'text/plain' },\n body: data,\n };\n },\n\n /** Create an HTML response */\n html(data: string, status = 200): HttpMockResponse {\n return {\n status,\n headers: { 'content-type': 'text/html' },\n body: data,\n };\n },\n\n /** Create an error response */\n error(status: number, message?: string): HttpMockResponse {\n return {\n status,\n statusText: message ?? getStatusText(status),\n body: message ? { error: message } : undefined,\n };\n },\n\n /** Create a 404 Not Found response */\n notFound(message = 'Not Found'): HttpMockResponse {\n return httpResponse.error(404, message);\n },\n\n /** Create a 500 Internal Server Error response */\n serverError(message = 'Internal Server Error'): HttpMockResponse {\n return httpResponse.error(500, message);\n },\n\n /** Create a 401 Unauthorized response */\n unauthorized(message = 'Unauthorized'): HttpMockResponse {\n return httpResponse.error(401, message);\n },\n\n /** Create a 403 Forbidden response */\n forbidden(message = 'Forbidden'): HttpMockResponse {\n return httpResponse.error(403, message);\n },\n\n /**\n * Create a network error that causes fetch to reject\n * This simulates real network failures where fetch throws instead of returning a Response\n */\n networkError(message = 'Network error'): HttpMockResponse {\n return {\n status: 0,\n body: { _networkError: true, message },\n // Mark this as a network error for the interceptor to throw\n _throwError: true,\n } as HttpMockResponse;\n },\n\n /** Create a delayed response */\n delayed<T>(data: T, delayMs: number, status = 200): HttpMockResponse {\n return {\n status,\n body: data as Record<string, unknown>,\n delay: delayMs,\n };\n },\n};\n\nfunction getStatusText(status: number): string {\n const texts: Record<number, string> = {\n 200: 'OK',\n 201: 'Created',\n 204: 'No Content',\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 500: 'Internal Server Error',\n 502: 'Bad Gateway',\n 503: 'Service Unavailable',\n };\n return texts[status] ?? 'Unknown';\n}\n"]}
@@ -1,10 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file http-mock.types.ts
4
- * @description Types for HTTP request mocking (fetch/XHR interception)
5
- *
6
- * This module allows mocking HTTP requests made by tools during MCP server testing,
7
- * enabling fully offline testing of MCP servers.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- //# sourceMappingURL=http-mock.types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"http-mock.types.js","sourceRoot":"","sources":["../../../src/http-mock/http-mock.types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG","sourcesContent":["/**\n * @file http-mock.types.ts\n * @description Types for HTTP request mocking (fetch/XHR interception)\n *\n * This module allows mocking HTTP requests made by tools during MCP server testing,\n * enabling fully offline testing of MCP servers.\n */\n\n/**\n * HTTP method types\n */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\n\n/**\n * Request matcher - defines which requests to intercept\n */\nexport interface HttpRequestMatcher {\n /** URL pattern - string for exact match, RegExp for pattern, or function for custom */\n url: string | RegExp | ((url: string) => boolean);\n /** HTTP method(s) to match (default: all methods) */\n method?: HttpMethod | HttpMethod[];\n /** Headers that must be present (partial match) */\n headers?: Record<string, string | RegExp>;\n /** Body matcher for POST/PUT/PATCH */\n body?: string | RegExp | Record<string, unknown> | ((body: unknown) => boolean);\n}\n\n/**\n * Mocked HTTP response\n */\nexport interface HttpMockResponse {\n /** HTTP status code (default: 200) */\n status?: number;\n /** Status text (default: 'OK') */\n statusText?: string;\n /** Response headers */\n headers?: Record<string, string>;\n /** Response body - string, object/array (will be JSON.stringify'd), or Buffer */\n body?: string | Record<string, unknown> | unknown[] | Buffer | ArrayBuffer;\n /** Delay before responding in ms */\n delay?: number;\n}\n\n/**\n * HTTP mock definition\n */\nexport interface HttpMockDefinition {\n /** Request matcher */\n match: HttpRequestMatcher;\n /** Response to return */\n response: HttpMockResponse | ((request: HttpRequestInfo) => HttpMockResponse | Promise<HttpMockResponse>);\n /** Number of times to use this mock (default: Infinity) */\n times?: number;\n /** Name for debugging/tracking */\n name?: string;\n}\n\n/**\n * Information about an intercepted HTTP request\n */\nexport interface HttpRequestInfo {\n /** Full URL */\n url: string;\n /** HTTP method */\n method: HttpMethod;\n /** Request headers */\n headers: Record<string, string>;\n /** Request body (parsed if JSON) */\n body?: unknown;\n /** Raw body string */\n rawBody?: string;\n}\n\n/**\n * Handle returned when adding an HTTP mock\n */\nexport interface HttpMockHandle {\n /** Remove this mock */\n remove(): void;\n /** Get call count */\n callCount(): number;\n /** Get all intercepted requests */\n calls(): HttpRequestInfo[];\n /** Wait for a specific number of calls */\n waitForCalls(count: number, timeoutMs?: number): Promise<HttpRequestInfo[]>;\n}\n\n/**\n * HTTP interceptor - controls when mocks are active\n */\nexport interface HttpInterceptor {\n /** Add an HTTP mock */\n mock(definition: HttpMockDefinition): HttpMockHandle;\n\n /** Convenience: mock a GET request */\n get(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle;\n\n /** Convenience: mock a POST request */\n post(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle;\n\n /** Convenience: mock a PUT request */\n put(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle;\n\n /** Convenience: mock a DELETE request */\n delete(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle;\n\n /** Convenience: mock any method */\n any(url: string | RegExp, response: HttpMockResponse | Record<string, unknown>): HttpMockHandle;\n\n /** Clear all mocks in this scope */\n clear(): void;\n\n /** Get all pending (unused) mocks */\n pending(): HttpMockDefinition[];\n\n /** Check if all mocks were used */\n isDone(): boolean;\n\n /** Assert all mocks were used (throws if not) */\n assertDone(): void;\n\n /** Enable passthrough for unmatched requests (default: false, throws error) */\n allowPassthrough(allow: boolean): void;\n\n /** Restore original fetch/XHR */\n restore(): void;\n}\n\n/**\n * Global HTTP mock manager\n */\nexport interface HttpMockManager {\n /** Create a new HTTP interceptor */\n interceptor(): HttpInterceptor;\n\n /** Enable HTTP mocking globally */\n enable(): void;\n\n /** Disable HTTP mocking and restore originals */\n disable(): void;\n\n /** Check if mocking is enabled */\n isEnabled(): boolean;\n\n /** Clear all scopes and mocks */\n clearAll(): void;\n}\n"]}
@@ -1,11 +0,0 @@
1
- "use strict";
2
- /**
3
- * @file index.ts
4
- * @description Barrel exports for HTTP mock module
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.httpResponse = exports.httpMock = void 0;
8
- var http_mock_1 = require("./http-mock");
9
- Object.defineProperty(exports, "httpMock", { enumerable: true, get: function () { return http_mock_1.httpMock; } });
10
- Object.defineProperty(exports, "httpResponse", { enumerable: true, get: function () { return http_mock_1.httpResponse; } });
11
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http-mock/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAAqD;AAA5C,qGAAA,QAAQ,OAAA;AAAE,yGAAA,YAAY,OAAA","sourcesContent":["/**\n * @file index.ts\n * @description Barrel exports for HTTP mock module\n */\n\nexport { httpMock, httpResponse } from './http-mock';\n\nexport type {\n HttpMethod,\n HttpRequestMatcher,\n HttpMockResponse,\n HttpMockDefinition,\n HttpRequestInfo,\n HttpMockHandle,\n HttpInterceptor,\n HttpMockManager,\n} from './http-mock.types';\n"]}