Skip to content

Commit 209b65c

Browse files
committed
fix: add handler input validation to handlers controller
1 parent d22e69d commit 209b65c

File tree

5 files changed

+113
-62
lines changed

5 files changed

+113
-62
lines changed
Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,82 @@
1+
import { invariant } from 'outvariant'
12
import { RequestHandler } from '../handlers/RequestHandler'
23
import { WebSocketHandler } from '../handlers/WebSocketHandler'
4+
import { devUtils } from '../utils/internal/devUtils'
35

46
export type AnyHandler = RequestHandler | WebSocketHandler
57

6-
export interface HandlersController<Handler extends AnyHandler = AnyHandler> {
7-
currentHandlers(): Array<Handler>
8-
use(nextHandlers: Array<Handler>): void
9-
reset(nextHandlers: Array<Handler>): void
8+
export type HandlersControllerFunction<Controller extends HandlersController> =
9+
(initialHandlers: Array<AnyHandler>) => Controller
10+
11+
export interface HandlersController {
12+
currentHandlers(): Array<AnyHandler>
13+
use(nextHandlers: Array<AnyHandler>): void
14+
reset(nextHandlers: Array<AnyHandler>): void
15+
}
16+
17+
export function validateHandlers(handlers: Array<AnyHandler>): boolean {
18+
return handlers.every((handler) => !Array.isArray(handler))
19+
}
20+
21+
export function defineHandlersController<
22+
Controller extends HandlersController = HandlersController,
23+
>(
24+
init: HandlersControllerFunction<Controller>,
25+
): HandlersControllerFunction<Controller> {
26+
return (initialHandlers) => {
27+
invariant(
28+
validateHandlers(initialHandlers),
29+
devUtils.formatMessage(
30+
'Failed to create a handlers controller: invalid handlers. Did you forget to spread the handlers array?',
31+
),
32+
)
33+
34+
const api = init(initialHandlers)
35+
36+
return {
37+
...api,
38+
use(nextHandlers) {
39+
invariant(
40+
validateHandlers(nextHandlers),
41+
devUtils.formatMessage(
42+
'Failed to prepend runtime handlers: invalid handlers. Did you forget to spread the handlers array?',
43+
),
44+
)
45+
46+
return api.use(nextHandlers)
47+
},
48+
reset(nextHandlers) {
49+
invariant(
50+
nextHandlers.length > 0 ? validateHandlers(nextHandlers) : true,
51+
devUtils.formatMessage(
52+
'Failed to replace initial handlers during reset: invalid handlers. Did you forget to spread the handlers array?',
53+
),
54+
)
55+
56+
return api.reset(nextHandlers)
57+
},
58+
}
59+
}
1060
}
1161

1262
/**
1363
* A simple handler controller that keeps the list of handlers in memory.
1464
*/
15-
export function inMemoryHandlersController<Handler extends AnyHandler>(
16-
initialHandlers: Array<Handler>,
17-
): HandlersController<Handler> {
18-
let handlers = [...initialHandlers]
19-
20-
return {
21-
currentHandlers() {
22-
return handlers
23-
},
24-
use(nextHandlers) {
25-
handlers.unshift(...nextHandlers)
26-
},
27-
reset(nextHandlers) {
28-
handlers =
29-
nextHandlers.length > 0 ? [...nextHandlers] : [...initialHandlers]
30-
},
31-
}
32-
}
65+
export const inMemoryHandlersController = defineHandlersController(
66+
(initialHandlers) => {
67+
let handlers = [...initialHandlers]
68+
69+
return {
70+
currentHandlers() {
71+
return handlers
72+
},
73+
use(nextHandlers) {
74+
handlers.unshift(...nextHandlers)
75+
},
76+
reset(nextHandlers) {
77+
handlers =
78+
nextHandlers.length > 0 ? [...nextHandlers] : [...initialHandlers]
79+
},
80+
}
81+
},
82+
)
Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,46 @@
11
import { AsyncLocalStorage } from 'node:async_hooks'
2-
import { HandlersController, AnyHandler } from '~/core/new/handlers-controller'
2+
import {
3+
HandlersController,
4+
AnyHandler,
5+
defineHandlersController,
6+
} from '~/core/new/handlers-controller'
37

4-
export interface AsyncHandlersController<Handler extends AnyHandler>
5-
extends HandlersController<Handler> {
6-
context: AsyncLocalStorage<AsyncHandlersControllerContext<Handler>>
8+
export interface AsyncHandlersController extends HandlersController {
9+
context: AsyncLocalStorage<AsyncHandlersControllerContext>
710
}
811

9-
export interface AsyncHandlersControllerContext<Handler extends AnyHandler> {
10-
initialHandlers: Array<Handler>
11-
handlers: Array<Handler>
12+
export interface AsyncHandlersControllerContext {
13+
initialHandlers: Array<AnyHandler>
14+
handlers: Array<AnyHandler>
1215
}
1316

14-
export function asyncHandlersController<Handler extends AnyHandler>(
15-
initialHandlers: Array<Handler>,
16-
): AsyncHandlersController<Handler> {
17-
const context = new AsyncLocalStorage<
18-
AsyncHandlersControllerContext<Handler>
19-
>()
20-
const fallbackContext: AsyncHandlersControllerContext<Handler> = {
21-
initialHandlers: [...initialHandlers],
22-
handlers: [],
23-
}
17+
export const asyncHandlersController =
18+
defineHandlersController<AsyncHandlersController>((initialHandlers) => {
19+
const context = new AsyncLocalStorage<AsyncHandlersControllerContext>()
2420

25-
const getContext = () => {
26-
return context.getStore() || fallbackContext
27-
}
21+
const fallbackContext: AsyncHandlersControllerContext = {
22+
initialHandlers: [...initialHandlers],
23+
handlers: [],
24+
}
2825

29-
return {
30-
context,
31-
currentHandlers() {
32-
const { initialHandlers, handlers } = getContext()
33-
return [...handlers, ...initialHandlers]
34-
},
35-
use(nextHandlers) {
36-
getContext().handlers.unshift(...nextHandlers)
37-
},
38-
reset(nextHandlers) {
39-
const context = getContext()
40-
context.handlers = []
41-
context.initialHandlers =
42-
nextHandlers.length > 0 ? nextHandlers : context.initialHandlers
43-
},
44-
}
45-
}
26+
const getContext = () => {
27+
return context.getStore() || fallbackContext
28+
}
29+
30+
return {
31+
context,
32+
currentHandlers() {
33+
const { initialHandlers, handlers } = getContext()
34+
return [...handlers, ...initialHandlers]
35+
},
36+
use(nextHandlers) {
37+
getContext().handlers.unshift(...nextHandlers)
38+
},
39+
reset(nextHandlers) {
40+
const context = getContext()
41+
context.handlers = []
42+
context.initialHandlers =
43+
nextHandlers.length > 0 ? nextHandlers : context.initialHandlers
44+
},
45+
}
46+
})

src/node/setup-server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class SetupServerApi implements SetupServer {
4141
public listHandlers: SetupServerCommon['listHandlers']
4242

4343
#network: NetworkApi<AnyHandler>
44-
#handlersController: AsyncHandlersController<AnyHandler>
44+
#handlersController: AsyncHandlersController
4545
#currentHandlersProxy?: ReversibleProxy<HandlersController['currentHandlers']>
4646
#listenOptions?: Partial<ListenOptions>
4747

test/node/msw-api/setup-server/input-validation.node.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ test('throws an error given an Array of request handlers to "setupServer"', () =
1212
}
1313

1414
expect(createServer).toThrow(
15-
`[MSW] Failed to apply given request handlers: invalid input. Did you forget to spread the request handlers Array?`,
15+
`[MSW] Failed to create a handlers controller: invalid handlers. Did you forget to spread the handlers array?`,
1616
)
1717
})

test/node/msw-api/setup-server/use.node.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,6 @@ test('throws if provided the invalid handlers array', async () => {
134134
[http.get('*', () => new Response())],
135135
),
136136
).toThrow(
137-
'[MSW] Failed to call "use()" with the given request handlers: invalid input. Did you forget to spread the array of request handlers?',
137+
'[MSW] Failed to prepend runtime handlers: invalid handlers. Did you forget to spread the handlers array?',
138138
)
139139
})

0 commit comments

Comments
 (0)