next-api-mock 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/.eslintrc.js +19 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  3. package/.idea/misc.xml +17 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/next-api-mock.iml +9 -0
  6. package/.idea/vcs.xml +6 -0
  7. package/README.md +34 -0
  8. package/__tests__/configValidator.test.ts +69 -0
  9. package/__tests__/graphqlMock.test.ts +53 -0
  10. package/dist/cache.d.ts +19 -0
  11. package/dist/cache.d.ts.map +1 -0
  12. package/dist/cache.js +37 -0
  13. package/dist/configValidator.d.ts +8 -0
  14. package/dist/configValidator.d.ts.map +1 -0
  15. package/dist/configValidator.js +23 -0
  16. package/dist/graphqlMock.d.ts +5 -0
  17. package/dist/graphqlMock.d.ts.map +1 -0
  18. package/dist/graphqlMock.js +49 -0
  19. package/dist/index.d.ts +19 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +242 -0
  22. package/dist/middleware.d.ts +4 -0
  23. package/dist/middleware.d.ts.map +1 -0
  24. package/dist/middleware.js +20 -0
  25. package/dist/mockDatabase.d.ts +12 -0
  26. package/dist/mockDatabase.d.ts.map +1 -0
  27. package/dist/mockDatabase.js +35 -0
  28. package/dist/monitoring.d.ts +15 -0
  29. package/dist/monitoring.d.ts.map +1 -0
  30. package/dist/monitoring.js +86 -0
  31. package/dist/plugins.d.ts +12 -0
  32. package/dist/plugins.d.ts.map +1 -0
  33. package/dist/plugins.js +19 -0
  34. package/dist/security.d.ts +7 -0
  35. package/dist/security.d.ts.map +1 -0
  36. package/dist/security.js +40 -0
  37. package/dist/serverMock.d.ts +11 -0
  38. package/dist/serverMock.d.ts.map +1 -0
  39. package/dist/serverMock.js +115 -0
  40. package/dist/types.d.ts +34 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +2 -0
  43. package/dist/validation.d.ts +9 -0
  44. package/dist/validation.d.ts.map +1 -0
  45. package/dist/validation.js +19 -0
  46. package/jest.config.js +13 -0
  47. package/package.json +57 -0
  48. package/src/cache.ts +36 -0
  49. package/src/configValidator.ts +23 -0
  50. package/src/graphqlMock.ts +44 -0
  51. package/src/index.ts +210 -0
  52. package/src/middleware.ts +14 -0
  53. package/src/mockDatabase.ts +33 -0
  54. package/src/monitoring.ts +47 -0
  55. package/src/plugins.ts +16 -0
  56. package/src/rateLimit.ts +26 -0
  57. package/src/security.ts +42 -0
  58. package/src/serverMock.ts +127 -0
  59. package/src/types.ts +44 -0
  60. package/src/validation.ts +19 -0
  61. package/tsconfig.json +29 -0
  62. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,4 @@
