next-api-mock 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc.js +19 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/misc.xml +17 -0
- package/.idea/modules.xml +8 -0
- package/.idea/next-api-mock.iml +9 -0
- package/.idea/vcs.xml +6 -0
- package/README.md +34 -0
- package/__tests__/configValidator.test.ts +69 -0
- package/__tests__/graphqlMock.test.ts +53 -0
- package/dist/cache.d.ts +19 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +37 -0
- package/dist/configValidator.d.ts +8 -0
- package/dist/configValidator.d.ts.map +1 -0
- package/dist/configValidator.js +23 -0
- package/dist/graphqlMock.d.ts +5 -0
- package/dist/graphqlMock.d.ts.map +1 -0
- package/dist/graphqlMock.js +49 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +242 -0
- package/dist/middleware.d.ts +4 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +20 -0
- package/dist/mockDatabase.d.ts +12 -0
- package/dist/mockDatabase.d.ts.map +1 -0
- package/dist/mockDatabase.js +35 -0
- package/dist/monitoring.d.ts +15 -0
- package/dist/monitoring.d.ts.map +1 -0
- package/dist/monitoring.js +86 -0
- package/dist/plugins.d.ts +12 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +19 -0
- package/dist/security.d.ts +7 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +40 -0
- package/dist/serverMock.d.ts +11 -0
- package/dist/serverMock.d.ts.map +1 -0
- package/dist/serverMock.js +115 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/validation.d.ts +9 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +19 -0
- package/jest.config.js +13 -0
- package/package.json +57 -0
- package/src/cache.ts +36 -0
- package/src/configValidator.ts +23 -0
- package/src/graphqlMock.ts +44 -0
- package/src/index.ts +210 -0
- package/src/middleware.ts +14 -0
- package/src/mockDatabase.ts +33 -0
- package/src/monitoring.ts +47 -0
- package/src/plugins.ts +16 -0
- package/src/rateLimit.ts +26 -0
- package/src/security.ts +42 -0
- package/src/serverMock.ts +127 -0
- package/src/types.ts +44 -0
- package/src/validation.ts +19 -0
- package/tsconfig.json +29 -0
- package/tsconfig.tsbuildinfo +1 -0
package/.eslintrc.js
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module.exports = {
|
2
|
+
root: true,
|
3
|
+
parser: '@typescript-eslint/parser',
|
4
|
+
plugins: ['@typescript-eslint'],
|
5
|
+
extends: [
|
6
|
+
'eslint:recommended',
|
7
|
+
'plugin:@typescript-eslint/recommended',
|
8
|
+
],
|
9
|
+
env: {
|
10
|
+
node: true,
|
11
|
+
jest: true,
|
12
|
+
},
|
13
|
+
settings: {
|
14
|
+
react: {
|
15
|
+
version: 'detect',
|
16
|
+
},
|
17
|
+
},
|
18
|
+
};
|
19
|
+
|
package/.idea/misc.xml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="DiscordProjectSettings">
|
4
|
+
<option name="show" value="ASK" />
|
5
|
+
<option name="description" value="" />
|
6
|
+
<option name="applicationTheme" value="default" />
|
7
|
+
<option name="iconsTheme" value="default" />
|
8
|
+
<option name="button1Title" value="" />
|
9
|
+
<option name="button1Url" value="" />
|
10
|
+
<option name="button2Title" value="" />
|
11
|
+
<option name="button2Url" value="" />
|
12
|
+
<option name="customApplicationId" value="" />
|
13
|
+
</component>
|
14
|
+
<component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="18" project-jdk-type="JavaSDK">
|
15
|
+
<output url="file://$PROJECT_DIR$/out" />
|
16
|
+
</component>
|
17
|
+
</project>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="ProjectModuleManager">
|
4
|
+
<modules>
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/next-api-mock.iml" filepath="$PROJECT_DIR$/.idea/next-api-mock.iml" />
|
6
|
+
</modules>
|
7
|
+
</component>
|
8
|
+
</project>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module type="JAVA_MODULE" version="4">
|
3
|
+
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
4
|
+
<exclude-output />
|
5
|
+
<content url="file://$MODULE_DIR$" />
|
6
|
+
<orderEntry type="inheritedJdk" />
|
7
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
8
|
+
</component>
|
9
|
+
</module>
|
package/.idea/vcs.xml
ADDED
package/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
<div align="center">
|
2
|
+
|
3
|
+
# 🎭 next-api-mock
|
4
|
+
|
5
|
+
[![npm version](https://img.shields.io/npm/v/next-api-mock.svg?style=flat-square)](https://www.npmjs.com/package/next-api-mock)
|
6
|
+
[![license](https://img.shields.io/npm/l/next-api-mock.svg?style=flat-square)](https://github.com/yourusername/next-api-mock/blob/main/LICENSE)
|
7
|
+
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
|
8
|
+
|
9
|
+
A powerful, flexible, and production-ready API mocking library for Next.js applications.
|
10
|
+
|
11
|
+
[Features](#-features) •
|
12
|
+
[Installation](#-installation) •
|
13
|
+
[Quick Start](#-quick-start) •
|
14
|
+
[Documentation](#-documentation) •
|
15
|
+
[Contributing](#-contributing)
|
16
|
+
|
17
|
+
</div>
|
18
|
+
|
19
|
+
## 🚀 Features
|
20
|
+
|
21
|
+
- 🔧 Easy configuration and setup
|
22
|
+
- 🔄 Support for dynamic and conditional responses
|
23
|
+
- 📊 Built-in pagination handling
|
24
|
+
- 🚦 Simulated network conditions (delay, errors)
|
25
|
+
- 🔍 GraphQL mocking support
|
26
|
+
- 🖥️ Server Component and Server Action mocking
|
27
|
+
- 🧩 Flexible middleware and interceptors
|
28
|
+
- 🗄️ Efficient caching mechanism
|
29
|
+
|
30
|
+
## 📦 Installation
|
31
|
+
|
32
|
+
```bash
|
33
|
+
npm install next-api-mock
|
34
|
+
```
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import { validateConfig } from '../src/configValidator'
|
2
|
+
import { MockConfig, MockFunction } from '../src/types'
|
3
|
+
import { NextApiRequest } from 'next'
|
4
|
+
import { NextRequest } from 'next/server'
|
5
|
+
|
6
|
+
describe('Config Validator', () => {
|
7
|
+
it('should pass for valid configurations', () => {
|
8
|
+
const validConfig: MockConfig = {
|
9
|
+
'/api/test': {
|
10
|
+
status: 200,
|
11
|
+
data: { message: 'Test successful' },
|
12
|
+
},
|
13
|
+
'/api/function': ((req: NextApiRequest | NextRequest) => ({
|
14
|
+
status: 200,
|
15
|
+
data: { method: req.method },
|
16
|
+
})) as MockFunction,
|
17
|
+
}
|
18
|
+
expect(() => validateConfig(validConfig)).not.toThrow()
|
19
|
+
})
|
20
|
+
|
21
|
+
it('should throw for invalid value types', () => {
|
22
|
+
const invalidConfig = {
|
23
|
+
'/api/test': 'not an object or function',
|
24
|
+
} as unknown as MockConfig;
|
25
|
+
expect(() => validateConfig(invalidConfig)).toThrow(/Invalid configuration for path \/api\/test/)
|
26
|
+
})
|
27
|
+
|
28
|
+
it('should throw for missing data property', () => {
|
29
|
+
const invalidConfig = {
|
30
|
+
'/api/test': {
|
31
|
+
status: 200,
|
32
|
+
},
|
33
|
+
} as unknown as MockConfig;
|
34
|
+
expect(() => validateConfig(invalidConfig)).toThrow(/Invalid configuration for path \/api\/test/)
|
35
|
+
})
|
36
|
+
|
37
|
+
it('should throw for missing status property', () => {
|
38
|
+
const invalidConfig = {
|
39
|
+
'/api/test': {
|
40
|
+
data: { message: 'Test' },
|
41
|
+
},
|
42
|
+
} as unknown as MockConfig;
|
43
|
+
expect(() => validateConfig(invalidConfig)).toThrow(/Invalid configuration for path \/api\/test/)
|
44
|
+
})
|
45
|
+
|
46
|
+
it('should throw for partially valid configurations', () => {
|
47
|
+
const partiallyValidConfig = {
|
48
|
+
'/api/valid': {
|
49
|
+
status: 200,
|
50
|
+
data: { message: 'Valid' },
|
51
|
+
},
|
52
|
+
'/api/invalid': {
|
53
|
+
status: 200,
|
54
|
+
},
|
55
|
+
} as unknown as MockConfig;
|
56
|
+
expect(() => validateConfig(partiallyValidConfig)).toThrow(/Invalid configuration for path \/api\/invalid/)
|
57
|
+
})
|
58
|
+
|
59
|
+
it('should pass for valid function configurations', () => {
|
60
|
+
const validConfig: MockConfig = {
|
61
|
+
'/api/function': ((req: NextApiRequest | NextRequest) => ({
|
62
|
+
status: 200,
|
63
|
+
data: { method: req.method },
|
64
|
+
})) as MockFunction,
|
65
|
+
}
|
66
|
+
expect(() => validateConfig(validConfig)).not.toThrow()
|
67
|
+
})
|
68
|
+
})
|
69
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { createGraphQLMockHandler } from '../src/graphqlMock'
|
2
|
+
import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'
|
3
|
+
import { NextApiRequest } from 'next'
|
4
|
+
|
5
|
+
describe('GraphQL Mock Handler', () => {
|
6
|
+
const schema = new GraphQLSchema({
|
7
|
+
query: new GraphQLObjectType({
|
8
|
+
name: 'Query',
|
9
|
+
fields: {
|
10
|
+
hello: {
|
11
|
+
type: GraphQLString,
|
12
|
+
resolve: () => 'Hello, world!',
|
13
|
+
},
|
14
|
+
},
|
15
|
+
}),
|
16
|
+
})
|
17
|
+
|
18
|
+
const handler = createGraphQLMockHandler(schema)
|
19
|
+
|
20
|
+
it('should return 405 for non-POST requests', async () => {
|
21
|
+
const req = { method: 'GET' } as NextApiRequest
|
22
|
+
const result = await handler(req)
|
23
|
+
expect(result.status).toBe(405)
|
24
|
+
expect(result.data).toEqual({ errors: [{ message: 'Method not allowed' }] })
|
25
|
+
})
|
26
|
+
|
27
|
+
it('should return 400 if query is missing', async () => {
|
28
|
+
const req = { method: 'POST', body: {} } as NextApiRequest
|
29
|
+
const result = await handler(req)
|
30
|
+
expect(result.status).toBe(400)
|
31
|
+
expect(result.data).toEqual({ errors: [{ message: 'Query is required' }] })
|
32
|
+
})
|
33
|
+
|
34
|
+
it('should execute a valid GraphQL query', async () => {
|
35
|
+
const req = {
|
36
|
+
method: 'POST',
|
37
|
+
body: { query: '{ hello }' },
|
38
|
+
} as NextApiRequest
|
39
|
+
const result = await handler(req)
|
40
|
+
expect(result.status).toBe(200)
|
41
|
+
expect(result.data).toEqual({ data: { hello: 'Hello, world!' } })
|
42
|
+
})
|
43
|
+
|
44
|
+
it('should handle GraphQL errors', async () => {
|
45
|
+
const req = {
|
46
|
+
method: 'POST',
|
47
|
+
body: { query: '{ invalidField }' },
|
48
|
+
} as NextApiRequest
|
49
|
+
const result = await handler(req)
|
50
|
+
expect(result.status).toBe(200)
|
51
|
+
expect(result.data).toHaveProperty('errors')
|
52
|
+
})
|
53
|
+
})
|
package/dist/cache.d.ts
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
import { MockResponse } from './types';
|
2
|
+
/**
|
3
|
+
* Caches a mock response.
|
4
|
+
* @param key The cache key.
|
5
|
+
* @param value The mock response to cache.
|
6
|
+
* @param ttl The time-to-live for the cache entry in milliseconds.
|
7
|
+
*/
|
8
|
+
export declare function cacheResponse(key: string, value: MockResponse, ttl?: number): void;
|
9
|
+
/**
|
10
|
+
* Retrieves a cached mock response.
|
11
|
+
* @param key The cache key.
|
12
|
+
* @returns The cached mock response, or undefined if not found.
|
13
|
+
*/
|
14
|
+
export declare function getCachedResponse(key: string): MockResponse | undefined;
|
15
|
+
/**
|
16
|
+
* Clears the entire cache.
|
17
|
+
*/
|
18
|
+
export declare function clearCache(): void;
|
19
|
+
//# sourceMappingURL=cache.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAStC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAElF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEvE;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
|
package/dist/cache.js
ADDED
@@ -0,0 +1,37 @@
|
|
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.cacheResponse = cacheResponse;
|
7
|
+
exports.getCachedResponse = getCachedResponse;
|
8
|
+
exports.clearCache = clearCache;
|
9
|
+
const lru_cache_1 = __importDefault(require("lru-cache"));
|
10
|
+
const options = {
|
11
|
+
max: 500,
|
12
|
+
ttl: 1000 * 60 * 5 // 5 minutes
|
13
|
+
};
|
14
|
+
const cache = new lru_cache_1.default(options);
|
15
|
+
/**
|
16
|
+
* Caches a mock response.
|
17
|
+
* @param key The cache key.
|
18
|
+
* @param value The mock response to cache.
|
19
|
+
* @param ttl The time-to-live for the cache entry in milliseconds.
|
20
|
+
*/
|
21
|
+
function cacheResponse(key, value, ttl) {
|
22
|
+
cache.set(key, value, ttl ? { ttl } : undefined);
|
23
|
+
}
|
24
|
+
/**
|
25
|
+
* Retrieves a cached mock response.
|
26
|
+
* @param key The cache key.
|
27
|
+
* @returns The cached mock response, or undefined if not found.
|
28
|
+
*/
|
29
|
+
function getCachedResponse(key) {
|
30
|
+
return cache.get(key);
|
31
|
+
}
|
32
|
+
/**
|
33
|
+
* Clears the entire cache.
|
34
|
+
*/
|
35
|
+
function clearCache() {
|
36
|
+
cache.clear();
|
37
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { MockConfig } from './types';
|
2
|
+
/**
|
3
|
+
* Validates the provided mock configuration.
|
4
|
+
* @param config The mock configuration to validate.
|
5
|
+
* @throws Error if the configuration is invalid.
|
6
|
+
*/
|
7
|
+
export declare function validateConfig(config: MockConfig): void;
|
8
|
+
//# sourceMappingURL=configValidator.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"configValidator.d.ts","sourceRoot":"","sources":["../src/configValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEpC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAcvD"}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.validateConfig = validateConfig;
|
4
|
+
/**
|
5
|
+
* Validates the provided mock configuration.
|
6
|
+
* @param config The mock configuration to validate.
|
7
|
+
* @throws Error if the configuration is invalid.
|
8
|
+
*/
|
9
|
+
function validateConfig(config) {
|
10
|
+
Object.entries(config).forEach(([path, value]) => {
|
11
|
+
if (typeof value !== 'function' && typeof value !== 'object') {
|
12
|
+
throw new Error(`Invalid configuration for path ${path}. Expected function or object, got ${typeof value}`);
|
13
|
+
}
|
14
|
+
if (typeof value === 'object' && value !== null) {
|
15
|
+
if (!Object.prototype.hasOwnProperty.call(value, 'data')) {
|
16
|
+
throw new Error(`Invalid configuration for path ${path}. Missing 'data' property`);
|
17
|
+
}
|
18
|
+
if (!Object.prototype.hasOwnProperty.call(value, 'status')) {
|
19
|
+
throw new Error(`Invalid configuration for path ${path}. Missing 'status' property`);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
});
|
23
|
+
}
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { NextApiRequest } from 'next';
|
2
|
+
import { GraphQLSchema } from 'graphql';
|
3
|
+
import { MockResponse } from './types';
|
4
|
+
export declare function createGraphQLMockHandler(schema: GraphQLSchema): (req: NextApiRequest) => Promise<MockResponse>;
|
5
|
+
//# sourceMappingURL=graphqlMock.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"graphqlMock.d.ts","sourceRoot":"","sources":["../src/graphqlMock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AACrC,OAAO,EAAW,aAAa,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEtC,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,SACvC,cAAc,KAAG,OAAO,CAAC,YAAY,CAAC,CAqC5D"}
|
@@ -0,0 +1,49 @@
|
|
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.createGraphQLMockHandler = createGraphQLMockHandler;
|
13
|
+
const graphql_1 = require("graphql");
|
14
|
+
function createGraphQLMockHandler(schema) {
|
15
|
+
return (req) => __awaiter(this, void 0, void 0, function* () {
|
16
|
+
if (req.method !== 'POST') {
|
17
|
+
return {
|
18
|
+
status: 405,
|
19
|
+
data: { errors: [{ message: 'Method not allowed' }] }
|
20
|
+
};
|
21
|
+
}
|
22
|
+
const { query, variables, operationName } = req.body;
|
23
|
+
if (!query) {
|
24
|
+
return {
|
25
|
+
status: 400,
|
26
|
+
data: { errors: [{ message: 'Query is required' }] }
|
27
|
+
};
|
28
|
+
}
|
29
|
+
try {
|
30
|
+
const result = yield (0, graphql_1.graphql)({
|
31
|
+
schema,
|
32
|
+
source: query,
|
33
|
+
variableValues: variables,
|
34
|
+
operationName
|
35
|
+
});
|
36
|
+
return {
|
37
|
+
status: 200,
|
38
|
+
data: result
|
39
|
+
};
|
40
|
+
}
|
41
|
+
catch (error) {
|
42
|
+
console.error('GraphQL execution error:', error);
|
43
|
+
return {
|
44
|
+
status: 500,
|
45
|
+
data: { errors: [{ message: 'Internal server error' }] }
|
46
|
+
};
|
47
|
+
}
|
48
|
+
});
|
49
|
+
}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
import { NextApiRequest, NextApiResponse } from 'next';
|
2
|
+
import { MockResponse, MockConfig, RequestLogger, MockInterceptor, MockOptions, MiddlewareFunction } from './types';
|
3
|
+
import { createMockDatabase } from './mockDatabase';
|
4
|
+
import { createGraphQLMockHandler } from './graphqlMock';
|
5
|
+
import { configureServerMocks, resetServerMocks, mockServerAction, createServerComponentMock } from './serverMock';
|
6
|
+
import { registerPlugin } from './plugins';
|
7
|
+
import { reportMetrics } from './monitoring';
|
8
|
+
import { createRequestValidator } from './validation';
|
9
|
+
export declare function configureMocksAsync(configPromise: Promise<MockConfig>, options?: Partial<MockOptions>): Promise<void>;
|
10
|
+
export declare function configureMocks(loadedConfig: MockConfig, options?: Partial<MockOptions>): void;
|
11
|
+
export declare function resetMocks(): void;
|
12
|
+
export declare function setGlobalDelay(delay: number): void;
|
13
|
+
export declare function setRequestLogger(logger: RequestLogger): void;
|
14
|
+
export declare function setMockInterceptor(interceptor: MockInterceptor): void;
|
15
|
+
export declare function onMockResponse(listener: (path: string, response: MockResponse<unknown>) => void): () => void;
|
16
|
+
export declare function addMiddleware(middleware: MiddlewareFunction): void;
|
17
|
+
export declare function createMockHandler(path: string): (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
18
|
+
export { createMockDatabase, createGraphQLMockHandler, configureServerMocks, resetServerMocks, mockServerAction, createServerComponentMock, registerPlugin, reportMetrics, createRequestValidator };
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAOtD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAgB,MAAM,SAAS,CAAA;AACjI,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAIxD,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAClH,OAAO,EAAqB,cAAc,EAAE,MAAM,WAAW,CAAA;AAE7D,OAAO,EAAkB,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AA6BrD,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3H;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAgB7F;AAED,wBAAgB,UAAU,IAAI,IAAI,CAQjC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,eAAe,GAAG,IAAI,CAErE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAG5G;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAElE;AAeD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,SACvB,cAAc,OAAO,eAAe,KAAG,OAAO,CAAC,IAAI,CAAC,CAsF1E;AAED,OAAO,EACH,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,aAAa,EACb,sBAAsB,EACzB,CAAA"}
|
package/dist/index.js
ADDED
@@ -0,0 +1,242 @@
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
46
|
+
};
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
48
|
+
exports.createRequestValidator = exports.reportMetrics = exports.registerPlugin = exports.createServerComponentMock = exports.mockServerAction = exports.resetServerMocks = exports.configureServerMocks = exports.createGraphQLMockHandler = exports.createMockDatabase = void 0;
|
49
|
+
exports.configureMocksAsync = configureMocksAsync;
|
50
|
+
exports.configureMocks = configureMocks;
|
51
|
+
exports.resetMocks = resetMocks;
|
52
|
+
exports.setGlobalDelay = setGlobalDelay;
|
53
|
+
exports.setRequestLogger = setRequestLogger;
|
54
|
+
exports.setMockInterceptor = setMockInterceptor;
|
55
|
+
exports.onMockResponse = onMockResponse;
|
56
|
+
exports.addMiddleware = addMiddleware;
|
57
|
+
exports.createMockHandler = createMockHandler;
|
58
|
+
const url_1 = require("url");
|
59
|
+
const path_to_regexp_1 = require("path-to-regexp");
|
60
|
+
const events_1 = require("events");
|
61
|
+
const winston = __importStar(require("winston"));
|
62
|
+
const config_1 = __importDefault(require("config"));
|
63
|
+
const express_rate_limit_1 = require("express-rate-limit");
|
64
|
+
const mockDatabase_1 = require("./mockDatabase");
|
65
|
+
Object.defineProperty(exports, "createMockDatabase", { enumerable: true, get: function () { return mockDatabase_1.createMockDatabase; } });
|
66
|
+
const graphqlMock_1 = require("./graphqlMock");
|
67
|
+
Object.defineProperty(exports, "createGraphQLMockHandler", { enumerable: true, get: function () { return graphqlMock_1.createGraphQLMockHandler; } });
|
68
|
+
const configValidator_1 = require("./configValidator");
|
69
|
+
const middleware_1 = require("./middleware");
|
70
|
+
const cache_1 = require("./cache");
|
71
|
+
const serverMock_1 = require("./serverMock");
|
72
|
+
Object.defineProperty(exports, "configureServerMocks", { enumerable: true, get: function () { return serverMock_1.configureServerMocks; } });
|
73
|
+
Object.defineProperty(exports, "resetServerMocks", { enumerable: true, get: function () { return serverMock_1.resetServerMocks; } });
|
74
|
+
Object.defineProperty(exports, "mockServerAction", { enumerable: true, get: function () { return serverMock_1.mockServerAction; } });
|
75
|
+
Object.defineProperty(exports, "createServerComponentMock", { enumerable: true, get: function () { return serverMock_1.createServerComponentMock; } });
|
76
|
+
const plugins_1 = require("./plugins");
|
77
|
+
Object.defineProperty(exports, "registerPlugin", { enumerable: true, get: function () { return plugins_1.registerPlugin; } });
|
78
|
+
const security_1 = require("./security");
|
79
|
+
const monitoring_1 = require("./monitoring");
|
80
|
+
Object.defineProperty(exports, "reportMetrics", { enumerable: true, get: function () { return monitoring_1.reportMetrics; } });
|
81
|
+
const validation_1 = require("./validation");
|
82
|
+
Object.defineProperty(exports, "createRequestValidator", { enumerable: true, get: function () { return validation_1.createRequestValidator; } });
|
83
|
+
const logger = winston.createLogger({
|
84
|
+
level: config_1.default.get('logLevel') || 'info',
|
85
|
+
format: winston.format.json(),
|
86
|
+
defaultMeta: { service: 'mock-library' },
|
87
|
+
transports: [
|
88
|
+
new winston.transports.Console(),
|
89
|
+
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
90
|
+
new winston.transports.File({ filename: 'combined.log' }),
|
91
|
+
],
|
92
|
+
});
|
93
|
+
const defaultConfig = {};
|
94
|
+
const defaultOptions = {
|
95
|
+
enableLogging: false,
|
96
|
+
cacheTimeout: 5 * 60 * 1000, // 5 minutes
|
97
|
+
defaultDelay: 0,
|
98
|
+
errorRate: 0,
|
99
|
+
};
|
100
|
+
let mockConfig = Object.assign({}, defaultConfig);
|
101
|
+
let mockOptions = Object.assign({}, defaultOptions);
|
102
|
+
let globalDelay = 0;
|
103
|
+
let requestLogger = null;
|
104
|
+
let mockInterceptor = null;
|
105
|
+
const eventEmitter = new events_1.EventEmitter();
|
106
|
+
const middlewarePipeline = [];
|
107
|
+
function configureMocksAsync(configPromise, options) {
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
109
|
+
try {
|
110
|
+
const loadedConfig = yield configPromise;
|
111
|
+
configureMocks(loadedConfig, options);
|
112
|
+
}
|
113
|
+
catch (error) {
|
114
|
+
logger.error('Error loading configuration:', error);
|
115
|
+
throw new Error('Failed to load mock configuration');
|
116
|
+
}
|
117
|
+
});
|
118
|
+
}
|
119
|
+
function configureMocks(loadedConfig, options) {
|
120
|
+
try {
|
121
|
+
(0, configValidator_1.validateConfig)(loadedConfig);
|
122
|
+
mockConfig = Object.assign(Object.assign({}, defaultConfig), loadedConfig);
|
123
|
+
mockOptions = Object.assign(Object.assign({}, defaultOptions), options);
|
124
|
+
if (mockOptions.enableLogging) {
|
125
|
+
setRequestLogger(defaultLogger);
|
126
|
+
}
|
127
|
+
if (mockOptions.defaultDelay) {
|
128
|
+
setGlobalDelay(mockOptions.defaultDelay);
|
129
|
+
}
|
130
|
+
(0, plugins_1.initializePlugins)(mockConfig);
|
131
|
+
}
|
132
|
+
catch (error) {
|
133
|
+
logger.error('Error configuring mocks:', error);
|
134
|
+
throw new Error('Failed to configure mocks');
|
135
|
+
}
|
136
|
+
}
|
137
|
+
function resetMocks() {
|
138
|
+
mockConfig = Object.assign({}, defaultConfig);
|
139
|
+
mockOptions = Object.assign({}, defaultOptions);
|
140
|
+
globalDelay = 0;
|
141
|
+
requestLogger = null;
|
142
|
+
mockInterceptor = null;
|
143
|
+
(0, cache_1.clearCache)();
|
144
|
+
(0, serverMock_1.resetServerMocks)();
|
145
|
+
}
|
146
|
+
function setGlobalDelay(delay) {
|
147
|
+
globalDelay = delay;
|
148
|
+
}
|
149
|
+
function setRequestLogger(logger) {
|
150
|
+
requestLogger = logger;
|
151
|
+
}
|
152
|
+
function setMockInterceptor(interceptor) {
|
153
|
+
mockInterceptor = interceptor;
|
154
|
+
}
|
155
|
+
function onMockResponse(listener) {
|
156
|
+
eventEmitter.on('mockResponse', listener);
|
157
|
+
return () => eventEmitter.off('mockResponse', listener);
|
158
|
+
}
|
159
|
+
function addMiddleware(middleware) {
|
160
|
+
middlewarePipeline.push(middleware);
|
161
|
+
}
|
162
|
+
function defaultLogger(req, res) {
|
163
|
+
logger.info(`${req.method} ${req.url} - Status: ${res.statusCode}`);
|
164
|
+
}
|
165
|
+
function shouldSimulateError() {
|
166
|
+
return Math.random() < mockOptions.errorRate;
|
167
|
+
}
|
168
|
+
const limiter = (0, express_rate_limit_1.rateLimit)({
|
169
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
170
|
+
max: 100 // limit each IP to 100 requests per windowMs
|
171
|
+
});
|
172
|
+
function createMockHandler(path) {
|
173
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
174
|
+
try {
|
175
|
+
(0, security_1.setSecureHeaders)(res);
|
176
|
+
// Fix: Replace empty arrow function with a named function
|
177
|
+
const rateLimitHandler = (err) => {
|
178
|
+
if (err) {
|
179
|
+
logger.error('Rate limit error:', err);
|
180
|
+
res.status(429).json({ error: 'Too many requests' });
|
181
|
+
}
|
182
|
+
};
|
183
|
+
limiter(req, res, rateLimitHandler);
|
184
|
+
if (shouldSimulateError()) {
|
185
|
+
throw new Error('Simulated server error');
|
186
|
+
}
|
187
|
+
for (const middleware of middlewarePipeline) {
|
188
|
+
yield middleware(req, res);
|
189
|
+
}
|
190
|
+
const cacheKey = `${req.method}:${path}:${JSON.stringify(req.query)}:${JSON.stringify(req.body)}`;
|
191
|
+
const cachedResponse = (0, cache_1.getCachedResponse)(cacheKey);
|
192
|
+
if (cachedResponse) {
|
193
|
+
res.status(cachedResponse.status || 200).json(cachedResponse.data);
|
194
|
+
return;
|
195
|
+
}
|
196
|
+
const { pathname } = (0, url_1.parse)(req.url || '', true);
|
197
|
+
const matchFn = (0, path_to_regexp_1.match)(path, { decode: decodeURIComponent });
|
198
|
+
if (pathname === null) {
|
199
|
+
res.status(400).json({ error: 'Invalid URL' });
|
200
|
+
return;
|
201
|
+
}
|
202
|
+
const matched = matchFn(pathname);
|
203
|
+
if (!matched) {
|
204
|
+
res.status(404).json({ error: 'Not found' });
|
205
|
+
return;
|
206
|
+
}
|
207
|
+
let mockResponse;
|
208
|
+
const mockConfigItem = mockConfig[path];
|
209
|
+
if (typeof mockConfigItem === 'function') {
|
210
|
+
mockResponse = yield mockConfigItem(req);
|
211
|
+
}
|
212
|
+
else {
|
213
|
+
mockResponse = mockConfigItem;
|
214
|
+
}
|
215
|
+
if (!mockResponse) {
|
216
|
+
res.status(404).json({ error: 'Not found' });
|
217
|
+
return;
|
218
|
+
}
|
219
|
+
mockResponse = yield (0, middleware_1.applyMiddleware)(req, mockResponse, mockInterceptor);
|
220
|
+
if (requestLogger) {
|
221
|
+
requestLogger(req, res);
|
222
|
+
}
|
223
|
+
const delay = mockResponse.delay || globalDelay;
|
224
|
+
if (delay) {
|
225
|
+
yield new Promise(resolve => setTimeout(resolve, delay));
|
226
|
+
}
|
227
|
+
if (mockResponse.headers) {
|
228
|
+
Object.entries(mockResponse.headers).forEach(([key, value]) => {
|
229
|
+
res.setHeader(key, value);
|
230
|
+
});
|
231
|
+
}
|
232
|
+
eventEmitter.emit('mockResponse', path, mockResponse);
|
233
|
+
(0, monitoring_1.collectMetrics)(req, res, mockResponse);
|
234
|
+
(0, cache_1.cacheResponse)(cacheKey, mockResponse, mockOptions.cacheTimeout);
|
235
|
+
res.status(mockResponse.status || 200).json(mockResponse.data);
|
236
|
+
}
|
237
|
+
catch (error) {
|
238
|
+
logger.error('Error in mock handler:', error);
|
239
|
+
res.status(500).json({ error: 'Internal server error', details: error.message });
|
240
|
+
}
|
241
|
+
});
|
242
|
+
}
|