Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: improve hideStackFrames intellisense #44181

Merged
merged 11 commits into from
Aug 17, 2022
5 changes: 3 additions & 2 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,9 @@ function makeNodeErrorWithCode(Base, key) {

/**
* This function removes unnecessary frames from Node.js core errors.
* @template {(...args: any[]) => any} T
* @type {(fn: T) => T}
* @template {(...args: unknown[]) => unknown} T
* @param {T} fn
* @returns {T}
*/
function hideStackFrames(fn) {
// We rename the functions that will be hidden to cut off the stacktrace
Expand Down
205 changes: 177 additions & 28 deletions lib/internal/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,20 @@ const {
} = require('internal/util/types');
const { signals } = internalBinding('constants').os;

/**
* @callback isInt32
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @param {number} value
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @returns {boolean}
anonrig marked this conversation as resolved.
Show resolved Hide resolved
*/
aduh95 marked this conversation as resolved.
Show resolved Hide resolved
function isInt32(value) {
return value === (value | 0);
}

/**
* @callback isUint32
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @param {number} value
* @returns {boolean}
anonrig marked this conversation as resolved.
Show resolved Hide resolved
*/
aduh95 marked this conversation as resolved.
Show resolved Hide resolved
function isUint32(value) {
return value === (value >>> 0);
}
Expand Down Expand Up @@ -70,6 +80,16 @@ function parseFileMode(value, name, def) {
return value;
}

/**
* @callback validateInteger
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
* @returns {asserts value is number}
*/

/** @type {validateInteger} */
const validateInteger = hideStackFrames(
(value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => {
if (typeof value !== 'number')
Expand All @@ -81,6 +101,16 @@ const validateInteger = hideStackFrames(
}
);

/**
* @callback validateInt32
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
* @returns {asserts value is number}
*/

/** @type {validateInt32} */
const validateInt32 = hideStackFrames(
(value, name, min = -2147483648, max = 2147483647) => {
// The defaults for min and max correspond to the limits of 32-bit integers.
Expand All @@ -96,7 +126,17 @@ const validateInt32 = hideStackFrames(
}
);

const validateUint32 = hideStackFrames((value, name, positive) => {
/**
* @callback validateUint32
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @returns {asserts value is number}
*/

/** @type {validateUint32} */
const validateUint32 = hideStackFrames((value, name, positive = false) => {
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
}
Expand All @@ -111,24 +151,51 @@ const validateUint32 = hideStackFrames((value, name, positive) => {
}
});

/**
* @callback validateString
* @param {*} value
* @param {string} name
* @returns {asserts value is string}
*/

/** @type {validateString} */
function validateString(value, name) {
if (typeof value !== 'string')
throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
}

/**
* @callback validateNumber
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
* @returns {asserts value is number}
*/

/** @type {validateNumber} */
function validateNumber(value, name, min = undefined, max) {
if (typeof value !== 'number')
throw new ERR_INVALID_ARG_TYPE(name, 'number', value);

if ((min != null && value < min) || (max != null && value > max) ||
((min != null || max != null) && NumberIsNaN(value))) {
((min != null || max != null) && NumberIsNaN(value))) {
anonrig marked this conversation as resolved.
Show resolved Hide resolved
throw new ERR_OUT_OF_RANGE(
name,
`${min != null ? `>= ${min}` : ''}${min != null && max != null ? ' && ' : ''}${max != null ? `<= ${max}` : ''}`,
value);
}
}

/**
* @callback validateOneOf
* @template T
* @param {T} value
* @param {string} name
* @param {T[]} oneOf
*/

/** @type {validateOneOf} */
const validateOneOf = hideStackFrames((value, name, oneOf) => {
if (!ArrayPrototypeIncludes(oneOf, value)) {
const allowed = ArrayPrototypeJoin(
Expand All @@ -140,6 +207,14 @@ const validateOneOf = hideStackFrames((value, name, oneOf) => {
}
});

/**
* @callback validateBoolean
* @param {*} value
* @param {string} name
* @returns {asserts value is boolean}
*/

/** @type {validateBoolean} */
function validateBoolean(value, name) {
if (typeof value !== 'boolean')
throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value);
Expand All @@ -152,28 +227,36 @@ function getOwnPropertyValueOrDefault(options, key, defaultValue) {
}

/**
* @param {unknown} value
* @callback validateObject
* @param {*} value
* @param {string} name
* @param {{
* allowArray?: boolean,
* allowFunction?: boolean,
* nullable?: boolean
* }} [options]
* @param {{allowArray: boolean=, allowFunction: boolean=, nullable: boolean=}} [options]
anonrig marked this conversation as resolved.
Show resolved Hide resolved
*/

/** @type {validateObject} */
const validateObject = hideStackFrames(
(value, name, options) => {
(value, name, options = null) => {
const allowArray = getOwnPropertyValueOrDefault(options, 'allowArray', false);
const allowFunction = getOwnPropertyValueOrDefault(options, 'allowFunction', false);
const nullable = getOwnPropertyValueOrDefault(options, 'nullable', false);
if ((!nullable && value === null) ||
(!allowArray && ArrayIsArray(value)) ||
(typeof value !== 'object' && (
!allowFunction || typeof value !== 'function'
))) {
(!allowArray && ArrayIsArray(value)) ||
(typeof value !== 'object' && (
!allowFunction || typeof value !== 'function'
))) {
anonrig marked this conversation as resolved.
Show resolved Hide resolved
throw new ERR_INVALID_ARG_TYPE(name, 'Object', value);
}
});

/**
* @callback validateArray
* @param {*} value
* @param {string} name
* @param {number} [minLength]
* @returns {asserts value is unknown[]}
anonrig marked this conversation as resolved.
Show resolved Hide resolved
*/

/** @type {validateArray} */
const validateArray = hideStackFrames((value, name, minLength = 0) => {
if (!ArrayIsArray(value)) {
throw new ERR_INVALID_ARG_TYPE(name, 'Array', value);
Expand All @@ -182,76 +265,142 @@ const validateArray = hideStackFrames((value, name, minLength = 0) => {
const reason = `must be longer than ${minLength}`;
throw new ERR_INVALID_ARG_VALUE(name, value, reason);
}

return true;
anonrig marked this conversation as resolved.
Show resolved Hide resolved
});

/**
* @callback validateSignalName
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @param {*} signal
* @param {string} name
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @returns {asserts signal is keyof signals}
*/

/** @type {validateSignalName} */
anonrig marked this conversation as resolved.
Show resolved Hide resolved
function validateSignalName(signal, name = 'signal') {
validateString(signal, name);

if (signals[signal] === undefined) {
if (signals[StringPrototypeToUpperCase(signal)] !== undefined) {
throw new ERR_UNKNOWN_SIGNAL(signal +
' (signals must use all capital letters)');
' (signals must use all capital letters)');
anonrig marked this conversation as resolved.
Show resolved Hide resolved
}

throw new ERR_UNKNOWN_SIGNAL(signal);
}
}

/**
* @callback validateBuffer
* @param {*} buffer
* @param {string} [name='buffer']
anonrig marked this conversation as resolved.
Show resolved Hide resolved
*/

/** @type {validateBuffer} */
const validateBuffer = hideStackFrames((buffer, name = 'buffer') => {
if (!isArrayBufferView(buffer)) {
throw new ERR_INVALID_ARG_TYPE(name,
['Buffer', 'TypedArray', 'DataView'],
buffer);
throw new ERR_INVALID_ARG_TYPE(name, ['Buffer', 'TypedArray', 'DataView'], buffer);
anonrig marked this conversation as resolved.
Show resolved Hide resolved
}
});

/**
* @callback validateEncoding
* @param {string} data
* @param {string} encoding
*/

/** @type {validateEncoding} */
anonrig marked this conversation as resolved.
Show resolved Hide resolved
function validateEncoding(data, encoding) {
const normalizedEncoding = normalizeEncoding(encoding);
const length = data.length;

if (normalizedEncoding === 'hex' && length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE('encoding', encoding,
`is invalid for data of length ${length}`);
throw new ERR_INVALID_ARG_VALUE('encoding', encoding, `is invalid for data of length ${length}`);
anonrig marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Check that the port number is not NaN when coerced to a number,
// is an integer and that it falls within the legal range of port numbers.
/**
* @callback validatePort
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @description Check that the port number is not NaN when coerced to a number,
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* is an integer and that it falls within the legal range of port numbers.
* @param {*} port
* @param {string} [name='Port']
* @param {boolean} [allowZero=true]
* @returns {number}
*/
aduh95 marked this conversation as resolved.
Show resolved Hide resolved
function validatePort(port, name = 'Port', allowZero = true) {
if ((typeof port !== 'number' && typeof port !== 'string') ||
(typeof port === 'string' && StringPrototypeTrim(port).length === 0) ||
+port !== (+port >>> 0) ||
port > 0xFFFF ||
(port === 0 && !allowZero)) {
(typeof port === 'string' && StringPrototypeTrim(port).length === 0) ||
+port !== (+port >>> 0) ||
port > 0xFFFF ||
(port === 0 && !allowZero)) {
anonrig marked this conversation as resolved.
Show resolved Hide resolved
throw new ERR_SOCKET_BAD_PORT(name, port, allowZero);
}
return port | 0;
}

/**
* @callback validateAbortSignal
* @param {string} signal
* @param {string} name
*/
anonrig marked this conversation as resolved.
Show resolved Hide resolved
const validateAbortSignal = hideStackFrames((signal, name) => {
if (signal !== undefined &&
(signal === null ||
typeof signal !== 'object' ||
!('aborted' in signal))) {
(signal === null ||
typeof signal !== 'object' ||
!('aborted' in signal))) {
anonrig marked this conversation as resolved.
Show resolved Hide resolved
throw new ERR_INVALID_ARG_TYPE(name, 'AbortSignal', signal);
}
});

/**
* @callback validateFunction
* @param {*} value
* @param {string} name
* @returns {asserts value is Function}
*/

/** @type {validateFunction} */
const validateFunction = hideStackFrames((value, name) => {
if (typeof value !== 'function')
throw new ERR_INVALID_ARG_TYPE(name, 'Function', value);
});

/**
* @callback validatePlainFunction
* @param {*} value
* @param {string} name
* @returns {asserts value is Function}
*/

/** @type {validatePlainFunction} */
const validatePlainFunction = hideStackFrames((value, name) => {
if (typeof value !== 'function' || isAsyncFunction(value))
throw new ERR_INVALID_ARG_TYPE(name, 'Function', value);
});

/**
* @callback validateUndefined
* @param {*} value
* @param {string} name
* @returns {asserts value is undefined}
*/

/** @type {validateUndefined} */
const validateUndefined = hideStackFrames((value, name) => {
if (value !== undefined)
throw new ERR_INVALID_ARG_TYPE(name, 'undefined', value);
});

/**
* @callback validateUnion
anonrig marked this conversation as resolved.
Show resolved Hide resolved
* @template T
* @param {T} value
* @param {string} name
* @param {T[]} union
*/

/** @type {validateUnion} */
anonrig marked this conversation as resolved.
Show resolved Hide resolved
function validateUnion(value, name, union) {
if (!ArrayPrototypeIncludes(union, value)) {
throw new ERR_INVALID_ARG_TYPE(name, `('${ArrayPrototypeJoin(union, '|')}')`, value);
Expand Down