• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const Dispatcher = require('./dispatcher')
4const {
5  ClientDestroyedError,
6  ClientClosedError,
7  InvalidArgumentError
8} = require('./core/errors')
9const { kDestroy, kClose, kDispatch, kInterceptors } = require('./core/symbols')
10
11const kDestroyed = Symbol('destroyed')
12const kClosed = Symbol('closed')
13const kOnDestroyed = Symbol('onDestroyed')
14const kOnClosed = Symbol('onClosed')
15const kInterceptedDispatch = Symbol('Intercepted Dispatch')
16
17class DispatcherBase extends Dispatcher {
18  constructor () {
19    super()
20
21    this[kDestroyed] = false
22    this[kOnDestroyed] = null
23    this[kClosed] = false
24    this[kOnClosed] = []
25  }
26
27  get destroyed () {
28    return this[kDestroyed]
29  }
30
31  get closed () {
32    return this[kClosed]
33  }
34
35  get interceptors () {
36    return this[kInterceptors]
37  }
38
39  set interceptors (newInterceptors) {
40    if (newInterceptors) {
41      for (let i = newInterceptors.length - 1; i >= 0; i--) {
42        const interceptor = this[kInterceptors][i]
43        if (typeof interceptor !== 'function') {
44          throw new InvalidArgumentError('interceptor must be an function')
45        }
46      }
47    }
48
49    this[kInterceptors] = newInterceptors
50  }
51
52  close (callback) {
53    if (callback === undefined) {
54      return new Promise((resolve, reject) => {
55        this.close((err, data) => {
56          return err ? reject(err) : resolve(data)
57        })
58      })
59    }
60
61    if (typeof callback !== 'function') {
62      throw new InvalidArgumentError('invalid callback')
63    }
64
65    if (this[kDestroyed]) {
66      queueMicrotask(() => callback(new ClientDestroyedError(), null))
67      return
68    }
69
70    if (this[kClosed]) {
71      if (this[kOnClosed]) {
72        this[kOnClosed].push(callback)
73      } else {
74        queueMicrotask(() => callback(null, null))
75      }
76      return
77    }
78
79    this[kClosed] = true
80    this[kOnClosed].push(callback)
81
82    const onClosed = () => {
83      const callbacks = this[kOnClosed]
84      this[kOnClosed] = null
85      for (let i = 0; i < callbacks.length; i++) {
86        callbacks[i](null, null)
87      }
88    }
89
90    // Should not error.
91    this[kClose]()
92      .then(() => this.destroy())
93      .then(() => {
94        queueMicrotask(onClosed)
95      })
96  }
97
98  destroy (err, callback) {
99    if (typeof err === 'function') {
100      callback = err
101      err = null
102    }
103
104    if (callback === undefined) {
105      return new Promise((resolve, reject) => {
106        this.destroy(err, (err, data) => {
107          return err ? /* istanbul ignore next: should never error */ reject(err) : resolve(data)
108        })
109      })
110    }
111
112    if (typeof callback !== 'function') {
113      throw new InvalidArgumentError('invalid callback')
114    }
115
116    if (this[kDestroyed]) {
117      if (this[kOnDestroyed]) {
118        this[kOnDestroyed].push(callback)
119      } else {
120        queueMicrotask(() => callback(null, null))
121      }
122      return
123    }
124
125    if (!err) {
126      err = new ClientDestroyedError()
127    }
128
129    this[kDestroyed] = true
130    this[kOnDestroyed] = this[kOnDestroyed] || []
131    this[kOnDestroyed].push(callback)
132
133    const onDestroyed = () => {
134      const callbacks = this[kOnDestroyed]
135      this[kOnDestroyed] = null
136      for (let i = 0; i < callbacks.length; i++) {
137        callbacks[i](null, null)
138      }
139    }
140
141    // Should not error.
142    this[kDestroy](err).then(() => {
143      queueMicrotask(onDestroyed)
144    })
145  }
146
147  [kInterceptedDispatch] (opts, handler) {
148    if (!this[kInterceptors] || this[kInterceptors].length === 0) {
149      this[kInterceptedDispatch] = this[kDispatch]
150      return this[kDispatch](opts, handler)
151    }
152
153    let dispatch = this[kDispatch].bind(this)
154    for (let i = this[kInterceptors].length - 1; i >= 0; i--) {
155      dispatch = this[kInterceptors][i](dispatch)
156    }
157    this[kInterceptedDispatch] = dispatch
158    return dispatch(opts, handler)
159  }
160
161  dispatch (opts, handler) {
162    if (!handler || typeof handler !== 'object') {
163      throw new InvalidArgumentError('handler must be an object')
164    }
165
166    try {
167      if (!opts || typeof opts !== 'object') {
168        throw new InvalidArgumentError('opts must be an object.')
169      }
170
171      if (this[kDestroyed] || this[kOnDestroyed]) {
172        throw new ClientDestroyedError()
173      }
174
175      if (this[kClosed]) {
176        throw new ClientClosedError()
177      }
178
179      return this[kInterceptedDispatch](opts, handler)
180    } catch (err) {
181      if (typeof handler.onError !== 'function') {
182        throw new InvalidArgumentError('invalid onError method')
183      }
184
185      handler.onError(err)
186
187      return false
188    }
189  }
190}
191
192module.exports = DispatcherBase
193