1
+ import { NextApiRequest } from 'next';
2
+ import { MockResponse, MockInterceptor } from './types';
3
+ export declare function applyMiddleware(req: NextApiRequest, response: MockResponse, interceptor: MockInterceptor | null): Promise<MockResponse>;
4
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAEvD,wBAAsB,eAAe,CACjC,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,eAAe,GAAG,IAAI,GACpC,OAAO,CAAC,YAAY,CAAC,CAKvB"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.applyMiddleware = applyMiddleware;
13
+ function applyMiddleware(req, response, interceptor) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ if (interceptor) {
16
+ return yield interceptor(req, response);
17
+ }
18
+ return response;
19
+ });
20
+ }
@@ -0,0 +1,12 @@
1
+ export declare function createMockDatabase<T extends {
2
+ id: string | number;
3
+ }>(): {
4
+ getAll: () => T[];
5
+ getById: (id: string | number) => T | undefined;
6
+ create: (item: T) => T;
7
+ update: (id: string | number, updates: Partial<T>) => T | null;
8
+ delete: (id: string | number) => T | null;
9
+ reset: () => void;
10
+ query: (predicate: (item: T) => boolean) => T[];
11
+ };
12
+ //# sourceMappingURL=mockDatabase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockDatabase.d.ts","sourceRoot":"","sources":["../src/mockDatabase.ts"],"names":[],"mappings":"AAAA,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE;;kBAK9C,MAAM,GAAG,MAAM;mBACd,CAAC;iBAIH,MAAM,GAAG,MAAM,WAAW,OAAO,CAAC,CAAC,CAAC;iBAQpC,MAAM,GAAG,MAAM;;uBAYT,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO;EAE9C"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMockDatabase = createMockDatabase;
4
+ function createMockDatabase() {
5
+ let data = [];
6
+ return {
7
+ getAll: () => [...data],
8
+ getById: (id) => data.find(item => item.id === id),
9
+ create: (item) => {
10
+ data.push(item);
11
+ return item;
12
+ },
13
+ update: (id, updates) => {
14
+ const index = data.findIndex(item => item.id === id);
15
+ if (index !== -1) {
16
+ data[index] = Object.assign(Object.assign({}, data[index]), updates);
17
+ return data[index];
18
+ }
19
+ return null;
20
+ },
21
+ delete: (id) => {
22
+ const index = data.findIndex(item => item.id === id);
23
+ if (index !== -1) {
24
+ const deleted = data[index];
25
+ data.splice(index, 1);
26
+ return deleted;
27
+ }
28
+ return null;
29
+ },
30
+ reset: () => {
31
+ data = [];
32
+ },
33
+ query: (predicate) => data.filter(predicate)
34
+ };
35
+ }
@@ -0,0 +1,15 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next';
2
+ import { MockResponse } from './types';
3
+ /**
4
+ * Collects metrics for a mock request.
5
+ * @param req The incoming request.
6
+ * @param res The outgoing response.
7
+ * @param mockResponse The mock response.
8
+ */
9
+ export declare function collectMetrics(req: NextApiRequest, res: NextApiResponse, mockResponse: MockResponse): void;
10
+ /**
11
+ * Reports the collected metrics.
12
+ * @returns A Promise that resolves to a string representation of the collected metrics.
13
+ */
14
+ export declare function reportMetrics(): Promise<string>;
15
+ //# sourceMappingURL=monitoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring.d.ts","sourceRoot":"","sources":["../src/monitoring.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AActC;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAe1G;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAErD"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.collectMetrics = collectMetrics;
46
+ exports.reportMetrics = reportMetrics;
47
+ const prometheus = __importStar(require("prom-client"));
48
+ const requestCounter = new prometheus.Counter({
49
+ name: 'mock_requests_total',
50
+ help: 'Total number of mock requests',
51
+ labelNames: ['method', 'path', 'status'],
52
+ });
53
+ const responseTimeHistogram = new prometheus.Histogram({
54
+ name: 'mock_response_time_seconds',
55
+ help: 'Response time in seconds',
56
+ labelNames: ['method', 'path'],
57
+ });
58
+ /**
59
+ * Collects metrics for a mock request.
60
+ * @param req The incoming request.
61
+ * @param res The outgoing response.
62
+ * @param mockResponse The mock response.
63
+ */
64
+ function collectMetrics(req, res, mockResponse) {
65
+ const labels = {
66
+ method: req.method || 'UNKNOWN',
67
+ path: req.url || 'UNKNOWN',
68
+ status: mockResponse.status.toString(),
69
+ };
70
+ requestCounter.inc(labels);
71
+ const responseTime = process.hrtime();
72
+ res.on('finish', () => {
73
+ const [seconds, nanoseconds] = process.hrtime(responseTime);
74
+ const duration = seconds + nanoseconds / 1e9;
75
+ responseTimeHistogram.observe({ method: labels.method, path: labels.path }, duration);
76
+ });
77
+ }
78
+ /**
79
+ * Reports the collected metrics.
80
+ * @returns A Promise that resolves to a string representation of the collected metrics.
81
+ */
82
+ function reportMetrics() {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ return yield prometheus.register.metrics();
85
+ });
86
+ }
@@ -0,0 +1,12 @@
1
+ import { MockConfig, Plugin } from './types';
2
+ /**
3
+ * Registers a new plugin.
4
+ * @param plugin The plugin to register.
5
+ */
6
+ export declare function registerPlugin(plugin: Plugin): void;
7
+ /**
8
+ * Initializes all registered plugins with the provided configuration.
9
+ * @param config The mock configuration.
10
+ */
11
+ export declare function initializePlugins(config: MockConfig): void;
12
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAE5C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEnD;AACD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAE1D"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPlugin = registerPlugin;
4
+ exports.initializePlugins = initializePlugins;
5
+ const plugins = [];
6
+ /**
7
+ * Registers a new plugin.
8
+ * @param plugin The plugin to register.
9
+ */
10
+ function registerPlugin(plugin) {
11
+ plugins.push(plugin);
12
+ }
13
+ /**
14
+ * Initializes all registered plugins with the provided configuration.
15
+ * @param config The mock configuration.
16
+ */
17
+ function initializePlugins(config) {
18
+ plugins.forEach(plugin => plugin.initialize(config));
19
+ }
@@ -0,0 +1,7 @@
1
+ import { NextApiResponse } from 'next';
2
+ /**
3
+ * Sets secure headers on the response using helmet.
4
+ * @param res The Next.js API response object.
5
+ */
6
+ export declare function setSecureHeaders(res: NextApiResponse): void;
7
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAItC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAgC3D"}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.setSecureHeaders = setSecureHeaders;
7
+ const helmet_1 = __importDefault(require("helmet"));
8
+ /**
9
+ * Sets secure headers on the response using helmet.
10
+ * @param res The Next.js API response object.
11
+ */
12
+ function setSecureHeaders(res) {
13
+ const helmetMiddleware = (0, helmet_1.default)({
14
+ contentSecurityPolicy: {
15
+ directives: {
16
+ defaultSrc: ["'self'"],
17
+ scriptSrc: ["'self'", "'unsafe-inline'"],
18
+ styleSrc: ["'self'", "'unsafe-inline'"],
19
+ imgSrc: ["'self'", "data:", "https:"],
20
+ },
21
+ },
22
+ referrerPolicy: {
23
+ policy: 'strict-origin-when-cross-origin',
24
+ },
25
+ });
26
+ // Create a mock request object
27
+ const mockReq = {};
28
+ // Cast NextApiResponse to ServerResponse
29
+ const nodeRes = res;
30
+ // Define a named function to handle potential errors
31
+ const handleHelmetError = (err) => {
32
+ if (err) {
33
+ console.error('Helmet middleware error:', err);
34
+ // You might want to set a default security header if Helmet fails
35
+ res.setHeader('X-Content-Type-Options', 'nosniff');
36
+ }
37
+ };
38
+ // Apply helmet middleware with error handling
39
+ helmetMiddleware(mockReq, nodeRes, handleHelmetError);
40
+ }
@@ -0,0 +1,11 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { MockResponse, MockConfig, ServerComponentMock } from './types';
3
+ export declare function configureServerMocks(config: MockConfig, baseUrl?: string): void;
4
+ export declare function resetServerMocks(): void;
5
+ export declare function mockServerAction<TArgs extends unknown[], TReturn>(actionName: string, ...args: TArgs): Promise<TReturn>;
6
+ export declare function createServerComponentMock<TProps>(componentName: string): ServerComponentMock<TProps, Promise<NextResponse>>;
7
+ export interface TypedMockResponse<T> extends MockResponse {
8
+ data: T;
9
+ }
10
+ export declare function createTypedMock<T>(response: TypedMockResponse<T>): TypedMockResponse<T>;
11
+ //# sourceMappingURL=serverMock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverMock.d.ts","sourceRoot":"","sources":["../src/serverMock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAKvE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAK/E;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC;AAED,wBAAsB,gBAAgB,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACnE,UAAU,EAAE,MAAM,EAClB,GAAG,IAAI,EAAE,KAAK,GACf,OAAO,CAAC,OAAO,CAAC,CAclB;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAC5C,aAAa,EAAE,MAAM,GACtB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAiBpD;AA+DD,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,YAAY;IACtD,IAAI,EAAE,CAAC,CAAA;CACV;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAEvF"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.configureServerMocks = configureServerMocks;
13
+ exports.resetServerMocks = resetServerMocks;
14
+ exports.mockServerAction = mockServerAction;
15
+ exports.createServerComponentMock = createServerComponentMock;
16
+ exports.createTypedMock = createTypedMock;
17
+ const server_1 = require("next/server");
18
+ let serverMockConfig = {};
19
+ let mockBaseUrl = 'https://example.com';
20
+ function configureServerMocks(config, baseUrl) {
21
+ serverMockConfig = Object.assign(Object.assign({}, serverMockConfig), config);
22
+ if (baseUrl) {
23
+ mockBaseUrl = baseUrl;
24
+ }
25
+ }
26
+ function resetServerMocks() {
27
+ serverMockConfig = {};
28
+ mockBaseUrl = 'https://example.com';
29
+ }
30
+ function mockServerAction(actionName, ...args) {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ const mockConfig = serverMockConfig[actionName];
33
+ if (typeof mockConfig === 'function') {
34
+ const mockRequest = yield createMockNextRequest({
35
+ method: 'POST',
36
+ body: JSON.stringify(args),
37
+ });
38
+ const result = yield mockConfig(mockRequest);
39
+ return result.data;
40
+ }
41
+ else if (mockConfig) {
42
+ return mockConfig.data;
43
+ }
44
+ throw new Error(`No mock configured for server action: ${actionName}`);
45
+ });
46
+ }
47
+ function createServerComponentMock(componentName) {
48
+ return (props) => __awaiter(this, void 0, void 0, function* () {
49
+ const mockConfig = serverMockConfig[componentName];
50
+ if (typeof mockConfig === 'function') {
51
+ const searchParams = new URLSearchParams(props);
52
+ const mockRequest = yield createMockNextRequest({
53
+ method: 'GET',
54
+ url: `${mockBaseUrl}?${searchParams.toString()}`
55
+ });
56
+ const mockResponse = yield mockConfig(mockRequest);
57
+ return createMockNextResponse(mockResponse);
58
+ }
59
+ else if (mockConfig) {
60
+ return createMockNextResponse(mockConfig);
61
+ }
62
+ throw new Error(`No mock configured for server component: ${componentName}`);
63
+ });
64
+ }
65
+ function createMockNextRequest(options) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ const url = options.url || mockBaseUrl;
68
+ const init = {
69
+ method: options.method || 'GET',
70
+ headers: options.headers,
71
+ body: options.body,
72
+ };
73
+ const request = new Request(url, init);
74
+ const nextRequest = new server_1.NextRequest(request);
75
+ Object.defineProperties(nextRequest, {
76
+ geo: {
77
+ value: {
78
+ city: 'Mock City',
79
+ country: 'Mock Country',
80
+ region: 'Mock Region',
81
+ latitude: '0',
82
+ longitude: '0'
83
+ },
84
+ writable: true
85
+ },
86
+ ip: {
87
+ value: '127.0.0.1',
88
+ writable: true
89
+ }
90
+ });
91
+ return nextRequest;
92
+ });
93
+ }
94
+ function createMockNextResponse(mockResponse) {
95
+ const { data, status = 200, headers = {} } = mockResponse;
96
+ const bodyContent = typeof data === 'string' ? data : JSON.stringify(data);
97
+ const stream = new ReadableStream({
98
+ start(controller) {
99
+ controller.enqueue(new TextEncoder().encode(bodyContent));
100
+ controller.close();
101
+ }
102
+ });
103
+ const response = new Response(stream, {
104
+ status,
105
+ headers: new Headers(headers)
106
+ });
107
+ return new server_1.NextResponse(response.body, {
108
+ status,
109
+ headers: new Headers(headers),
110
+ url: mockBaseUrl
111
+ });
112
+ }
113
+ function createTypedMock(response) {
114
+ return response;
115
+ }
@@ -0,0 +1,34 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ export type MockResponse<T = unknown> = {
4
+ status: number;
5
+ data: T;
6
+ delay?: number;
7
+ headers?: Record<string, string>;
8
+ };
9
+ export type MockFunction = (req: NextApiRequest | NextRequest) => MockResponse<unknown> | Promise<MockResponse<unknown>>;
10
+ export type MockConfig = {
11
+ [key: string]: MockResponse<unknown> | MockFunction;
12
+ };
13
+ export type RequestLogger = (req: NextApiRequest, res: NextApiResponse) => void;
14
+ export type MockInterceptor = (req: NextApiRequest | NextRequest, mockResponse: MockResponse<unknown>) => MockResponse<unknown> | Promise<MockResponse<unknown>>;
15
+ export type ServerActionMock<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn;
16
+ export type ServerComponentMock<TProps, TReturn = Promise<NextResponse>> = (props: TProps) => TReturn;
17
+ export type MockOptions = {
18
+ enableLogging: boolean;
19
+ cacheTimeout: number;
20
+ defaultDelay: number;
21
+ errorRate: number;
22
+ };
23
+ export type MiddlewareFunction = (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
24
+ export type Plugin = {
25
+ name: string;
26
+ initialize: (config: MockConfig) => void;
27
+ beforeRequest?: (req: NextApiRequest | NextRequest) => void;
28
+ afterResponse?: (req: NextApiRequest | NextRequest, response: MockResponse<unknown>) => void;
29
+ errorHandler?: (error: Error, req: NextApiRequest | NextRequest) => MockResponse<unknown>;
30
+ transformResponse?: (response: MockResponse<unknown>) => MockResponse<unknown>;
31
+ validateConfig?: (config: MockConfig) => boolean;
32
+ cleanup?: () => void;
33
+ };
34
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEvD,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,WAAW,KAAK,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;AAExH,MAAM,MAAM,UAAU,GAAG;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,YAAY,CAAA;CACtD,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;AAE/E,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;AAEhK,MAAM,MAAM,gBAAgB,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAA;AAE5F,MAAM,MAAM,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;AAErG,MAAM,MAAM,WAAW,GAAG;IACtB,aAAa,EAAE,OAAO,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE7F,MAAM,MAAM,MAAM,GAAG;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IACxC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,WAAW,KAAK,IAAI,CAAA;IAC3D,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IAC5F,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,GAAG,WAAW,KAAK,YAAY,CAAC,OAAO,CAAC,CAAA;IACzF,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,YAAY,CAAC,OAAO,CAAC,CAAA;IAC9E,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAA;IAChD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,9 @@
1
+ import * as Joi from 'joi';
2
+ import { NextApiRequest, NextApiResponse } from 'next';
3
+ /**
4
+ * Creates a request validator middleware using the provided Joi schema.
5
+ * @param schema The Joi schema to validate against.
6
+ * @returns A middleware function that validates incoming requests.
7
+ */
8
+ export declare function createRequestValidator(schema: Joi.ObjectSchema): (req: NextApiRequest, res: NextApiResponse, next: () => void) => void;
9
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,KAAK,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAEtD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,SAC9C,cAAc,OAAO,eAAe,QAAQ,MAAM,IAAI,UAQtE"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRequestValidator = createRequestValidator;
4
+ /**
5
+ * Creates a request validator middleware using the provided Joi schema.
6
+ * @param schema The Joi schema to validate against.
7
+ * @returns A middleware function that validates incoming requests.
8
+ */
9
+ function createRequestValidator(schema) {
10
+ return (req, res, next) => {
11
+ const { error } = schema.validate(req.body);
12
+ if (error) {
13
+ res.status(400).json({ error: 'Invalid request', details: error.details });
14
+ }
15
+ else {
16
+ next();
17
+ }
18
+ };
19
+ }
package/jest.config.js ADDED
@@ -0,0 +1,13 @@
1
+ /** @type {import('jest').Config} */
2
+ const config = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ moduleDirectories: ['node_modules', 'src'],
6
+ transform: {
7
+ '^.+\\.tsx?$': 'ts-jest',
8
+ },
9
+ testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
10
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
11
+ };
12
+
13
+ module.exports = config;
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "next-api-mock",
3
+ "version": "1.0.0",
4
+ "description": "A production-ready, scalable tool for mocking API and GraphQL responses in Next.js applications",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "lint": "eslint 'src/**/*.ts'",
11
+ "prepublishOnly": "npm run lint && npm run test && npm run build"
12
+ },
13
+ "keywords": [
14
+ "next.js",
15
+ "api",
16
+ "mock",
17
+ "testing",
18
+ "development",
19
+ "graphql",
20
+ "production-ready"
21
+ ],
22
+ "author": "Arjun Sharda",
23
+ "license": "MIT",
24
+ "peerDependencies": {
25
+ "next": "^15.0.3",
26
+ "react": "^18.3.1",
27
+ "react-dom": "^18.3.1"
28
+ },
29
+ "dependencies": {
30
+ "config": "^3.3.12",
31
+ "express-rate-limit": "^7.4.1",
32
+ "graphql": "^16.6.0",
33
+ "helmet": "^8.0.0",
34
+ "joi": "^17.13.3",
35
+ "lru-cache": "^7.18.3",
36
+ "path-to-regexp": "^6.2.1",
37
+ "prom-client": "^15.1.3",
38
+ "winston": "^3.17.0"
39
+ },
40
+ "devDependencies": {
41
+ "@eslint/js": "^9.15.0",
42
+ "@types/config": "^3.3.3",
43
+ "@types/express": "^5.0.0",
44
+ "@types/jest": "^29.5.0",
45
+ "@types/lru-cache": "^7.10.10",
46
+ "@types/node": "^18.15.0",
47
+ "@types/react": "^18.0.28",
48
+ "@types/react-dom": "^18.3.1",
49
+ "@typescript-eslint/eslint-plugin": "^5.59.0",
50
+ "@typescript-eslint/parser": "^5.59.0",
51
+ "eslint": "^8.38.0",
52
+ "jest": "^29.5.0",
53
+ "ts-jest": "^29.1.0",
54
+ "typescript": "^5.0.4",
55
+ "typescript-eslint": "^0.0.1-alpha.0"
56
+ }
57
+ }
package/src/cache.ts ADDED
@@ -0,0 +1,36 @@
1
+ import LRU from 'lru-cache'
2
+ import { MockResponse } from './types'
3
+
4
+ const options = {
5
+ max: 500,
6
+ ttl: 1000 * 60 * 5 // 5 minutes
7
+ }
8
+
9
+ const cache = new LRU<string, MockResponse>(options)
10
+
11
+ /**
12
+ * Caches a mock response.
13
+ * @param key The cache key.
14
+ * @param value The mock response to cache.
15
+ * @param ttl The time-to-live for the cache entry in milliseconds.
16
+ */
17
+ export function cacheResponse(key: string, value: MockResponse, ttl?: number): void {
18
+ cache.set(key, value, ttl ? { ttl } : undefined)
19
+ }
20
+
21
+ /**
22
+ * Retrieves a cached mock response.
23
+ * @param key The cache key.
24
+ * @returns The cached mock response, or undefined if not found.
25
+ */
26
+ export function getCachedResponse(key: string): MockResponse | undefined {
27
+ return cache.get(key)
28
+ }
29
+
30
+ /**
31
+ * Clears the entire cache.
32
+ */
33
+ export function clearCache(): void {
34
+ cache.clear()
35
+ }
36
+
@@ -0,0 +1,23 @@
1
+ import { MockConfig } from './types'
2
+
3
+ /**
4
+ * Validates the provided mock configuration.
5
+ * @param config The mock configuration to validate.
6
+ * @throws Error if the configuration is invalid.
7
+ */
8
+ export function validateConfig(config: MockConfig): void {
9
+ Object.entries(config).forEach(([path, value]) => {
10
+ if (typeof value !== 'function' && typeof value !== 'object') {
11
+ throw new Error(`Invalid configuration for path ${path}. Expected function or object, got ${typeof value}`)
12
+ }
13
+ if (typeof value === 'object' && value !== null) {
14
+ if (!Object.prototype.hasOwnProperty.call(value, 'data')) {
15
+ throw new Error(`Invalid configuration for path ${path}. Missing 'data' property`)
16
+ }
17
+ if (!Object.prototype.hasOwnProperty.call(value, 'status')) {
18
+ throw new Error(`Invalid configuration for path ${path}. Missing 'status' property`)
19
+ }
20
+ }
21
+ })
22
+ }
23
+