-
-
Notifications
You must be signed in to change notification settings - Fork 604
Expand file tree
/
Copy pathSetupApi.ts
More file actions
129 lines (108 loc) · 3.94 KB
/
SetupApi.ts
File metadata and controls
129 lines (108 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { invariant } from 'outvariant'
import { Emitter, type DefaultEventMap } from 'rettime'
import { RequestHandler } from './handlers/RequestHandler'
import { LifeCycleEventEmitter } from './sharedOptions'
import { devUtils } from './utils/internal/devUtils'
import { pipeEvents } from './utils/internal/pipeEvents'
import { toReadonlyArray } from './utils/internal/toReadonlyArray'
import { Disposable } from './utils/internal/Disposable'
import type { WebSocketHandler } from './handlers/WebSocketHandler'
export abstract class HandlersController {
abstract prepend(
runtimeHandlers: Array<RequestHandler | WebSocketHandler>,
): void
abstract reset: (
nextHandles: Array<RequestHandler | WebSocketHandler>,
) => void
abstract currentHandlers: () => Array<RequestHandler | WebSocketHandler>
}
export class InMemoryHandlersController implements HandlersController {
private handlers: Array<RequestHandler | WebSocketHandler>
constructor(
private initialHandlers: Array<RequestHandler | WebSocketHandler>,
) {
this.handlers = [...initialHandlers]
}
public prepend(
runtimeHandles: Array<RequestHandler | WebSocketHandler>,
): void {
this.handlers.unshift(...runtimeHandles)
}
public reset(nextHandlers: Array<RequestHandler | WebSocketHandler>): void {
this.handlers =
nextHandlers.length > 0 ? [...nextHandlers] : [...this.initialHandlers]
}
public currentHandlers(): Array<RequestHandler | WebSocketHandler> {
return this.handlers
}
}
/**
* Generic class for the mock API setup.
*/
export abstract class SetupApi<
EventMap extends DefaultEventMap,
> extends Disposable {
protected handlersController: HandlersController
protected readonly emitter: Emitter<EventMap>
protected readonly publicEmitter: Emitter<EventMap>
public readonly events: LifeCycleEventEmitter<EventMap>
constructor(...initialHandlers: Array<RequestHandler | WebSocketHandler>) {
super()
invariant(
this.validateHandlers(initialHandlers),
devUtils.formatMessage(
`Failed to apply given request handlers: invalid input. Did you forget to spread the request handlers Array?`,
),
)
this.handlersController = new InMemoryHandlersController(initialHandlers)
this.emitter = new Emitter<EventMap>()
this.publicEmitter = new Emitter<EventMap>()
pipeEvents(this.emitter, this.publicEmitter)
this.events = this.createLifeCycleEvents()
this.subscriptions.push(() => {
this.emitter.removeAllListeners()
this.publicEmitter.removeAllListeners()
})
}
private validateHandlers(handlers: ReadonlyArray<unknown>): boolean {
// Guard against incorrect call signature of the setup API.
return handlers.every((handler) => !Array.isArray(handler))
}
public use(
...runtimeHandlers: Array<RequestHandler | WebSocketHandler>
): void {
invariant(
this.validateHandlers(runtimeHandlers),
devUtils.formatMessage(
`Failed to call "use()" with the given request handlers: invalid input. Did you forget to spread the array of request handlers?`,
),
)
this.handlersController.prepend(runtimeHandlers)
}
public restoreHandlers(): void {
this.handlersController.currentHandlers().forEach((handler) => {
if ('isUsed' in handler) {
handler.isUsed = false
}
})
}
public resetHandlers(
...nextHandlers: Array<RequestHandler | WebSocketHandler>
): void {
this.handlersController.reset(nextHandlers)
}
public listHandlers(): ReadonlyArray<RequestHandler | WebSocketHandler> {
return toReadonlyArray(this.handlersController.currentHandlers())
}
private createLifeCycleEvents(): LifeCycleEventEmitter<EventMap> {
return {
on: this.publicEmitter.on.bind(this.publicEmitter),
removeListener: this.publicEmitter.removeListener.bind(
this.publicEmitter,
),
removeAllListeners: this.publicEmitter.removeAllListeners.bind(
this.publicEmitter,
),
}
}
}