diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-02-14 17:38:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 17:38:45 +0100 |
commit | d47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch) | |
tree | 6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/internal/validators.mjs | |
parent | 1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff) |
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is
transpiled and snapshotted during the build process.
During the first pass a minimal amount of work was done to create the
snapshot, a lot of code in "ext/node" depends on presence of "Deno"
global. This code will be gradually fixed in the follow up PRs to migrate
it to import relevant APIs from "internal:" modules.
Currently the code from snapshot is not used in any way, and all
Node/npm compatibility still uses code from
"https://deno.land/std/node" (or from the location specified by
"DENO_NODE_COMPAT_URL"). This will also be handled in a follow
up PRs.
---------
Co-authored-by: crowlkats <crowlkats@toaxl.com>
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/internal/validators.mjs')
-rw-r--r-- | ext/node/polyfills/internal/validators.mjs | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/ext/node/polyfills/internal/validators.mjs b/ext/node/polyfills/internal/validators.mjs new file mode 100644 index 000000000..bea9e881a --- /dev/null +++ b/ext/node/polyfills/internal/validators.mjs @@ -0,0 +1,317 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; +import { hideStackFrames } from "internal:deno_node/polyfills/internal/hide_stack_frames.ts"; +import { isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { normalizeEncoding } from "internal:deno_node/polyfills/internal/normalize_encoding.mjs"; + +/** + * @param {number} value + * @returns {boolean} + */ +function isInt32(value) { + return value === (value | 0); +} + +/** + * @param {unknown} value + * @returns {boolean} + */ +function isUint32(value) { + return value === (value >>> 0); +} + +const octalReg = /^[0-7]+$/; +const modeDesc = "must be a 32-bit unsigned integer or an octal string"; + +/** + * Parse and validate values that will be converted into mode_t (the S_* + * constants). Only valid numbers and octal strings are allowed. They could be + * converted to 32-bit unsigned integers or non-negative signed integers in the + * C++ land, but any value higher than 0o777 will result in platform-specific + * behaviors. + * + * @param {*} value Values to be validated + * @param {string} name Name of the argument + * @param {number} [def] If specified, will be returned for invalid values + * @returns {number} + */ +function parseFileMode(value, name, def) { + value ??= def; + if (typeof value === "string") { + if (!octalReg.test(value)) { + throw new codes.ERR_INVALID_ARG_VALUE(name, value, modeDesc); + } + value = Number.parseInt(value, 8); + } + + validateInt32(value, name, 0, 2 ** 32 - 1); + return value; +} + +const validateBuffer = hideStackFrames((buffer, name = "buffer") => { + if (!isArrayBufferView(buffer)) { + throw new codes.ERR_INVALID_ARG_TYPE( + name, + ["Buffer", "TypedArray", "DataView"], + buffer, + ); + } +}); + +const validateInteger = hideStackFrames( + ( + value, + name, + min = Number.MIN_SAFE_INTEGER, + max = Number.MAX_SAFE_INTEGER, + ) => { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + if (value < min || value > max) { + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + * @param {{ + * allowArray?: boolean, + * allowFunction?: boolean, + * nullable?: boolean + * }} [options] + */ +const validateObject = hideStackFrames((value, name, options) => { + const useDefaultOptions = options == null; + const allowArray = useDefaultOptions ? false : options.allowArray; + const allowFunction = useDefaultOptions ? false : options.allowFunction; + const nullable = useDefaultOptions ? false : options.nullable; + if ( + (!nullable && value === null) || + (!allowArray && Array.isArray(value)) || + (typeof value !== "object" && ( + !allowFunction || typeof value !== "function" + )) + ) { + throw new codes.ERR_INVALID_ARG_TYPE(name, "Object", value); + } +}); + +const validateInt32 = hideStackFrames( + (value, name, min = -2147483648, max = 2147483647) => { + // The defaults for min and max correspond to the limits of 32-bit integers. + if (!isInt32(value)) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + + if (value < min || value > max) { + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + }, +); + +const validateUint32 = hideStackFrames( + (value, name, positive) => { + if (!isUint32(value)) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + const min = positive ? 1 : 0; + // 2 ** 32 === 4294967296 + throw new codes.ERR_OUT_OF_RANGE( + name, + `>= ${min} && < 4294967296`, + value, + ); + } + if (positive && value === 0) { + throw new codes.ERR_OUT_OF_RANGE(name, ">= 1 && < 4294967296", value); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + */ +function validateString(value, name) { + if (typeof value !== "string") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "string", value); + } +} + +/** + * @param {unknown} value + * @param {string} name + */ +function validateNumber(value, name) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } +} + +/** + * @param {unknown} value + * @param {string} name + */ +function validateBoolean(value, name) { + if (typeof value !== "boolean") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "boolean", value); + } +} + +/** + * @param {unknown} value + * @param {string} name + * @param {unknown[]} oneOf + */ +const validateOneOf = hideStackFrames( + (value, name, oneOf) => { + if (!Array.prototype.includes.call(oneOf, value)) { + const allowed = Array.prototype.join.call( + Array.prototype.map.call( + oneOf, + (v) => (typeof v === "string" ? `'${v}'` : String(v)), + ), + ", ", + ); + const reason = "must be one of: " + allowed; + + throw new codes.ERR_INVALID_ARG_VALUE(name, value, reason); + } + }, +); + +export function validateEncoding(data, encoding) { + const normalizedEncoding = normalizeEncoding(encoding); + const length = data.length; + + if (normalizedEncoding === "hex" && length % 2 !== 0) { + throw new codes.ERR_INVALID_ARG_VALUE( + "encoding", + encoding, + `is invalid for data of length ${length}`, + ); + } +} + +// 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. +/** + * @param {string} name + * @returns {number} + */ +function validatePort(port, name = "Port", allowZero = true) { + if ( + (typeof port !== "number" && typeof port !== "string") || + (typeof port === "string" && + String.prototype.trim.call(port).length === 0) || + +port !== (+port >>> 0) || + port > 0xFFFF || + (port === 0 && !allowZero) + ) { + throw new codes.ERR_SOCKET_BAD_PORT(name, port, allowZero); + } + + return port; +} + +/** + * @param {unknown} signal + * @param {string} name + */ +const validateAbortSignal = hideStackFrames( + (signal, name) => { + if ( + signal !== undefined && + (signal === null || + typeof signal !== "object" || + !("aborted" in signal)) + ) { + throw new codes.ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + */ +const validateFunction = hideStackFrames( + (value, name) => { + if (typeof value !== "function") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "Function", value); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + */ +const validateArray = hideStackFrames( + (value, name, minLength = 0) => { + if (!Array.isArray(value)) { + throw new codes.ERR_INVALID_ARG_TYPE(name, "Array", value); + } + if (value.length < minLength) { + const reason = `must be longer than ${minLength}`; + throw new codes.ERR_INVALID_ARG_VALUE(name, value, reason); + } + }, +); + +export default { + isInt32, + isUint32, + parseFileMode, + validateAbortSignal, + validateArray, + validateBoolean, + validateBuffer, + validateFunction, + validateInt32, + validateInteger, + validateNumber, + validateObject, + validateOneOf, + validatePort, + validateString, + validateUint32, +}; +export { + isInt32, + isUint32, + parseFileMode, + validateAbortSignal, + validateArray, + validateBoolean, + validateBuffer, + validateFunction, + validateInt32, + validateInteger, + validateNumber, + validateObject, + validateOneOf, + validatePort, + validateString, + validateUint32, +}; |