• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const { format, inspect, AggregateError: CustomAggregateError } = require('./util')
4
5/*
6  This file is a reduced and adapted version of the main lib/internal/errors.js file defined at
7
8  https://github.com/nodejs/node/blob/master/lib/internal/errors.js
9
10  Don't try to replace with the original file and keep it up to date (starting from E(...) definitions)
11  with the upstream file.
12*/
13
14const AggregateError = globalThis.AggregateError || CustomAggregateError
15const kIsNodeError = Symbol('kIsNodeError')
16const kTypes = [
17  'string',
18  'function',
19  'number',
20  'object',
21  // Accept 'Function' and 'Object' as alternative to the lower cased version.
22  'Function',
23  'Object',
24  'boolean',
25  'bigint',
26  'symbol'
27]
28const classRegExp = /^([A-Z][a-z0-9]*)+$/
29const nodeInternalPrefix = '__node_internal_'
30const codes = {}
31function assert(value, message) {
32  if (!value) {
33    throw new codes.ERR_INTERNAL_ASSERTION(message)
34  }
35}
36
37// Only use this for integers! Decimal numbers do not work with this function.
38function addNumericalSeparator(val) {
39  let res = ''
40  let i = val.length
41  const start = val[0] === '-' ? 1 : 0
42  for (; i >= start + 4; i -= 3) {
43    res = `_${val.slice(i - 3, i)}${res}`
44  }
45  return `${val.slice(0, i)}${res}`
46}
47function getMessage(key, msg, args) {
48  if (typeof msg === 'function') {
49    assert(
50      msg.length <= args.length,
51      // Default options do not count.
52      `Code: ${key}; The provided arguments length (${args.length}) does not match the required ones (${msg.length}).`
53    )
54    return msg(...args)
55  }
56  const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length
57  assert(
58    expectedLength === args.length,
59    `Code: ${key}; The provided arguments length (${args.length}) does not match the required ones (${expectedLength}).`
60  )
61  if (args.length === 0) {
62    return msg
63  }
64  return format(msg, ...args)
65}
66function E(code, message, Base) {
67  if (!Base) {
68    Base = Error
69  }
70  class NodeError extends Base {
71    constructor(...args) {
72      super(getMessage(code, message, args))
73    }
74    toString() {
75      return `${this.name} [${code}]: ${this.message}`
76    }
77  }
78  Object.defineProperties(NodeError.prototype, {
79    name: {
80      value: Base.name,
81      writable: true,
82      enumerable: false,
83      configurable: true
84    },
85    toString: {
86      value() {
87        return `${this.name} [${code}]: ${this.message}`
88      },
89      writable: true,
90      enumerable: false,
91      configurable: true
92    }
93  })
94  NodeError.prototype.code = code
95  NodeError.prototype[kIsNodeError] = true
96  codes[code] = NodeError
97}
98function hideStackFrames(fn) {
99  // We rename the functions that will be hidden to cut off the stacktrace
100  // at the outermost one
101  const hidden = nodeInternalPrefix + fn.name
102  Object.defineProperty(fn, 'name', {
103    value: hidden
104  })
105  return fn
106}
107function aggregateTwoErrors(innerError, outerError) {
108  if (innerError && outerError && innerError !== outerError) {
109    if (Array.isArray(outerError.errors)) {
110      // If `outerError` is already an `AggregateError`.
111      outerError.errors.push(innerError)
112      return outerError
113    }
114    const err = new AggregateError([outerError, innerError], outerError.message)
115    err.code = outerError.code
116    return err
117  }
118  return innerError || outerError
119}
120class AbortError extends Error {
121  constructor(message = 'The operation was aborted', options = undefined) {
122    if (options !== undefined && typeof options !== 'object') {
123      throw new codes.ERR_INVALID_ARG_TYPE('options', 'Object', options)
124    }
125    super(message, options)
126    this.code = 'ABORT_ERR'
127    this.name = 'AbortError'
128  }
129}
130E('ERR_ASSERTION', '%s', Error)
131E(
132  'ERR_INVALID_ARG_TYPE',
133  (name, expected, actual) => {
134    assert(typeof name === 'string', "'name' must be a string")
135    if (!Array.isArray(expected)) {
136      expected = [expected]
137    }
138    let msg = 'The '
139    if (name.endsWith(' argument')) {
140      // For cases like 'first argument'
141      msg += `${name} `
142    } else {
143      msg += `"${name}" ${name.includes('.') ? 'property' : 'argument'} `
144    }
145    msg += 'must be '
146    const types = []
147    const instances = []
148    const other = []
149    for (const value of expected) {
150      assert(typeof value === 'string', 'All expected entries have to be of type string')
151      if (kTypes.includes(value)) {
152        types.push(value.toLowerCase())
153      } else if (classRegExp.test(value)) {
154        instances.push(value)
155      } else {
156        assert(value !== 'object', 'The value "object" should be written as "Object"')
157        other.push(value)
158      }
159    }
160
161    // Special handle `object` in case other instances are allowed to outline
162    // the differences between each other.
163    if (instances.length > 0) {
164      const pos = types.indexOf('object')
165      if (pos !== -1) {
166        types.splice(types, pos, 1)
167        instances.push('Object')
168      }
169    }
170    if (types.length > 0) {
171      switch (types.length) {
172        case 1:
173          msg += `of type ${types[0]}`
174          break
175        case 2:
176          msg += `one of type ${types[0]} or ${types[1]}`
177          break
178        default: {
179          const last = types.pop()
180          msg += `one of type ${types.join(', ')}, or ${last}`
181        }
182      }
183      if (instances.length > 0 || other.length > 0) {
184        msg += ' or '
185      }
186    }
187    if (instances.length > 0) {
188      switch (instances.length) {
189        case 1:
190          msg += `an instance of ${instances[0]}`
191          break
192        case 2:
193          msg += `an instance of ${instances[0]} or ${instances[1]}`
194          break
195        default: {
196          const last = instances.pop()
197          msg += `an instance of ${instances.join(', ')}, or ${last}`
198        }
199      }
200      if (other.length > 0) {
201        msg += ' or '
202      }
203    }
204    switch (other.length) {
205      case 0:
206        break
207      case 1:
208        if (other[0].toLowerCase() !== other[0]) {
209          msg += 'an '
210        }
211        msg += `${other[0]}`
212        break
213      case 2:
214        msg += `one of ${other[0]} or ${other[1]}`
215        break
216      default: {
217        const last = other.pop()
218        msg += `one of ${other.join(', ')}, or ${last}`
219      }
220    }
221    if (actual == null) {
222      msg += `. Received ${actual}`
223    } else if (typeof actual === 'function' && actual.name) {
224      msg += `. Received function ${actual.name}`
225    } else if (typeof actual === 'object') {
226      var _actual$constructor
227      if (
228        (_actual$constructor = actual.constructor) !== null &&
229        _actual$constructor !== undefined &&
230        _actual$constructor.name
231      ) {
232        msg += `. Received an instance of ${actual.constructor.name}`
233      } else {
234        const inspected = inspect(actual, {
235          depth: -1
236        })
237        msg += `. Received ${inspected}`
238      }
239    } else {
240      let inspected = inspect(actual, {
241        colors: false
242      })
243      if (inspected.length > 25) {
244        inspected = `${inspected.slice(0, 25)}...`
245      }
246      msg += `. Received type ${typeof actual} (${inspected})`
247    }
248    return msg
249  },
250  TypeError
251)
252E(
253  'ERR_INVALID_ARG_VALUE',
254  (name, value, reason = 'is invalid') => {
255    let inspected = inspect(value)
256    if (inspected.length > 128) {
257      inspected = inspected.slice(0, 128) + '...'
258    }
259    const type = name.includes('.') ? 'property' : 'argument'
260    return `The ${type} '${name}' ${reason}. Received ${inspected}`
261  },
262  TypeError
263)
264E(
265  'ERR_INVALID_RETURN_VALUE',
266  (input, name, value) => {
267    var _value$constructor
268    const type =
269      value !== null &&
270      value !== undefined &&
271      (_value$constructor = value.constructor) !== null &&
272      _value$constructor !== undefined &&
273      _value$constructor.name
274        ? `instance of ${value.constructor.name}`
275        : `type ${typeof value}`
276    return `Expected ${input} to be returned from the "${name}"` + ` function but got ${type}.`
277  },
278  TypeError
279)
280E(
281  'ERR_MISSING_ARGS',
282  (...args) => {
283    assert(args.length > 0, 'At least one arg needs to be specified')
284    let msg
285    const len = args.length
286    args = (Array.isArray(args) ? args : [args]).map((a) => `"${a}"`).join(' or ')
287    switch (len) {
288      case 1:
289        msg += `The ${args[0]} argument`
290        break
291      case 2:
292        msg += `The ${args[0]} and ${args[1]} arguments`
293        break
294      default:
295        {
296          const last = args.pop()
297          msg += `The ${args.join(', ')}, and ${last} arguments`
298        }
299        break
300    }
301    return `${msg} must be specified`
302  },
303  TypeError
304)
305E(
306  'ERR_OUT_OF_RANGE',
307  (str, range, input) => {
308    assert(range, 'Missing "range" argument')
309    let received
310    if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) {
311      received = addNumericalSeparator(String(input))
312    } else if (typeof input === 'bigint') {
313      received = String(input)
314      if (input > 2n ** 32n || input < -(2n ** 32n)) {
315        received = addNumericalSeparator(received)
316      }
317      received += 'n'
318    } else {
319      received = inspect(input)
320    }
321    return `The value of "${str}" is out of range. It must be ${range}. Received ${received}`
322  },
323  RangeError
324)
325E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error)
326E('ERR_METHOD_NOT_IMPLEMENTED', 'The %s method is not implemented', Error)
327E('ERR_STREAM_ALREADY_FINISHED', 'Cannot call %s after a stream was finished', Error)
328E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error)
329E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error)
330E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError)
331E('ERR_STREAM_PREMATURE_CLOSE', 'Premature close', Error)
332E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error)
333E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event', Error)
334E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error)
335E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError)
336module.exports = {
337  AbortError,
338  aggregateTwoErrors: hideStackFrames(aggregateTwoErrors),
339  hideStackFrames,
340  codes
341}
342