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,44 @@
1
+ import { NextApiRequest } from 'next'
2
+ import { graphql, GraphQLSchema } from 'graphql'
3
+ import { MockResponse } from './types'
4
+
5
+ export function createGraphQLMockHandler(schema: GraphQLSchema) {
6
+ return async (req: NextApiRequest): Promise<MockResponse> => {
7
+ if (req.method !== 'POST') {
8
+ return {
9
+ status: 405,
10
+ data: { errors: [{ message: 'Method not allowed' }] }
11
+ }
12
+ }
13
+
14
+ const { query, variables, operationName } = req.body
15
+
16
+ if (!query) {
17
+ return {
18
+ status: 400,
19
+ data: { errors: [{ message: 'Query is required' }] }
20
+ }
21
+ }
22
+
23
+ try {
24
+ const result = await graphql({
25
+ schema,
26
+ source: query,
27
+ variableValues: variables,
28
+ operationName
29
+ })
30
+
31
+ return {
32
+ status: 200,
33
+ data: result
34
+ }
35
+ } catch (error) {
36
+ console.error('GraphQL execution error:', error)
37
+ return {
38
+ status: 500,
39
+ data: { errors: [{ message: 'Internal server error' }] }
40
+ }
41
+ }
42
+ }
43
+ }
44
+
package/src/index.ts ADDED
@@ -0,0 +1,210 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next'
2
+ import { parse } from 'url'
3
+ import { match } from 'path-to-regexp'
4
+ import { EventEmitter } from 'events'
5
+ import * as winston from 'winston'
6
+ import config from 'config'
7
+ import { MockResponse, MockConfig, RequestLogger, MockInterceptor, MockOptions, MiddlewareFunction, MockFunction } from './types'
8
+ import { createMockDatabase } from './mockDatabase'
9
+ import { createGraphQLMockHandler } from './graphqlMock'
10
+ import { validateConfig } from './configValidator'
11
+ import { applyMiddleware } from './middleware'
12
+ import { cacheResponse, getCachedResponse, clearCache } from './cache'
13
+ import { configureServerMocks, resetServerMocks, mockServerAction, createServerComponentMock } from './serverMock'
14
+ import { initializePlugins, registerPlugin } from './plugins'
15
+ import { setSecureHeaders } from './security'
16
+ import { collectMetrics, reportMetrics } from './monitoring'
17
+ import { createRequestValidator } from './validation'
18
+ import { createRateLimiter } from './rateLimit'
19
+
20
+ const logger = winston.createLogger({
21
+ level: config.get<string>('logLevel') || 'info',
22
+ format: winston.format.json(),
23
+ defaultMeta: { service: 'mock-library' },
24
+ transports: [
25
+ new winston.transports.Console(),
26
+ new winston.transports.File({ filename: 'error.log', level: 'error' }),
27
+ new winston.transports.File({ filename: 'combined.log' }),
28
+ ],
29
+ })
30
+
31
+ const defaultConfig: MockConfig = {}
32
+ const defaultOptions: MockOptions = {
33
+ enableLogging: false,
34
+ cacheTimeout: 5 * 60 * 1000, // 5 minutes
35
+ defaultDelay: 0,
36
+ errorRate: 0,
37
+ }
38
+
39
+ let mockConfig: MockConfig = { ...defaultConfig }
40
+ let mockOptions: MockOptions = { ...defaultOptions }
41
+ let globalDelay = 0
42
+ let requestLogger: RequestLogger | null = null
43
+ let mockInterceptor: MockInterceptor | null = null
44
+ const eventEmitter = new EventEmitter()
45
+ const middlewarePipeline: MiddlewareFunction[] = []
46
+
47
+ export async function configureMocksAsync(configPromise: Promise<MockConfig>, options?: Partial<MockOptions>): Promise<void> {
48
+ try {
49
+ const loadedConfig = await configPromise
50
+ configureMocks(loadedConfig, options)
51
+ } catch (error) {
52
+ logger.error('Error loading configuration:', error)
53
+ throw new Error('Failed to load mock configuration')
54
+ }
55
+ }
56
+
57
+ export function configureMocks(loadedConfig: MockConfig, options?: Partial<MockOptions>): void {
58
+ try {
59
+ validateConfig(loadedConfig)
60
+ mockConfig = { ...defaultConfig, ...loadedConfig }
61
+ mockOptions = { ...defaultOptions, ...options }
62
+ if (mockOptions.enableLogging) {
63
+ setRequestLogger(defaultLogger)
64
+ }
65
+ if (mockOptions.defaultDelay) {
66
+ setGlobalDelay(mockOptions.defaultDelay)
67
+ }
68
+ initializePlugins(mockConfig)
69
+ } catch (error) {
70
+ logger.error('Error configuring mocks:', error)
71
+ throw new Error('Failed to configure mocks')
72
+ }
73
+ }
74
+
75
+ export function resetMocks(): void {
76
+ mockConfig = { ...defaultConfig }
77
+ mockOptions = { ...defaultOptions }
78
+ globalDelay = 0
79
+ requestLogger = null
80
+ mockInterceptor = null
81
+ clearCache()
82
+ resetServerMocks()
83
+ }
84
+
85
+ export function setGlobalDelay(delay: number): void {
86
+ globalDelay = delay
87
+ }
88
+
89
+ export function setRequestLogger(logger: RequestLogger): void {
90
+ requestLogger = logger
91
+ }
92
+
93
+ export function setMockInterceptor(interceptor: MockInterceptor): void {
94
+ mockInterceptor = interceptor
95
+ }
96
+
97
+ export function onMockResponse(listener: (path: string, response: MockResponse<unknown>) => void): () => void {
98
+ eventEmitter.on('mockResponse', listener)
99
+ return () => eventEmitter.off('mockResponse', listener)
100
+ }
101
+
102
+ export function addMiddleware(middleware: MiddlewareFunction): void {
103
+ middlewarePipeline.push(middleware)
104
+ }
105
+
106
+ function defaultLogger(req: NextApiRequest, res: NextApiResponse): void {
107
+ logger.info(`${req.method} ${req.url} - Status: ${res.statusCode}`)
108
+ }
109
+
110
+ function shouldSimulateError(): boolean {
111
+ return Math.random() < mockOptions.errorRate
112
+ }
113
+
114
+ const limiter = createRateLimiter({
115
+ windowMs: 15 * 60 * 1000, // 15 minutes
116
+ max: 100 // limit each IP to 100 requests per windowMs
117
+ })
118
+
119
+ export function createMockHandler(path: string) {
120
+ return limiter(async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
121
+ try {
122
+ setSecureHeaders(res)
123
+
124
+ if (shouldSimulateError()) {
125
+ throw new Error('Simulated server error')
126
+ }
127
+
128
+ for (const middleware of middlewarePipeline) {
129
+ await middleware(req, res)
130
+ }
131
+
132
+ const cacheKey = `${req.method}:${path}:${JSON.stringify(req.query)}:${JSON.stringify(req.body)}`
133
+ const cachedResponse = getCachedResponse(cacheKey)
134
+
135
+ if (cachedResponse) {
136
+ res.status(cachedResponse.status || 200).json(cachedResponse.data)
137
+ return
138
+ }
139
+
140
+ const { pathname } = parse(req.url || '', true)
141
+ const matchFn = match(path, { decode: decodeURIComponent })
142
+
143
+ if (pathname === null) {
144
+ res.status(400).json({ error: 'Invalid URL' })
145
+ return
146
+ }
147
+
148
+ const matched = matchFn(pathname)
149
+
150
+ if (!matched) {
151
+ res.status(404).json({ error: 'Not found' })
152
+ return
153
+ }
154
+
155
+ let mockResponse: MockResponse<unknown>
156
+ const mockConfigItem = mockConfig[path]
157
+
158
+ if (typeof mockConfigItem === 'function') {
159
+ mockResponse = await (mockConfigItem as MockFunction)(req)
160
+ } else {
161
+ mockResponse = mockConfigItem as MockResponse<unknown>
162
+ }
163
+
164
+ if (!mockResponse) {
165
+ res.status(404).json({ error: 'Not found' })
166
+ return
167
+ }
168
+
169
+ mockResponse = await applyMiddleware(req, mockResponse, mockInterceptor)
170
+
171
+ if (requestLogger) {
172
+ requestLogger(req, res)
173
+ }
174
+
175
+ const delay = mockResponse.delay || globalDelay
176
+ if (delay) {
177
+ await new Promise(resolve => setTimeout(resolve, delay))
178
+ }
179
+
180
+ if (mockResponse.headers) {
181
+ Object.entries(mockResponse.headers).forEach(([key, value]) => {
182
+ res.setHeader(key, value as string)
183
+ })
184
+ }
185
+
186
+ eventEmitter.emit('mockResponse', path, mockResponse)
187
+ collectMetrics(req, res, mockResponse)
188
+
189
+ cacheResponse(cacheKey, mockResponse, mockOptions.cacheTimeout)
190
+
191
+ res.status(mockResponse.status || 200).json(mockResponse.data)
192
+ } catch (error) {
193
+ logger.error('Error in mock handler:', error)
194
+ res.status(500).json({ error: 'Internal server error', details: (error as Error).message })
195
+ }
196
+ })
197
+ }
198
+
199
+ export {
200
+ createMockDatabase,
201
+ createGraphQLMockHandler,
202
+ configureServerMocks,
203
+ resetServerMocks,
204
+ mockServerAction,
205
+ createServerComponentMock,
206
+ registerPlugin,
207
+ reportMetrics,
208
+ createRequestValidator
209
+ }
210
+
@@ -0,0 +1,14 @@
1
+ import { NextApiRequest } from 'next'
2
+ import { MockResponse, MockInterceptor } from './types'
3
+
4
+ export async function applyMiddleware(
5
+ req: NextApiRequest,
6
+ response: MockResponse,
7
+ interceptor: MockInterceptor | null
8
+ ): Promise<MockResponse> {
9
+ if (interceptor) {
10
+ return await interceptor(req, response)
11
+ }
12
+ return response
13
+ }
14
+
@@ -0,0 +1,33 @@
1
+ export function createMockDatabase<T extends { id: string | number }>() {
2
+ let data: T[] = []
3
+
4
+ return {
5
+ getAll: () => [...data],
6
+ getById: (id: string | number) => data.find(item => item.id === id),
7
+ create: (item: T) => {
8
+ data.push(item)
9
+ return item
10
+ },
11
+ update: (id: string | number, updates: Partial<T>) => {
12
+ const index = data.findIndex(item => item.id === id)
13
+ if (index !== -1) {
14
+ data[index] = { ...data[index], ...updates }
15
+ return data[index]
16
+ }
17
+ return null
18
+ },
19
+ delete: (id: string | number) => {
20
+ const index = data.findIndex(item => item.id === id)
21
+ if (index !== -1) {
22
+ const deleted = data[index]
23
+ data.splice(index, 1)
24
+ return deleted
25
+ }
26
+ return null
27
+ },
28
+ reset: () => {
29
+ data = []
30
+ },
31
+ query: (predicate: (item: T) => boolean) => data.filter(predicate)
32
+ }
33
+ }
@@ -0,0 +1,47 @@
1
+ import * as prometheus from 'prom-client'
2
+ import { NextApiRequest, NextApiResponse } from 'next'
3
+ import { MockResponse } from './types'
4
+
5
+ const requestCounter = new prometheus.Counter({
6
+ name: 'mock_requests_total',
7
+ help: 'Total number of mock requests',
8
+ labelNames: ['method', 'path', 'status'],
9
+ })
10
+
11
+ const responseTimeHistogram = new prometheus.Histogram({
12
+ name: 'mock_response_time_seconds',
13
+ help: 'Response time in seconds',
14
+ labelNames: ['method', 'path'],
15
+ })
16
+
17
+ /**
18
+ * Collects metrics for a mock request.
19
+ * @param req The incoming request.
20
+ * @param res The outgoing response.
21
+ * @param mockResponse The mock response.
22
+ */
23
+ export function collectMetrics(req: NextApiRequest, res: NextApiResponse, mockResponse: MockResponse): void {
24
+ const labels = {
25
+ method: req.method || 'UNKNOWN',
26
+ path: req.url || 'UNKNOWN',
27
+ status: mockResponse.status.toString(),
28
+ }
29
+
30
+ requestCounter.inc(labels)
31
+
32
+ const responseTime = process.hrtime()
33
+ res.on('finish', () => {
34
+ const [seconds, nanoseconds] = process.hrtime(responseTime)
35
+ const duration = seconds + nanoseconds / 1e9
36
+ responseTimeHistogram.observe({ method: labels.method, path: labels.path }, duration)
37
+ })
38
+ }
39
+
40
+ /**
41
+ * Reports the collected metrics.
42
+ * @returns A Promise that resolves to a string representation of the collected metrics.
43
+ */
44
+ export async function reportMetrics(): Promise<string> {
45
+ return await prometheus.register.metrics()
46
+ }
47
+
package/src/plugins.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { MockConfig, Plugin } from './types'
2
+ const plugins: Plugin[] = []
3
+ /**
4
+ * Registers a new plugin.
5
+ * @param plugin The plugin to register.
6
+ */
7
+ export function registerPlugin(plugin: Plugin): void {
8
+ plugins.push(plugin)
9
+ }
10
+ /**
11
+ * Initializes all registered plugins with the provided configuration.
12
+ * @param config The mock configuration.
13
+ */
14
+ export function initializePlugins(config: MockConfig): void {
15
+ plugins.forEach(plugin => plugin.initialize(config))
16
+ }
@@ -0,0 +1,26 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next'
2
+ import { rateLimit, Options } from 'express-rate-limit'
3
+ import { Request, Response, NextFunction } from 'express'
4
+
5
+ type NextApiHandler = (req: NextApiRequest, res: NextApiResponse) => Promise<void>
6
+
7
+ export function createRateLimiter(options: Partial<Options>) {
8
+ const limiter = rateLimit(options)
9
+
10
+ return function rateLimitMiddleware(handler: NextApiHandler): NextApiHandler {
11
+ return async (req: NextApiRequest, res: NextApiResponse) => {
12
+ const expressReq = req as unknown as Request
13
+ const expressRes = res as unknown as Response
14
+
15
+ return new Promise<void>((resolve, reject) => {
16
+ limiter(expressReq, expressRes, (result: any) => {
17
+ if (result instanceof Error) {
18
+ return reject(result)
19
+ }
20
+ return resolve(handler(req, res))
21
+ })
22
+ })
23
+ }
24
+ }
25
+ }
26
+
@@ -0,0 +1,42 @@
1
+ import { NextApiResponse } from 'next'
2
+ import helmet from 'helmet'
3
+ import { IncomingMessage, ServerResponse } from 'http'
4
+
5
+ /**
6
+ * Sets secure headers on the response using helmet.
7
+ * @param res The Next.js API response object.
8
+ */
9
+ export function setSecureHeaders(res: NextApiResponse): void {
10
+ const helmetMiddleware = helmet({
11
+ contentSecurityPolicy: {
12
+ directives: {
13
+ defaultSrc: ["'self'"],
14
+ scriptSrc: ["'self'", "'unsafe-inline'"],
15
+ styleSrc: ["'self'", "'unsafe-inline'"],
16
+ imgSrc: ["'self'", "data:", "https:"],
17
+ },
18
+ },
19
+ referrerPolicy: {
20
+ policy: 'strict-origin-when-cross-origin',
21
+ },
22
+ })
23
+
24
+ // Create a mock request object
25
+ const mockReq = {} as IncomingMessage
26
+
27
+ // Cast NextApiResponse to ServerResponse
28
+ const nodeRes = res as unknown as ServerResponse
29
+
30
+ // Define a named function to handle potential errors
31
+ const handleHelmetError = (err?: unknown): void => {
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
+
39
+ // Apply helmet middleware with error handling
40
+ helmetMiddleware(mockReq, nodeRes, handleHelmetError)
41
+ }
42
+
@@ -0,0 +1,127 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { MockResponse, MockConfig, ServerComponentMock } from './types'
3
+
4
+ let serverMockConfig: MockConfig = {}
5
+ let mockBaseUrl = 'https://example.com'
6
+
7
+ export function configureServerMocks(config: MockConfig, baseUrl?: string): void {
8
+ serverMockConfig = { ...serverMockConfig, ...config }
9
+ if (baseUrl) {
10
+ mockBaseUrl = baseUrl
11
+ }
12
+ }
13
+
14
+ export function resetServerMocks(): void {
15
+ serverMockConfig = {}
16
+ mockBaseUrl = 'https://example.com'
17
+ }
18
+
19
+ export async function mockServerAction<TArgs extends unknown[], TReturn>(
20
+ actionName: string,
21
+ ...args: TArgs
22
+ ): Promise<TReturn> {
23
+ const mockConfig = serverMockConfig[actionName]
24
+ if (typeof mockConfig === 'function') {
25
+ const mockRequest = await createMockNextRequest({
26
+ method: 'POST',
27
+ body: JSON.stringify(args),
28
+ })
29
+
30
+ const result = await mockConfig(mockRequest)
31
+ return result.data as TReturn
32
+ } else if (mockConfig) {
33
+ return mockConfig.data as TReturn
34
+ }
35
+ throw new Error(`No mock configured for server action: ${actionName}`)
36
+ }
37
+
38
+ export function createServerComponentMock<TProps>(
39
+ componentName: string
40
+ ): ServerComponentMock<TProps, Promise<NextResponse>> {
41
+ return async (props: TProps): Promise<NextResponse> => {
42
+ const mockConfig = serverMockConfig[componentName]
43
+ if (typeof mockConfig === 'function') {
44
+ const searchParams = new URLSearchParams(props as Record<string, string>)
45
+ const mockRequest = await createMockNextRequest({
46
+ method: 'GET',
47
+ url: `${mockBaseUrl}?${searchParams.toString()}`
48
+ })
49
+
50
+ const mockResponse = await mockConfig(mockRequest)
51
+ return createMockNextResponse(mockResponse)
52
+ } else if (mockConfig) {
53
+ return createMockNextResponse(mockConfig as MockResponse)
54
+ }
55
+ throw new Error(`No mock configured for server component: ${componentName}`)
56
+ }
57
+ }
58
+
59
+ async function createMockNextRequest(options: {
60
+ method?: string;
61
+ url?: string;
62
+ headers?: HeadersInit;
63
+ body?: BodyInit;
64
+ }): Promise<NextRequest> {
65
+ const url = options.url || mockBaseUrl
66
+
67
+ const init: RequestInit = {
68
+ method: options.method || 'GET',
69
+ headers: options.headers,
70
+ body: options.body,
71
+ }
72
+
73
+ const request = new Request(url, init)
74
+ const nextRequest = new NextRequest(request)
75
+
76
+ Object.defineProperties(nextRequest, {
77
+ geo: {
78
+ value: {
79
+ city: 'Mock City',
80
+ country: 'Mock Country',
81
+ region: 'Mock Region',
82
+ latitude: '0',
83
+ longitude: '0'
84
+ },
85
+ writable: true
86
+ },
87
+ ip: {
88
+ value: '127.0.0.1',
89
+ writable: true
90
+ }
91
+ })
92
+
93
+ return nextRequest
94
+ }
95
+
96
+ function createMockNextResponse(mockResponse: MockResponse): NextResponse {
97
+ const { data, status = 200, headers = {} } = mockResponse
98
+
99
+ const bodyContent = typeof data === 'string' ? data : JSON.stringify(data)
100
+
101
+ const stream = new ReadableStream({
102
+ start(controller) {
103
+ controller.enqueue(new TextEncoder().encode(bodyContent))
104
+ controller.close()
105
+ }
106
+ })
107
+
108
+ const response = new Response(stream, {
109
+ status,
110
+ headers: new Headers(headers)
111
+ })
112
+
113
+ return new NextResponse(response.body, {
114
+ status,
115
+ headers: new Headers(headers),
116
+ url: mockBaseUrl
117
+ })
118
+ }
119
+
120
+ export interface TypedMockResponse<T> extends MockResponse {
121
+ data: T
122
+ }
123
+
124
+ export function createTypedMock<T>(response: TypedMockResponse<T>): TypedMockResponse<T> {
125
+ return response
126
+ }
127
+
package/src/types.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { NextApiRequest, NextApiResponse } from 'next'
2
+ import { NextRequest, NextResponse } from 'next/server'
3
+
4
+ export type MockResponse<T = unknown> = {
5
+ status: number
6
+ data: T
7
+ delay?: number
8
+ headers?: Record<string, string>
9
+ }
10
+
11
+ export type MockFunction = (req: NextApiRequest | NextRequest) => MockResponse<unknown> | Promise<MockResponse<unknown>>
12
+
13
+ export type MockConfig = {
14
+ [key: string]: MockResponse<unknown> | MockFunction
15
+ }
16
+
17
+ export type RequestLogger = (req: NextApiRequest, res: NextApiResponse) => void
18
+
19
+ export type MockInterceptor = (req: NextApiRequest | NextRequest, mockResponse: MockResponse<unknown>) => MockResponse<unknown> | Promise<MockResponse<unknown>>
20
+
21
+ export type ServerActionMock<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn
22
+
23
+ export type ServerComponentMock<TProps, TReturn = Promise<NextResponse>> = (props: TProps) => TReturn
24
+
25
+ export type MockOptions = {
26
+ enableLogging: boolean
27
+ cacheTimeout: number
28
+ defaultDelay: number
29
+ errorRate: number
30
+ }
31
+
32
+ export type MiddlewareFunction = (req: NextApiRequest, res: NextApiResponse) => Promise<void>
33
+
34
+ export type Plugin = {
35
+ name: string
36
+ initialize: (config: MockConfig) => void
37
+ beforeRequest?: (req: NextApiRequest | NextRequest) => void
38
+ afterResponse?: (req: NextApiRequest | NextRequest, response: MockResponse<unknown>) => void
39
+ errorHandler?: (error: Error, req: NextApiRequest | NextRequest) => MockResponse<unknown>
40
+ transformResponse?: (response: MockResponse<unknown>) => MockResponse<unknown>
41
+ validateConfig?: (config: MockConfig) => boolean
42
+ cleanup?: () => void
43
+ }
44
+
@@ -0,0 +1,19 @@
1
+ import * as Joi from 'joi'
2
+ import { NextApiRequest, NextApiResponse } from 'next'
3
+
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
+ export function createRequestValidator(schema: Joi.ObjectSchema) {
10
+ return (req: NextApiRequest, res: NextApiResponse, next: () => void) => {
11
+ const { error } = schema.validate(req.body)
12
+ if (error) {
13
+ res.status(400).json({ error: 'Invalid request', details: error.details })
14
+ } else {
15
+ next()
16
+ }
17
+ }
18
+ }
19
+
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "plugins": [
18
+ {
19
+ "name": "next"
20
+ }
21
+ ],
22
+ "paths": {
23
+ "@/*": ["./*"]
24
+ }
25
+ },
26
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27
+ "exclude": ["node_modules"]
28
+ }
29
+