Skip to content

Commit

Permalink
fix: add jsdoc and do minor changes in utils.js (#3550)
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak authored Sep 5, 2024
1 parent 89a46dd commit 61a4b69
Showing 1 changed file with 148 additions and 20 deletions.
168 changes: 148 additions & 20 deletions lib/core/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class BodyAsyncIterable {
}
}

/**
* @param {*} body
* @returns {*}
*/
function wrapRequestBody (body) {
if (isStream(body)) {
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
Expand Down Expand Up @@ -67,11 +71,19 @@ function wrapRequestBody (body) {
}
}

/**
* @param {*} obj
* @returns {obj is import('node:stream').Stream}
*/
function isStream (obj) {
return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function'
}

// based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
/**
* @param {*} object
* @returns {object is Blob}
* based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License)
*/
function isBlobLike (object) {
if (object === null) {
return false
Expand Down Expand Up @@ -108,6 +120,10 @@ function serializePathWithQuery (url, queryParams) {
return url
}

/**
* @param {number|string|undefined} port
* @returns {boolean}
*/
function isValidPort (port) {
const value = parseInt(port, 10)
return (
Expand All @@ -117,6 +133,12 @@ function isValidPort (port) {
)
}

/**
* Check if the value is a valid http or https prefixed string.
*
* @param {string} value
* @returns {boolean}
*/
function isHttpOrHttpsPrefixed (value) {
return (
value != null &&
Expand All @@ -134,8 +156,15 @@ function isHttpOrHttpsPrefixed (value) {
)
}

/**
* @param {string|URL|Record<string,string>} url
* @returns {URL}
*/
function parseURL (url) {
if (typeof url === 'string') {
/**
* @type {URL}
*/
url = new URL(url)

if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
Expand Down Expand Up @@ -205,6 +234,10 @@ function parseURL (url) {
return url
}

/**
* @param {string|URL|Record<string, string>} url
* @returns {URL}
*/
function parseOrigin (url) {
url = parseURL(url)

Expand All @@ -215,6 +248,10 @@ function parseOrigin (url) {
return url
}

/**
* @param {string} host
* @returns {string}
*/
function getHostname (host) {
if (host[0] === '[') {
const idx = host.indexOf(']')
Expand All @@ -229,8 +266,12 @@ function getHostname (host) {
return host.substring(0, idx)
}

// IP addresses are not valid server names per RFC6066
// > Currently, the only server names supported are DNS hostnames
/**
* IP addresses are not valid server names per RFC6066
* Currently, the only server names supported are DNS hostnames
* @param {string|null} host
* @returns {string|null}
*/
function getServerName (host) {
if (!host) {
return null
Expand All @@ -246,18 +287,34 @@ function getServerName (host) {
return servername
}

/**
* @param {*} obj
* @returns {*}
*/
function deepClone (obj) {
return JSON.parse(JSON.stringify(obj))
}

/**
* @param {*} obj
* @returns {obj is AsyncIterable}
*/
function isAsyncIterable (obj) {
return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function')
}

/**
* @param {*} obj
* @returns {obj is Iterable}
*/
function isIterable (obj) {
return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function'))
}

/**
* @param {Blob|Buffer|import ('stream').Stream} body
* @returns {number|null}
*/
function bodyLength (body) {
if (body == null) {
return 0
Expand All @@ -275,10 +332,19 @@ function bodyLength (body) {
return null
}

/**
* @param {import ('stream').Stream} body
* @returns {boolean}
*/
function isDestroyed (body) {
return body && !!(body.destroyed || body[kDestroyed] || (stream.isDestroyed?.(body)))
}

/**
* @param {import ('stream').Stream} stream
* @param {Error} [err]
* @returns
*/
function destroy (stream, err) {
if (stream == null || !isStream(stream) || isDestroyed(stream)) {
return
Expand All @@ -303,8 +369,12 @@ function destroy (stream, err) {
}

const KEEPALIVE_TIMEOUT_EXPR = /timeout=(\d+)/
/**
* @param {string} val
* @returns {number | null}
*/
function parseKeepAliveTimeout (val) {
const m = val.toString().match(KEEPALIVE_TIMEOUT_EXPR)
const m = val.match(KEEPALIVE_TIMEOUT_EXPR)
return m ? parseInt(m[1], 10) * 1000 : null
}

Expand All @@ -329,12 +399,13 @@ function bufferToLowerCasedHeaderName (value) {
}

/**
* @param {Record<string, string | string[]> | (Buffer | string | (Buffer | string)[])[]} headers
* @param {(Buffer | string)[]} headers
* @param {Record<string, string | string[]>} [obj]
* @returns {Record<string, string | string[]>}
*/
function parseHeaders (headers, obj) {
if (obj === undefined) obj = {}

for (let i = 0; i < headers.length; i += 2) {
const key = headerNameToString(headers[i])
let val = obj[key]
Expand Down Expand Up @@ -363,17 +434,24 @@ function parseHeaders (headers, obj) {
return obj
}

/**
* @param {Buffer[]} headers
* @returns {string[]}
*/
function parseRawHeaders (headers) {
const len = headers.length
const ret = new Array(len)
const headersLength = headers.length
/**
* @type {string[]}
*/
const ret = new Array(headersLength)

let hasContentLength = false
let contentDispositionIdx = -1
let key
let val
let kLen = 0

for (let n = 0; n < headers.length; n += 2) {
for (let n = 0; n < headersLength; n += 2) {
key = headers[n]
val = headers[n + 1]

Expand Down Expand Up @@ -439,13 +517,33 @@ function validateHandler (handler, method, upgrade) {
}
}

// A body is disturbed if it has been read from and it cannot
// be re-used without losing state or data.
/**
* A body is disturbed if it has been read from and it cannot be re-used without
* losing state or data.
* @param {import('node:stream').Readable} body
* @returns {boolean}
*/
function isDisturbed (body) {
// TODO (fix): Why is body[kBodyUsed] needed?
return !!(body && (stream.isDisturbed(body) || body[kBodyUsed]))
}

/**
* @typedef {object} SocketInfo
* @property {string} [localAddress]
* @property {number} [localPort]
* @property {string} [remoteAddress]
* @property {number} [remotePort]
* @property {string} [remoteFamily]
* @property {number} [timeout]
* @property {number} bytesWritten
* @property {number} bytesRead
*/

/**
* @param {import('net').Socket} socket
* @returns {SocketInfo}
*/
function getSocketInfo (socket) {
return {
localAddress: socket.localAddress,
Expand All @@ -459,7 +557,9 @@ function getSocketInfo (socket) {
}
}

/** @type {globalThis['ReadableStream']} */
/**
* @returns {globalThis['ReadableStream']}
*/
function ReadableStreamFrom (iterable) {
// We cannot use ReadableStream.from here because it does not return a byte stream.

Expand All @@ -484,16 +584,20 @@ function ReadableStreamFrom (iterable) {
}
return controller.desiredSize > 0
},
async cancel (reason) {
async cancel () {
await iterator.return()
},
type: 'bytes'
}
)
}

// The chunk should be a FormData instance and contains
// all the required methods.
/**
* The object should be a FormData instance and contains all the required
* methods.
* @param {*} object
* @returns {object is FormData}
*/
function isFormDataLike (object) {
return (
object &&
Expand Down Expand Up @@ -538,6 +642,7 @@ function isUSVString (val) {
/**
* @see https://tools.ietf.org/html/rfc7230#section-3.2.6
* @param {number} c
* @returns {boolean}
*/
function isTokenCharCode (c) {
switch (c) {
Expand Down Expand Up @@ -568,6 +673,7 @@ function isTokenCharCode (c) {

/**
* @param {string} characters
* @returns {boolean}
*/
function isValidHTTPToken (characters) {
if (characters.length === 0) {
Expand All @@ -594,17 +700,24 @@ const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/

/**
* @param {string} characters
* @returns {boolean}
*/
function isValidHeaderValue (characters) {
return !headerCharRegex.test(characters)
}

// Parsed accordingly to RFC 9110
// https://www.rfc-editor.org/rfc/rfc9110#field.content-range
const rangeHeaderRegex = /^bytes (\d+)-(\d+)\/(\d+)?$/

/**
* Parse accordingly to RFC 9110
* @see https://www.rfc-editor.org/rfc/rfc9110#field.content-range
* @param {string} [range]
* @returns
*/
function parseRangeHeader (range) {
if (range == null || range === '') return { start: 0, end: null, size: null }

const m = range ? range.match(/^bytes (\d+)-(\d+)\/(\d+)?$/) : null
const m = range ? range.match(rangeHeaderRegex) : null
return m
? {
start: parseInt(m[1]),
Expand All @@ -614,20 +727,35 @@ function parseRangeHeader (range) {
: null
}

/**
* @param {Record<string|symbol, any>} obj
* @param {string} name
* @param {Function} listener
*/
function addListener (obj, name, listener) {
const listeners = (obj[kListeners] ??= [])
listeners.push([name, listener])
obj.on(name, listener)
return obj
}

/**
* @param {Record<string|symbol, any>} obj
*/
function removeAllListeners (obj) {
for (const [name, listener] of obj[kListeners] ?? []) {
obj.removeListener(name, listener)
if (obj[kListeners] != null) {
for (const [name, listener] of obj[kListeners]) {
obj.removeListener(name, listener)
}
obj[kListeners] = null
}
obj[kListeners] = null
}

/**
* @param {import ('../dispatcher/client')} client
* @param {import ('../core/request')} request
* @param {Error} err
*/
function errorRequest (client, request, err) {
try {
request.onError(err)
Expand Down

0 comments on commit 61a4b69

Please sign in to comment.