CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
plugin.js280 linesDownload Raw Back to lib
1'use strict'2 3const { EventEmitter } = require('node:events')4const { inherits } = require('node:util')5const { debug } = require('./debug')6const { createPromise } = require('./create-promise')7const { AVV_ERR_PLUGIN_EXEC_TIMEOUT } = require('./errors')8const { getPluginName } = require('./get-plugin-name')9const { isPromiseLike } = require('./is-promise-like')10 11/**12 * @param {*} queue13 * @param {*} func14 * @param {*} options15 * @param {boolean} isAfter16 * @param {number} [timeout]17 */18function Plugin (queue, func, options, isAfter, timeout) {19  this.queue = queue20  this.func = func21  this.options = options22 23  /**24   * @type {boolean}25   */26  this.isAfter = isAfter27  /**28   * @type {number}29   */30  this.timeout = timeout31 32  /**33   * @type {boolean}34   */35  this.started = false36  /**37   * @type {string}38   */39  this.name = getPluginName(func, options)40 41  this.queue.pause()42 43  /**44   * @type {Error|null}45   */46  this._error = null47  /**48   * @type {boolean}49   */50  this.loaded = false51 52  this._promise = null53 54  this.startTime = null55}56 57inherits(Plugin, EventEmitter)58 59/**60 * @callback ExecCallback61 * @param {Error|null} execErr62 * @returns63 */64 65/**66 *67 * @param {*} server68 * @param {ExecCallback} callback69 * @returns70 */71Plugin.prototype.exec = function (server, callback) {72  debug('exec', this.name)73 74  this.server = server75  const func = this.func76  const name = this.name77  let completed = false78 79  this.options = typeof this.options === 'function' ? this.options(this.server) : this.options80 81  let timer = null82 83  /**84   * @param {Error} [execErr]85   */86  const done = (execErr) => {87    if (completed) {88      debug('loading complete', name)89      return90    }91 92    this._error = execErr93 94    if (execErr) {95      debug('exec errored', name)96    } else {97      debug('exec completed', name)98    }99 100    completed = true101 102    if (timer) {103      clearTimeout(timer)104    }105 106    callback(execErr)107  }108 109  if (this.timeout > 0) {110    debug('setting up timeout', name, this.timeout)111    timer = setTimeout(function () {112      debug('timed out', name)113      timer = null114      const readyTimeoutErr = new AVV_ERR_PLUGIN_EXEC_TIMEOUT(name)115      // TODO Remove reference to function116      readyTimeoutErr.fn = func117      done(readyTimeoutErr)118    }, this.timeout)119  }120 121  this.started = true122  this.startTime = Date.now()123  this.emit('start', this.server ? this.server.name : null, this.name, Date.now())124 125  const maybePromiseLike = func(this.server, this.options, done)126 127  if (isPromiseLike(maybePromiseLike)) {128    debug('exec: resolving promise', name)129 130    maybePromiseLike.then(131      () => process.nextTick(done),132      (e) => process.nextTick(done, e))133  } else if (func.length < 3) {134    done()135  }136}137 138/**139 * @returns {Promise}140 */141Plugin.prototype.loadedSoFar = function () {142  debug('loadedSoFar', this.name)143 144  if (this.loaded) {145    return Promise.resolve()146  }147 148  const setup = () => {149    this.server.after((afterErr, callback) => {150      this._error = afterErr151      this.queue.pause()152 153      if (this._promise) {154        if (afterErr) {155          debug('rejecting promise', this.name, afterErr)156          this._promise.reject(afterErr)157        } else {158          debug('resolving promise', this.name)159          this._promise.resolve()160        }161        this._promise = null162      }163 164      process.nextTick(callback, afterErr)165    })166    this.queue.resume()167  }168 169  let res170 171  if (!this._promise) {172    this._promise = createPromise()173    res = this._promise.promise174 175    if (!this.server) {176      this.on('start', setup)177    } else {178      setup()179    }180  } else {181    res = Promise.resolve()182  }183 184  return res185}186 187/**188 * @callback EnqueueCallback189 * @param {Error|null} enqueueErr190 * @param {Plugin} result191 */192 193/**194 *195 * @param {Plugin} plugin196 * @param {EnqueueCallback} callback197 */198Plugin.prototype.enqueue = function (plugin, callback) {199  debug('enqueue', this.name, plugin.name)200 201  this.emit('enqueue', this.server ? this.server.name : null, this.name, Date.now())202  this.queue.push(plugin, callback)203}204 205/**206 * @callback FinishCallback207 * @param {Error|null} finishErr208 * @returns209 */210/**211 *212 * @param {Error|null} err213 * @param {FinishCallback} callback214 * @returns215 */216Plugin.prototype.finish = function (err, callback) {217  debug('finish', this.name, err)218 219  const done = () => {220    if (this.loaded) {221      return222    }223 224    debug('loaded', this.name)225    this.emit('loaded', this.server ? this.server.name : null, this.name, Date.now())226    this.loaded = true227 228    callback(err)229  }230 231  if (err) {232    if (this._promise) {233      this._promise.reject(err)234      this._promise = null235    }236    done()237    return238  }239 240  const check = () => {241    debug('check', this.name, this.queue.length(), this.queue.running(), this._promise)242    if (this.queue.length() === 0 && this.queue.running() === 0) {243      if (this._promise) {244        const wrap = () => {245          debug('wrap')246          queueMicrotask(check)247        }248        this._promise.resolve()249        this._promise.promise.then(wrap, wrap)250        this._promise = null251      } else {252        done()253      }254    } else {255      debug('delayed', this.name)256      // finish when the queue of nested plugins to load is empty257      this.queue.drain = () => {258        debug('drain', this.name)259        this.queue.drain = noop260 261        // we defer the check, as a safety net for things262        // that might be scheduled in the loading callback263        queueMicrotask(check)264      }265    }266  }267 268  queueMicrotask(check)269 270  // we start loading the dependents plugins only once271  // the current level is finished272  this.queue.resume()273}274 275function noop () {}276 277module.exports = {278  Plugin279}280