File size: 2,679 Bytes
9ada4bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { EventMap, Listener } from 'strict-event-emitter'
import { Interceptor, ExtractEventNames } from './Interceptor'

export interface BatchInterceptorOptions<
  InterceptorList extends ReadonlyArray<Interceptor<any>>
> {
  name: string
  interceptors: InterceptorList
}

export type ExtractEventMapType<
  InterceptorList extends ReadonlyArray<Interceptor<any>>
> = InterceptorList extends ReadonlyArray<infer InterceptorType>
  ? InterceptorType extends Interceptor<infer EventMap>
    ? EventMap
    : never
  : never

/**
 * A batch interceptor that exposes a single interface
 * to apply and operate with multiple interceptors at once.
 */
export class BatchInterceptor<
  InterceptorList extends ReadonlyArray<Interceptor<any>>,
  Events extends EventMap = ExtractEventMapType<InterceptorList>
> extends Interceptor<Events> {
  static symbol: symbol

  private interceptors: InterceptorList

  constructor(options: BatchInterceptorOptions<InterceptorList>) {
    BatchInterceptor.symbol = Symbol(options.name)
    super(BatchInterceptor.symbol)
    this.interceptors = options.interceptors
  }

  protected setup() {
    const logger = this.logger.extend('setup')

    logger.info('applying all %d interceptors...', this.interceptors.length)

    for (const interceptor of this.interceptors) {
      logger.info('applying "%s" interceptor...', interceptor.constructor.name)
      interceptor.apply()

      logger.info('adding interceptor dispose subscription')
      this.subscriptions.push(() => interceptor.dispose())
    }
  }

  public on<EventName extends ExtractEventNames<Events>>(
    event: EventName,
    listener: Listener<Events[EventName]>
  ): this {
    // Instead of adding a listener to the batch interceptor,
    // propagate the listener to each of the individual interceptors.
    for (const interceptor of this.interceptors) {
      interceptor.on(event, listener)
    }

    return this
  }

  public once<EventName extends ExtractEventNames<Events>>(
    event: EventName,
    listener: Listener<Events[EventName]>
  ): this {
    for (const interceptor of this.interceptors) {
      interceptor.once(event, listener)
    }

    return this
  }

  public off<EventName extends ExtractEventNames<Events>>(
    event: EventName,
    listener: Listener<Events[EventName]>
  ): this {
    for (const interceptor of this.interceptors) {
      interceptor.off(event, listener)
    }

    return this
  }

  public removeAllListeners<EventName extends ExtractEventNames<Events>>(
    event?: EventName | undefined
  ): this {
    for (const interceptors of this.interceptors) {
      interceptors.removeAllListeners(event)
    }

    return this
  }
}